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 QDate newDate;
00540
00541 if ( !d->selectYear->isChecked() ) {
00542 return;
00543 }
00544
00545 KPopupFrame *popup = new KPopupFrame( this );
00546 KDatePickerPrivateYearSelector *picker = new KDatePickerPrivateYearSelector( calendar(), date(), popup );
00547 picker->resize( picker->sizeHint() );
00548 picker->setYear( calendar()->year( date() ) );
00549 picker->selectAll();
00550 popup->setMainWidget( picker );
00551 connect( picker, SIGNAL( closeMe( int ) ), popup, SLOT( close( int ) ) );
00552 picker->setFocus();
00553
00554 if( popup->exec( d->selectYear->mapToGlobal( QPoint( 0, d->selectMonth->height() ) ) ) ) {
00555
00556
00557 newDate = d->validDateInYearMonth( picker->year(), calendar()->month( date() ) );
00558
00559
00560
00561 if ( calendar()->isValid( newDate ) ) {
00562 calendar()->setDate( newDate,
00563 picker->year(), calendar()->month( date() ),
00564 qMin( calendar()->day( date() ), calendar()->daysInMonth( newDate ) )
00565 );
00566 }
00567 }
00568
00569
00570 if ( ! setDate( newDate ) ) {
00571 KNotification::beep();
00572 }
00573
00574 d->selectYear->setChecked( false );
00575 delete popup;
00576 }
00577
00578
00579
00580
00581 void KDatePicker::setEnabled( bool enable )
00582 {
00583 QWidget * widgets[] = {
00584 d->yearForward, d->yearBackward, d->monthForward, d->monthBackward,
00585 d->selectMonth, d->selectYear,
00586 d->line, d->table, d->selectWeek, d->todayButton
00587 };
00588 const int Size = sizeof( widgets ) / sizeof( widgets[0] );
00589 int count;
00590
00591 for( count = 0; count < Size; ++count ) {
00592 widgets[count]->setEnabled( enable );
00593 }
00594 d->table->setFocus();
00595 }
00596
00597 KDateTable *KDatePicker::dateTable() const
00598 {
00599 return d->table;
00600 }
00601
00602 void KDatePicker::lineEnterPressed()
00603 {
00604 QDate newDate = calendar()->readDate( d->line->text() );
00605
00606 if ( calendar()->isValid( newDate ) ) {
00607 emit( dateEntered( newDate ) );
00608 setDate( newDate );
00609 d->table->setFocus();
00610 } else {
00611 KNotification::beep();
00612 }
00613 }
00614
00615 void KDatePicker::todayButtonClicked()
00616 {
00617 setDate( QDate::currentDate() );
00618 d->table->setFocus();
00619 }
00620
00621 QSize KDatePicker::sizeHint() const
00622 {
00623 return QWidget::sizeHint();
00624 }
00625
00626 void KDatePicker::setFontSize( int s )
00627 {
00628 QWidget * buttons[] = {
00629 d->selectMonth,
00630 d->selectYear,
00631 };
00632 const int NoOfButtons = sizeof( buttons ) / sizeof( buttons[0] );
00633 int count;
00634 QFont font;
00635 QRect r;
00636
00637 d->fontsize = s;
00638 for( count = 0; count < NoOfButtons; ++count ) {
00639 font = buttons[count]->font();
00640 font.setPointSize( s );
00641 buttons[count]->setFont( font );
00642 }
00643 d->table->setFontSize( s );
00644
00645 QFontMetrics metrics( d->selectMonth->fontMetrics() );
00646 QString longestMonth;
00647
00648 for ( int i = 1; ; ++i ) {
00649 QString str = calendar()->monthName( i, calendar()->year( date() ), KCalendarSystem::LongName );
00650 if ( str.isNull() ) {
00651 break;
00652 }
00653 r = metrics.boundingRect( str );
00654
00655 if ( r.width() > d->maxMonthRect.width() ) {
00656 d->maxMonthRect.setWidth( r.width() );
00657 longestMonth = str;
00658 }
00659 if ( r.height() > d->maxMonthRect.height() ) {
00660 d->maxMonthRect.setHeight( r.height() );
00661 }
00662 }
00663
00664 QStyleOptionToolButton opt;
00665 opt.initFrom( d->selectMonth );
00666 opt.text = longestMonth;
00667
00668
00669 QSize textSize = metrics.size( Qt::TextShowMnemonic, longestMonth );
00670 textSize.setWidth( textSize.width() + metrics.width( QLatin1Char(' ') ) * 2 );
00671 int w = textSize.width();
00672 int h = textSize.height();
00673 opt.rect.setHeight( h );
00674
00675 QSize metricBound = style()->sizeFromContents(
00676 QStyle::CT_ToolButton, &opt, QSize( w, h ), d->selectMonth
00677 ).expandedTo( QApplication::globalStrut() );
00678
00679 d->selectMonth->setMinimumSize( metricBound );
00680 }
00681
00682 int KDatePicker::fontSize() const
00683 {
00684 return d->fontsize;
00685 }
00686
00687
00688 void KDatePicker::setCloseButton( bool enable )
00689 {
00690 if ( enable == ( d->closeButton != 0L ) ) {
00691 return;
00692 }
00693
00694 if ( enable ) {
00695 d->closeButton = new QToolButton( this );
00696 d->closeButton->setAutoRaise( true );
00697 d->navigationLayout->addSpacing( KDialog::spacingHint() );
00698 d->navigationLayout->addWidget( d->closeButton );
00699 d->closeButton->setToolTip( i18nc( "@action:button", "Close" ) );
00700 d->closeButton->setIcon( SmallIcon( "window-close" ) );
00701 connect( d->closeButton, SIGNAL( clicked() ),
00702 topLevelWidget(), SLOT( close() ) );
00703 } else {
00704 delete d->closeButton;
00705 d->closeButton = 0L;
00706 }
00707
00708 updateGeometry();
00709 }
00710
00711 bool KDatePicker::hasCloseButton() const
00712 {
00713 return ( d->closeButton );
00714 }