00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "kdatepicker.h"
00023 #include "kdatepicker_p.h"
00024 #include "kdatetable.h"
00025
00026 #include <QtGui/QApplication>
00027 #include <QtGui/QFont>
00028 #include <QtGui/QLayout>
00029 #include <QKeyEvent>
00030 #include <QtGui/QMenu>
00031 #include <QtGui/QPainter>
00032 #include <QtGui/QStyle>
00033 #include <QtGui/QToolButton>
00034 #include <QtGui/QDoubleValidator>
00035
00036 #include <kcalendarsystem.h>
00037 #include <kcombobox.h>
00038 #include <kdebug.h>
00039 #include <kdialog.h>
00040 #include <kglobal.h>
00041 #include <kicon.h>
00042 #include <kiconloader.h>
00043 #include <klineedit.h>
00044 #include <klocale.h>
00045 #include <knotification.h>
00046
00047 #include "kdatepicker.moc"
00048 #include "kdatepicker_p.moc"
00049
00050
00051
00052
00053 KDatePickerPrivateYearSelector::KDatePickerPrivateYearSelector(
00054 const KCalendarSystem *cal, const QDate ¤tDate, QWidget* parent )
00055 : QLineEdit( parent ), val( new QIntValidator( this ) ), result( 0 )
00056 {
00057 calendar = cal;
00058 oldDate = currentDate;
00059
00060 QFont font;
00061 font = KGlobalSettings::generalFont();
00062 setFont( font );
00063
00064 setFrame( false );
00065
00066 val->setRange( calendar->year( calendar->earliestValidDate() ),
00067 calendar->year( calendar->latestValidDate() ) );
00068 setValidator( val );
00069
00070 connect( this, SIGNAL( returnPressed() ), SLOT( yearEnteredSlot() ) );
00071 }
00072
00073 void KDatePickerPrivateYearSelector::yearEnteredSlot()
00074 {
00075 bool ok;
00076 int newYear;
00077 QDate newDate;
00078
00079
00080 newYear = text().toInt( &ok );
00081 if( !ok ) {
00082 KNotification::beep();
00083 return;
00084 }
00085
00086
00087 if ( calendar->setDate( newDate, newYear, calendar->month( oldDate ), calendar->day( oldDate ) ) ) {
00088 result = newYear;
00089 emit( closeMe( 1 ) );
00090 } else {
00091 KNotification::beep();
00092 }
00093
00094 }
00095
00096 int KDatePickerPrivateYearSelector::year()
00097 {
00098 return result;
00099 }
00100
00101 void KDatePickerPrivateYearSelector::setYear( int year )
00102 {
00103 setText( QString::number( year ) );
00104 }
00105
00106 class KDatePicker::KDatePickerPrivate
00107 {
00108 public:
00109 KDatePickerPrivate( KDatePicker *q ) :
00110 q( q ), closeButton( 0L ), selectWeek( 0L ), todayButton( 0 ), navigationLayout( 0 )
00111 {
00112 }
00113
00114 void fillWeeksCombo();
00115 QDate validDateInYearMonth( int year, int month );
00116
00118 KDatePicker *q;
00119
00120 QToolButton *closeButton;
00121 KComboBox *selectWeek;
00122 QToolButton *todayButton;
00123 QBoxLayout *navigationLayout;
00124
00126 QToolButton *yearForward;
00128 QToolButton *yearBackward;
00130 QToolButton *monthForward;
00132 QToolButton *monthBackward;
00134 QToolButton *selectMonth;
00136 QToolButton *selectYear;
00138 QLineEdit *line;
00140 KDateValidator *val;
00142 KDateTable *table;
00144 QSize maxMonthRect;
00145
00147 int fontsize;
00148 };
00149
00150 void KDatePicker::KDatePickerPrivate::fillWeeksCombo()
00151 {
00152
00153
00154
00155
00156
00157
00158 int thisYear = q->calendar()->year( q->date() );
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175 int lastMonthThisYear = q->calendar()->monthsInYear( q->date() );
00176 QDate dayInLastMonthOfYear = validDateInYearMonth( thisYear, lastMonthThisYear );
00177
00178 QDate lastDayOfYear;
00179 if ( ! q->calendar()->isValid( dayInLastMonthOfYear ) ||
00180 ! q->calendar()->setDate( lastDayOfYear, thisYear, lastMonthThisYear,
00181 q->calendar()->daysInMonth( dayInLastMonthOfYear ) ) ) {
00182 lastDayOfYear = q->calendar()->latestValidDate();
00183 }
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193 QDate day;
00194 if ( ! q->calendar()->setDate( day, thisYear, 1, 1 ) ) {
00195 day = q->calendar()->earliestValidDate();
00196 }
00197
00198 selectWeek->clear();
00199
00200
00201
00202
00203 for ( ; q->calendar()->isValid( day ) && day <= lastDayOfYear;
00204 day = q->calendar()->addDays( day, q->calendar()->daysInWeek( day ) ) ) {
00205
00206
00207
00208 int weekYear = thisYear;
00209 int weekNum = q->calendar()->weekNumber( day, &weekYear );
00210 QString weekString = i18n( "Week %1", weekNum );
00211
00212
00213 if ( weekYear != thisYear ) {
00214 weekString += '*';
00215 }
00216
00217
00218
00219 QDate targetDate = q->calendar()->addDays( day,
00220 q->calendar()->dayOfWeek( q->date() ) - q->calendar()->dayOfWeek( day )
00221 );
00222 selectWeek->addItem( weekString, targetDate );
00223
00224
00225
00226 if ( day < lastDayOfYear &&
00227 day.daysTo( lastDayOfYear ) < q->calendar()->daysInWeek( day ) &&
00228 q->calendar()->weekNumber( lastDayOfYear ) != q->calendar()->weekNumber( day ) ) {
00229 day = q->calendar()->addDays( lastDayOfYear, - q->calendar()->daysInWeek( q->date() ) );
00230 }
00231 }
00232 }
00233
00234 QDate KDatePicker::KDatePickerPrivate::validDateInYearMonth( int year, int month )
00235 {
00236 QDate newDate;
00237
00238
00239
00240 if ( q->calendar()->isValid( year, month, 1 ) ) {
00241 q->calendar()->setDate( newDate, year, month, 1 );
00242 } else if ( q->calendar()->isValid( year, month + 1, 1 ) ) {
00243 q->calendar()->setDate( newDate, year, month, 1 );
00244 q->calendar()->addDays( newDate, -1 );
00245 } else {
00246 newDate = QDate::fromJulianDay( 0 );
00247 }
00248
00249 return newDate;
00250 }
00251
00252 KDatePicker::KDatePicker( QWidget* parent ) : QFrame( parent ), d( new KDatePickerPrivate( this ) )
00253 {
00254 init( QDate::currentDate() );
00255 }
00256
00257 KDatePicker::KDatePicker( const QDate& date_, QWidget* parent )
00258 : QFrame( parent ), d( new KDatePickerPrivate( this ) )
00259 {
00260 init( date_ );
00261 }
00262
00263 void KDatePicker::init( const QDate &date_ )
00264 {
00265
00266 QBoxLayout * topLayout = new QVBoxLayout( this );
00267 topLayout->setSpacing( 0 );
00268 topLayout->setMargin( 0 );
00269
00270 d->navigationLayout = new QHBoxLayout();
00271 d->navigationLayout->setSpacing( 0 );
00272 d->navigationLayout->setMargin( 0 );
00273 topLayout->addLayout( d->navigationLayout );
00274 d->navigationLayout->addStretch();
00275 d->yearBackward = new QToolButton( this );
00276 d->yearBackward->setAutoRaise( true );
00277 d->navigationLayout->addWidget( d->yearBackward );
00278 d->monthBackward = new QToolButton( this );
00279 d->monthBackward ->setAutoRaise( true );
00280 d->navigationLayout->addWidget( d->monthBackward );
00281 d->navigationLayout->addSpacing( KDialog::spacingHint() );
00282
00283 d->selectMonth = new QToolButton( this );
00284 d->selectMonth ->setAutoRaise( true );
00285 d->navigationLayout->addWidget( d->selectMonth );
00286 d->selectYear = new QToolButton( this );
00287 d->selectYear->setCheckable( true );
00288 d->selectYear->setAutoRaise( true );
00289 d->navigationLayout->addWidget( d->selectYear );
00290 d->navigationLayout->addSpacing( KDialog::spacingHint() );
00291
00292 d->monthForward = new QToolButton( this );
00293 d->monthForward ->setAutoRaise( true );
00294 d->navigationLayout->addWidget( d->monthForward );
00295 d->yearForward = new QToolButton( this );
00296 d->yearForward ->setAutoRaise( true );
00297 d->navigationLayout->addWidget( d->yearForward );
00298 d->navigationLayout->addStretch();
00299
00300 d->line = new KLineEdit( this );
00301 d->val = new KDateValidator( this );
00302 d->table = new KDateTable( this );
00303 setFocusProxy( d->table );
00304
00305 d->fontsize = KGlobalSettings::generalFont().pointSize();
00306 if ( d->fontsize == -1 ) {
00307 d->fontsize = QFontInfo( KGlobalSettings::generalFont() ).pointSize();
00308 }
00309
00310 d->fontsize++;
00311
00312 d->selectWeek = new KComboBox( this );
00313 d->selectWeek->setFocusPolicy( Qt::NoFocus );
00314 d->todayButton = new QToolButton( this );
00315 d->todayButton->setIcon( KIcon( "go-jump-today" ) );
00316
00317 d->yearForward->setToolTip( i18n( "Next year" ) );
00318 d->yearBackward->setToolTip( i18n( "Previous year" ) );
00319 d->monthForward->setToolTip( i18n( "Next month" ) );
00320 d->monthBackward->setToolTip( i18n( "Previous month" ) );
00321 d->selectWeek->setToolTip( i18n( "Select a week" ) );
00322 d->selectMonth->setToolTip( i18n( "Select a month" ) );
00323 d->selectYear->setToolTip( i18n( "Select a year" ) );
00324 d->todayButton->setToolTip( i18n( "Select the current day" ) );
00325
00326
00327 setFontSize( d->fontsize );
00328 d->line->setValidator( d->val );
00329 d->line->installEventFilter( this );
00330 if ( QApplication::isRightToLeft() ) {
00331 d->yearForward->setIcon( KIcon( QLatin1String( "arrow-left-double" ) ) );
00332 d->yearBackward->setIcon( KIcon( QLatin1String( "arrow-right-double" ) ) );
00333 d->monthForward->setIcon( KIcon( QLatin1String( "arrow-left" ) ) );
00334 d->monthBackward->setIcon( KIcon( QLatin1String( "arrow-right" ) ) );
00335 } else {
00336 d->yearForward->setIcon( KIcon( QLatin1String( "arrow-right-double" ) ) );
00337 d->yearBackward->setIcon( KIcon( QLatin1String( "arrow-left-double" ) ) );
00338 d->monthForward->setIcon( KIcon( QLatin1String( "arrow-right" ) ) );
00339 d->monthBackward->setIcon( KIcon( QLatin1String( "arrow-left" ) ) );
00340 }
00341
00342 connect( d->table, SIGNAL( dateChanged( const QDate& ) ), SLOT( dateChangedSlot( const QDate& ) ) );
00343 connect( d->table, SIGNAL( tableClicked() ), SLOT( tableClickedSlot() ) );
00344 connect( d->monthForward, SIGNAL( clicked() ), SLOT( monthForwardClicked() ) );
00345 connect( d->monthBackward, SIGNAL( clicked() ), SLOT( monthBackwardClicked() ) );
00346 connect( d->yearForward, SIGNAL( clicked() ), SLOT( yearForwardClicked() ) );
00347 connect( d->yearBackward, SIGNAL( clicked() ), SLOT( yearBackwardClicked() ) );
00348 connect( d->selectWeek, SIGNAL( activated( int ) ), SLOT( weekSelected( int ) ) );
00349 connect( d->todayButton, SIGNAL( clicked() ), SLOT( todayButtonClicked() ) );
00350 connect( d->selectMonth, SIGNAL( clicked() ), SLOT( selectMonthClicked() ) );
00351 connect( d->selectYear, SIGNAL( toggled( bool ) ), SLOT( selectYearClicked() ) );
00352 connect( d->line, SIGNAL( returnPressed() ), SLOT( lineEnterPressed() ) );
00353
00354
00355 topLayout->addWidget( d->table );
00356
00357 QBoxLayout * bottomLayout = new QHBoxLayout();
00358 bottomLayout->setMargin( 0 );
00359 bottomLayout->setSpacing( 0 );
00360 topLayout->addLayout( bottomLayout );
00361
00362 bottomLayout->addWidget( d->todayButton );
00363 bottomLayout->addWidget( d->line );
00364 bottomLayout->addWidget( d->selectWeek );
00365
00366 d->table->setDate( date_ );
00367 dateChangedSlot( date_ );
00368 }
00369
00370 KDatePicker::~KDatePicker()
00371 {
00372 delete d;
00373 }
00374
00375 bool KDatePicker::eventFilter( QObject *o, QEvent *e )
00376 {
00377 if ( e->type() == QEvent::KeyPress ) {
00378 QKeyEvent * k = ( QKeyEvent * )e;
00379
00380 if ( ( k->key() == Qt::Key_PageUp ) ||
00381 ( k->key() == Qt::Key_PageDown ) ||
00382 ( k->key() == Qt::Key_Up ) ||
00383 ( k->key() == Qt::Key_Down ) ) {
00384 QApplication::sendEvent( d->table, e );
00385 d->table->setFocus();
00386 return true;
00387 }
00388 }
00389 return QFrame::eventFilter( o, e );
00390 }
00391
00392 void KDatePicker::resizeEvent( QResizeEvent* e )
00393 {
00394 QWidget::resizeEvent( e );
00395 }
00396
00397 void KDatePicker::dateChangedSlot( const QDate &date_ )
00398 {
00399 d->line->setText( calendar()->formatDate( date_, KLocale::ShortDate ) );
00400 d->selectMonth->setText( calendar()->monthName( date_, KCalendarSystem::LongName ) );
00401 d->fillWeeksCombo();
00402
00403
00404 QDate firstDay;
00405
00406
00407
00408
00409 QDate day;
00410 if ( ! calendar()->setDate( firstDay, calendar()->year( date_ ), 1, 1 ) ) {
00411 firstDay = calendar()->earliestValidDate();
00412 }
00413 d->selectWeek->setCurrentIndex( ( calendar()->dayOfYear( date_ ) + calendar()->dayOfWeek( firstDay ) - 2 ) /
00414 calendar()->daysInWeek( date_ ) );
00415 d->selectYear->setText( calendar()->yearString( date_, KCalendarSystem::LongFormat ) );
00416
00417 emit( dateChanged( date_ ) );
00418 }
00419
00420 void KDatePicker::tableClickedSlot()
00421 {
00422 emit( dateSelected( date() ) );
00423 emit( tableClicked() );
00424 }
00425
00426 const QDate & KDatePicker::date() const
00427 {
00428 return d->table->date();
00429 }
00430
00431 bool KDatePicker::setDate( const QDate &date_ )
00432 {
00433
00434
00435 return d->table->setDate( date_ );
00436 }
00437
00438 const KCalendarSystem *KDatePicker::calendar() const
00439 {
00440 return d->table->calendar();
00441 }
00442
00443 bool KDatePicker::setCalendar( KCalendarSystem *calendar )
00444 {
00445 return d->table->setCalendar( calendar );
00446 }
00447
00448 bool KDatePicker::setCalendar( const QString &calendarType )
00449 {
00450 return d->table->setCalendar( calendarType );
00451 }
00452
00453 void KDatePicker::monthForwardClicked()
00454 {
00455 if ( ! setDate( calendar()->addMonths( date(), 1 ) ) ) {
00456 KNotification::beep();
00457 }
00458 d->table->setFocus();
00459 }
00460
00461 void KDatePicker::monthBackwardClicked()
00462 {
00463 if ( ! setDate( calendar()->addMonths( date(), -1 ) ) ) {
00464 KNotification::beep();
00465 }
00466 d->table->setFocus();
00467 }
00468
00469 void KDatePicker::yearForwardClicked()
00470 {
00471 if ( ! setDate( calendar()->addYears( d->table->date(), 1 ) ) ) {
00472 KNotification::beep();
00473 }
00474 d->table->setFocus();
00475 }
00476
00477 void KDatePicker::yearBackwardClicked()
00478 {
00479 if ( ! setDate( calendar()->addYears( d->table->date(), -1 ) ) ) {
00480 KNotification::beep();
00481 }
00482 d->table->setFocus();
00483 }
00484
00485 void KDatePicker::weekSelected( int index )
00486 {
00487 QDate targetDay = d->selectWeek->itemData( index ).toDateTime().date();
00488
00489 if ( ! setDate( targetDay ) ) {
00490 KNotification::beep();
00491 }
00492 d->table->setFocus();
00493 }
00494
00495 void KDatePicker::selectMonthClicked()
00496 {
00497 d->table->setFocus();
00498
00499 QMenu popup( d->selectMonth );
00500
00501
00502
00503 for ( int m = 1; m <= calendar()->monthsInYear( date() ); m++ ) {
00504 popup.addAction( calendar()->monthName( m, calendar()->year( date() ) ) )->setData( m );
00505 }
00506
00507 QAction *item = popup.actions()[ calendar()->month( date() ) - 1 ];
00508
00509 if ( item ) {
00510 popup.setActiveAction( item );
00511 }
00512
00513
00514 if ( ( item = popup.exec( d->selectMonth->mapToGlobal( QPoint( 0, 0 ) ), item ) ) == 0 ) {
00515 return;
00516 }
00517
00518
00519
00520 QDate newDate = d->validDateInYearMonth( calendar()->year( date() ), item->data().toInt() );
00521
00522
00523
00524 if ( calendar()->isValid( newDate ) ) {
00525 calendar()->setDate( newDate,
00526 calendar()->year( date() ), item->data().toInt(),
00527 qMin( calendar()->day( date() ), calendar()->daysInMonth( newDate ) )
00528 );
00529 }
00530
00531
00532 if ( ! setDate( newDate ) ) {
00533 KNotification::beep();
00534 }
00535 }
00536
00537 void KDatePicker::selectYearClicked()
00538 {
00539 if ( !d->selectYear->isChecked() )
00540 return;
00541
00542 QDate newDate;
00543
00544 KPopupFrame *popup = new KPopupFrame( this );
00545 KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector( calendar(), date(), popup );
00546 picker->resize( picker->sizeHint() );
00547 picker->setYear( calendar()->year( date() ) );
00548 picker->selectAll();
00549 popup->setMainWidget( picker );
00550 connect( picker, SIGNAL( closeMe( int ) ), popup, SLOT( close( int ) ) );
00551 connect( popup, SIGNAL( destroyed( QObject* ) ), this, SLOT( uncheckYearSelector() ) );
00552
00553
00554 popup->setAttribute(Qt::WA_DeleteOnClose);
00555 picker->setFocus();
00556
00557 if( popup->exec( d->selectYear->mapToGlobal( QPoint( 0, d->selectMonth->height() ) ) ) ) {
00558
00559
00560
00561
00562 newDate = d->validDateInYearMonth( picker->year(), calendar()->month( date() ) );
00563
00564
00565
00566 if ( calendar()->isValid( newDate ) ) {
00567 calendar()->setDate( newDate,
00568 picker->year(), calendar()->month( date() ),
00569 qMin( calendar()->day( date() ), calendar()->daysInMonth( newDate ) )
00570 );
00571 }
00572 }
00573
00574
00575 if ( ! setDate( newDate ) ) {
00576 KNotification::beep();
00577 }
00578
00579 d->selectYear->setChecked( false );
00580 }
00581
00582 void KDatePicker::uncheckYearSelector()
00583 {
00584 d->selectYear->setChecked(false);
00585 d->selectYear->update();
00586 }
00587
00588
00589
00590 void KDatePicker::setEnabled( bool enable )
00591 {
00592 QWidget * widgets[] = {
00593 d->yearForward, d->yearBackward, d->monthForward, d->monthBackward,
00594 d->selectMonth, d->selectYear,
00595 d->line, d->table, d->selectWeek, d->todayButton
00596 };
00597 const int Size = sizeof( widgets ) / sizeof( widgets[0] );
00598 int count;
00599
00600 for( count = 0; count < Size; ++count ) {
00601 widgets[count]->setEnabled( enable );
00602 }
00603 d->table->setFocus();
00604 }
00605
00606 KDateTable *KDatePicker::dateTable() const
00607 {
00608 return d->table;
00609 }
00610
00611 void KDatePicker::lineEnterPressed()
00612 {
00613 QDate newDate = calendar()->readDate( d->line->text() );
00614
00615 if ( calendar()->isValid( newDate ) ) {
00616 emit( dateEntered( newDate ) );
00617 setDate( newDate );
00618 d->table->setFocus();
00619 } else {
00620 KNotification::beep();
00621 }
00622 }
00623
00624 void KDatePicker::todayButtonClicked()
00625 {
00626 setDate( QDate::currentDate() );
00627 d->table->setFocus();
00628 }
00629
00630 QSize KDatePicker::sizeHint() const
00631 {
00632 return QWidget::sizeHint();
00633 }
00634
00635 void KDatePicker::setFontSize( int s )
00636 {
00637 QWidget * buttons[] = {
00638 d->selectMonth,
00639 d->selectYear,
00640 };
00641 const int NoOfButtons = sizeof( buttons ) / sizeof( buttons[0] );
00642 int count;
00643 QFont font;
00644 QRect r;
00645
00646 d->fontsize = s;
00647 for( count = 0; count < NoOfButtons; ++count ) {
00648 font = buttons[count]->font();
00649 font.setPointSize( s );
00650 buttons[count]->setFont( font );
00651 }
00652 d->table->setFontSize( s );
00653
00654 QFontMetrics metrics( d->selectMonth->fontMetrics() );
00655 QString longestMonth;
00656
00657 for ( int i = 1; ; ++i ) {
00658 QString str = calendar()->monthName( i, calendar()->year( date() ), KCalendarSystem::LongName );
00659 if ( str.isNull() ) {
00660 break;
00661 }
00662 r = metrics.boundingRect( str );
00663
00664 if ( r.width() > d->maxMonthRect.width() ) {
00665 d->maxMonthRect.setWidth( r.width() );
00666 longestMonth = str;
00667 }
00668 if ( r.height() > d->maxMonthRect.height() ) {
00669 d->maxMonthRect.setHeight( r.height() );
00670 }
00671 }
00672
00673 QStyleOptionToolButton opt;
00674 opt.initFrom( d->selectMonth );
00675 opt.text = longestMonth;
00676
00677
00678 QSize textSize = metrics.size( Qt::TextShowMnemonic, longestMonth );
00679 textSize.setWidth( textSize.width() + metrics.width( QLatin1Char(' ') ) * 2 );
00680 int w = textSize.width();
00681 int h = textSize.height();
00682 opt.rect.setHeight( h );
00683
00684 QSize metricBound = style()->sizeFromContents(
00685 QStyle::CT_ToolButton, &opt, QSize( w, h ), d->selectMonth
00686 ).expandedTo( QApplication::globalStrut() );
00687
00688 d->selectMonth->setMinimumSize( metricBound );
00689 }
00690
00691 int KDatePicker::fontSize() const
00692 {
00693 return d->fontsize;
00694 }
00695
00696
00697 void KDatePicker::setCloseButton( bool enable )
00698 {
00699 if ( enable == ( d->closeButton != 0L ) ) {
00700 return;
00701 }
00702
00703 if ( enable ) {
00704 d->closeButton = new QToolButton( this );
00705 d->closeButton->setAutoRaise( true );
00706 d->navigationLayout->addSpacing( KDialog::spacingHint() );
00707 d->navigationLayout->addWidget( d->closeButton );
00708 d->closeButton->setToolTip( i18nc( "@action:button", "Close" ) );
00709 d->closeButton->setIcon( SmallIcon( "window-close" ) );
00710 connect( d->closeButton, SIGNAL( clicked() ),
00711 topLevelWidget(), SLOT( close() ) );
00712 } else {
00713 delete d->closeButton;
00714 d->closeButton = 0L;
00715 }
00716
00717 updateGeometry();
00718 }
00719
00720 bool KDatePicker::hasCloseButton() const
00721 {
00722 return ( d->closeButton );
00723 }