kdatepicker.cpp
00001 /* -*- C++ -*- 00002 This file is part of the KDE libraries 00003 Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org) 00004 (C) 1998-2001 Mirko Boehm (mirko@kde.org) 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License as published by the Free Software Foundation; either 00008 version 2 of the License, or (at your option) any later version. 00009 00010 This library is distributed in the hope that it will be useful, 00011 but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00013 Library General Public License for more details. 00014 00015 You should have received a copy of the GNU Library General Public License 00016 along with this library; see the file COPYING.LIB. If not, write to 00017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <tqlayout.h> 00022 #include <tqframe.h> 00023 #include <tqpainter.h> 00024 #include <tqdialog.h> 00025 #include <tqstyle.h> 00026 #include <tqtoolbutton.h> 00027 #include <tqcombobox.h> 00028 #include <tqtooltip.h> 00029 #include <tqfont.h> 00030 #include <tqvalidator.h> 00031 #include <tqpopupmenu.h> 00032 #include <tqtimer.h> 00033 00034 #include "kdatepicker.h" 00035 #include <kglobal.h> 00036 #include <kapplication.h> 00037 #include <kdialog.h> 00038 #include <klocale.h> 00039 #include <kiconloader.h> 00040 #include <ktoolbar.h> 00041 #include <klineedit.h> 00042 #include <kdebug.h> 00043 #include <knotifyclient.h> 00044 #include <kcalendarsystem.h> 00045 00046 #include "kdatetbl.h" 00047 #include "kdatepicker.moc" 00048 00049 // Week numbers are defined by ISO 8601 00050 // See http://www.merlyn.demon.co.uk/weekinfo.htm for details 00051 00052 class KDatePicker::KDatePickerPrivate 00053 { 00054 public: 00055 KDatePickerPrivate() : closeButton(0L), selectWeek(0L), todayButton(0), navigationLayout(0) {} 00056 00057 void fillWeeksCombo(const TQDate &date); 00058 00059 TQToolButton *closeButton; 00060 TQComboBox *selectWeek; 00061 TQToolButton *todayButton; 00062 TQBoxLayout *navigationLayout; 00063 }; 00064 00065 void KDatePicker::fillWeeksCombo(const TQDate &date) 00066 { 00067 // every year can have a different number of weeks 00068 const KCalendarSystem * calendar = KGlobal::locale()->calendar(); 00069 00070 // it could be that we had 53,1..52 and now 1..53 which is the same number but different 00071 // so always fill with new values 00072 00073 d->selectWeek->clear(); 00074 00075 // We show all week numbers for all weeks between first day of year to last day of year 00076 // This of course can be a list like 53,1,2..52 00077 00078 TQDate day; 00079 int year = calendar->year(date); 00080 calendar->setYMD(day, year, 1, 1); 00081 int lastMonth = calendar->monthsInYear(day); 00082 TQDate lastDay, firstDayOfLastMonth; 00083 calendar->setYMD(firstDayOfLastMonth, year, lastMonth, 1); 00084 calendar->setYMD(lastDay, year, lastMonth, calendar->daysInMonth(firstDayOfLastMonth)); 00085 00086 for (; day <= lastDay ; day = calendar->addDays(day, 7 /*calendar->daysOfWeek()*/) ) 00087 { 00088 TQString week = i18n("Week %1").arg(calendar->weekNumber(day, &year)); 00089 if ( year != calendar->year(day) ) week += "*"; // show that this is a week from a different year 00090 d->selectWeek->insertItem(week); 00091 00092 // make sure that the week of the lastDay is always inserted: in Chinese calendar 00093 // system, this is not always the case 00094 if(day < lastDay && day.daysTo(lastDay) < 7 && calendar->weekNumber(day) != calendar->weekNumber(lastDay)) 00095 day = TQT_TQDATE_OBJECT(lastDay.addDays(-7)); 00096 } 00097 } 00098 00099 KDatePicker::KDatePicker(TQWidget *parent, TQDate dt, const char *name) 00100 : TQFrame(parent,name) 00101 { 00102 init( dt ); 00103 } 00104 00105 KDatePicker::KDatePicker(TQWidget *parent, TQDate dt, const char *name, WFlags f) 00106 : TQFrame(parent,name, f) 00107 { 00108 init( dt ); 00109 } 00110 00111 KDatePicker::KDatePicker( TQWidget *parent, const char *name ) 00112 : TQFrame(parent,name) 00113 { 00114 init( TQDate::currentDate() ); 00115 } 00116 00117 void KDatePicker::init( const TQDate &dt ) 00118 { 00119 d = new KDatePickerPrivate(); 00120 00121 TQBoxLayout * topLayout = new TQVBoxLayout(this); 00122 00123 d->navigationLayout = new TQHBoxLayout(topLayout); 00124 d->navigationLayout->addStretch(); 00125 yearBackward = new TQToolButton(this); 00126 yearBackward->setAutoRaise(true); 00127 d->navigationLayout->addWidget(yearBackward); 00128 monthBackward = new TQToolButton(this); 00129 monthBackward ->setAutoRaise(true); 00130 d->navigationLayout->addWidget(monthBackward); 00131 d->navigationLayout->addSpacing(KDialog::spacingHint()); 00132 00133 selectMonth = new TQToolButton(this); 00134 selectMonth ->setAutoRaise(true); 00135 d->navigationLayout->addWidget(selectMonth); 00136 selectYear = new TQToolButton(this); 00137 selectYear->setToggleButton(true); 00138 selectYear->setAutoRaise(true); 00139 d->navigationLayout->addWidget(selectYear); 00140 d->navigationLayout->addSpacing(KDialog::spacingHint()); 00141 00142 monthForward = new TQToolButton(this); 00143 monthForward ->setAutoRaise(true); 00144 d->navigationLayout->addWidget(monthForward); 00145 yearForward = new TQToolButton(this); 00146 yearForward ->setAutoRaise(true); 00147 d->navigationLayout->addWidget(yearForward); 00148 d->navigationLayout->addStretch(); 00149 00150 line = new KLineEdit(this); 00151 val = new KDateValidator(this); 00152 table = new KDateTable(this); 00153 fontsize = KGlobalSettings::generalFont().pointSize(); 00154 if (fontsize == -1) 00155 fontsize = TQFontInfo(KGlobalSettings::generalFont()).pointSize(); 00156 00157 fontsize++; // Make a little bigger 00158 00159 d->selectWeek = new TQComboBox(false, this); // read only week selection 00160 d->todayButton = new TQToolButton(this); 00161 d->todayButton->setIconSet(SmallIconSet("today")); 00162 00163 TQToolTip::add(yearForward, i18n("Next year")); 00164 TQToolTip::add(yearBackward, i18n("Previous year")); 00165 TQToolTip::add(monthForward, i18n("Next month")); 00166 TQToolTip::add(monthBackward, i18n("Previous month")); 00167 TQToolTip::add(d->selectWeek, i18n("Select a week")); 00168 TQToolTip::add(selectMonth, i18n("Select a month")); 00169 TQToolTip::add(selectYear, i18n("Select a year")); 00170 TQToolTip::add(d->todayButton, i18n("Select the current day")); 00171 00172 // ----- 00173 setFontSize(fontsize); 00174 line->setValidator(val); 00175 line->installEventFilter( this ); 00176 if ( TQApplication::reverseLayout() ) 00177 { 00178 yearForward->setIconSet(BarIconSet(TQString::fromLatin1("2leftarrow"))); 00179 yearBackward->setIconSet(BarIconSet(TQString::fromLatin1("2rightarrow"))); 00180 monthForward->setIconSet(BarIconSet(TQString::fromLatin1("1leftarrow"))); 00181 monthBackward->setIconSet(BarIconSet(TQString::fromLatin1("1rightarrow"))); 00182 } 00183 else 00184 { 00185 yearForward->setIconSet(BarIconSet(TQString::fromLatin1("2rightarrow"))); 00186 yearBackward->setIconSet(BarIconSet(TQString::fromLatin1("2leftarrow"))); 00187 monthForward->setIconSet(BarIconSet(TQString::fromLatin1("1rightarrow"))); 00188 monthBackward->setIconSet(BarIconSet(TQString::fromLatin1("1leftarrow"))); 00189 } 00190 connect(table, TQT_SIGNAL(dateChanged(TQDate)), TQT_SLOT(dateChangedSlot(TQDate))); 00191 connect(table, TQT_SIGNAL(tableClicked()), TQT_SLOT(tableClickedSlot())); 00192 connect(monthForward, TQT_SIGNAL(clicked()), TQT_SLOT(monthForwardClicked())); 00193 connect(monthBackward, TQT_SIGNAL(clicked()), TQT_SLOT(monthBackwardClicked())); 00194 connect(yearForward, TQT_SIGNAL(clicked()), TQT_SLOT(yearForwardClicked())); 00195 connect(yearBackward, TQT_SIGNAL(clicked()), TQT_SLOT(yearBackwardClicked())); 00196 connect(d->selectWeek, TQT_SIGNAL(activated(int)), TQT_SLOT(weekSelected(int))); 00197 connect(d->todayButton, TQT_SIGNAL(clicked()), TQT_SLOT(todayButtonClicked())); 00198 connect(selectMonth, TQT_SIGNAL(clicked()), TQT_SLOT(selectMonthClicked())); 00199 connect(selectYear, TQT_SIGNAL(toggled(bool)), TQT_SLOT(selectYearClicked())); 00200 connect(line, TQT_SIGNAL(returnPressed()), TQT_SLOT(lineEnterPressed())); 00201 table->setFocus(); 00202 00203 00204 topLayout->addWidget(table); 00205 00206 TQBoxLayout * bottomLayout = new TQHBoxLayout(topLayout); 00207 bottomLayout->addWidget(d->todayButton); 00208 bottomLayout->addWidget(line); 00209 bottomLayout->addWidget(d->selectWeek); 00210 00211 table->setDate(dt); 00212 dateChangedSlot(dt); // needed because table emits changed only when newDate != oldDate 00213 } 00214 00215 KDatePicker::~KDatePicker() 00216 { 00217 delete d; 00218 } 00219 00220 bool 00221 KDatePicker::eventFilter(TQObject *o, TQEvent *e ) 00222 { 00223 if ( e->type() == TQEvent::KeyPress ) { 00224 TQKeyEvent *k = (TQKeyEvent *)e; 00225 00226 if ( (k->key() == TQt::Key_Prior) || 00227 (k->key() == TQt::Key_Next) || 00228 (k->key() == Qt::Key_Up) || 00229 (k->key() == Qt::Key_Down) ) 00230 { 00231 TQApplication::sendEvent( table, e ); 00232 table->setFocus(); 00233 return true; // eat event 00234 } 00235 } 00236 return TQFrame::eventFilter( o, e ); 00237 } 00238 00239 void 00240 KDatePicker::resizeEvent(TQResizeEvent* e) 00241 { 00242 TQWidget::resizeEvent(e); 00243 } 00244 00245 void 00246 KDatePicker::dateChangedSlot(TQDate date) 00247 { 00248 kdDebug(298) << "KDatePicker::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl; 00249 00250 const KCalendarSystem * calendar = KGlobal::locale()->calendar(); 00251 00252 line->setText(KGlobal::locale()->formatDate(date, true)); 00253 selectMonth->setText(calendar->monthName(date, false)); 00254 fillWeeksCombo(date); 00255 00256 // calculate the item num in the week combo box; normalize selected day so as if 1.1. is the first day of the week 00257 TQDate firstDay; 00258 calendar->setYMD(firstDay, calendar->year(date), 1, 1); 00259 d->selectWeek->setCurrentItem((calendar->dayOfYear(date) + calendar->dayOfWeek(firstDay) - 2) / 7/*calendar->daysInWeek()*/); 00260 selectYear->setText(calendar->yearString(date, false)); 00261 00262 emit(dateChanged(date)); 00263 } 00264 00265 void 00266 KDatePicker::tableClickedSlot() 00267 { 00268 kdDebug(298) << "KDatePicker::tableClickedSlot: table clicked." << endl; 00269 emit(dateSelected(table->getDate())); 00270 emit(tableClicked()); 00271 } 00272 00273 const TQDate& 00274 KDatePicker::getDate() const 00275 { 00276 return table->getDate(); 00277 } 00278 00279 const TQDate & 00280 KDatePicker::date() const 00281 { 00282 return table->getDate(); 00283 } 00284 00285 bool 00286 KDatePicker::setDate(const TQDate& date) 00287 { 00288 if(date.isValid()) 00289 { 00290 table->setDate(date); // this also emits dateChanged() which then calls our dateChangedSlot() 00291 return true; 00292 } 00293 else 00294 { 00295 kdDebug(298) << "KDatePicker::setDate: refusing to set invalid date." << endl; 00296 return false; 00297 } 00298 } 00299 00300 void 00301 KDatePicker::monthForwardClicked() 00302 { 00303 TQDate temp; 00304 temp = KGlobal::locale()->calendar()->addMonths( table->getDate(), 1 ); 00305 00306 setDate( temp ); 00307 } 00308 00309 void 00310 KDatePicker::monthBackwardClicked() 00311 { 00312 TQDate temp; 00313 temp = KGlobal::locale()->calendar()->addMonths( table->getDate(), -1 ); 00314 00315 setDate( temp ); 00316 } 00317 00318 void 00319 KDatePicker::yearForwardClicked() 00320 { 00321 TQDate temp; 00322 temp = KGlobal::locale()->calendar()->addYears( table->getDate(), 1 ); 00323 00324 setDate( temp ); 00325 } 00326 00327 void 00328 KDatePicker::yearBackwardClicked() 00329 { 00330 TQDate temp; 00331 temp = KGlobal::locale()->calendar()->addYears( table->getDate(), -1 ); 00332 00333 setDate( temp ); 00334 } 00335 00336 void KDatePicker::selectWeekClicked() {} // ### in 3.2 obsolete; kept for binary compatibility 00337 00338 void 00339 KDatePicker::weekSelected(int week) 00340 { 00341 const KCalendarSystem * calendar = KGlobal::locale()->calendar(); 00342 00343 TQDate date = table->getDate(); 00344 int year = calendar->year(date); 00345 00346 calendar->setYMD(date, year, 1, 1); // first day of selected year 00347 00348 // calculate the first day in the selected week (day 1 is first day of week) 00349 date = calendar->addDays(date, week * 7/*calendar->daysOfWeek()*/ -calendar->dayOfWeek(date) + 1); 00350 00351 setDate(date); 00352 } 00353 00354 void 00355 KDatePicker::selectMonthClicked() 00356 { 00357 // every year can have different month names (in some calendar systems) 00358 const KCalendarSystem * calendar = KGlobal::locale()->calendar(); 00359 TQDate date = table->getDate(); 00360 int i, month, months = calendar->monthsInYear(date); 00361 00362 TQPopupMenu popup(selectMonth); 00363 00364 for (i = 1; i <= months; i++) 00365 popup.insertItem(calendar->monthName(i, calendar->year(date)), i); 00366 00367 popup.setActiveItem(calendar->month(date) - 1); 00368 00369 if ( (month = popup.exec(selectMonth->mapToGlobal(TQPoint(0, 0)), calendar->month(date) - 1)) == -1 ) return; // canceled 00370 00371 int day = calendar->day(date); 00372 // ----- construct a valid date in this month: 00373 calendar->setYMD(date, calendar->year(date), month, 1); 00374 date = TQT_TQDATE_OBJECT(date.addDays(QMIN(day, calendar->daysInMonth(date)) - 1)); 00375 // ----- set this month 00376 setDate(date); 00377 } 00378 00379 void 00380 KDatePicker::selectYearClicked() 00381 { 00382 const KCalendarSystem * calendar = KGlobal::locale()->calendar(); 00383 00384 if (selectYear->state() == TQButton::Off) 00385 { 00386 return; 00387 } 00388 00389 int year; 00390 KPopupFrame* popup = new KPopupFrame(this); 00391 KDateInternalYearSelector* picker = new KDateInternalYearSelector(popup); 00392 // ----- 00393 picker->resize(picker->sizeHint()); 00394 picker->setYear( table->getDate().year() ); 00395 picker->selectAll(); 00396 popup->setMainWidget(picker); 00397 connect(picker, TQT_SIGNAL(closeMe(int)), popup, TQT_SLOT(close(int))); 00398 picker->setFocus(); 00399 if(popup->exec(selectYear->mapToGlobal(TQPoint(0, selectMonth->height())))) 00400 { 00401 TQDate date; 00402 int day; 00403 // ----- 00404 year=picker->getYear(); 00405 date=table->getDate(); 00406 day=calendar->day(date); 00407 // ----- construct a valid date in this month: 00408 //date.setYMD(year, date.month(), 1); 00409 //date.setYMD(year, date.month(), QMIN(day, date.daysInMonth())); 00410 calendar->setYMD(date, year, calendar->month(date), 00411 QMIN(day, calendar->daysInMonth(date))); 00412 // ----- set this month 00413 setDate(date); 00414 } else { 00415 KNotifyClient::beep(); 00416 } 00417 00418 delete popup; 00419 TQTimer::singleShot(0, this, TQT_SLOT(ensureSelectYearIsUp())); 00420 } 00421 00422 void 00423 KDatePicker::ensureSelectYearIsUp() 00424 { 00425 if (!selectYear->isDown()) 00426 { 00427 selectYear->setOn( false ); 00428 } 00429 } 00430 00431 void 00432 KDatePicker::setEnabled(bool enable) 00433 { 00434 TQWidget *widgets[]= { 00435 yearForward, yearBackward, monthForward, monthBackward, 00436 selectMonth, selectYear, 00437 line, table, d->selectWeek, d->todayButton }; 00438 const int Size=sizeof(widgets)/sizeof(widgets[0]); 00439 int count; 00440 // ----- 00441 for(count=0; count<Size; ++count) 00442 { 00443 widgets[count]->setEnabled(enable); 00444 } 00445 } 00446 00447 void 00448 KDatePicker::lineEnterPressed() 00449 { 00450 TQDate temp; 00451 // ----- 00452 if(val->date(line->text(), temp)==TQValidator::Acceptable) 00453 { 00454 kdDebug(298) << "KDatePicker::lineEnterPressed: valid date entered." << endl; 00455 emit(dateEntered(temp)); 00456 setDate(temp); 00457 } else { 00458 KNotifyClient::beep(); 00459 kdDebug(298) << "KDatePicker::lineEnterPressed: invalid date entered." << endl; 00460 } 00461 } 00462 00463 void 00464 KDatePicker::todayButtonClicked() 00465 { 00466 setDate(TQDate::currentDate()); 00467 } 00468 00469 TQSize 00470 KDatePicker::sizeHint() const 00471 { 00472 return TQWidget::sizeHint(); 00473 } 00474 00475 void 00476 KDatePicker::setFontSize(int s) 00477 { 00478 TQWidget *buttons[]= { 00479 // yearBackward, 00480 // monthBackward, 00481 selectMonth, 00482 selectYear, 00483 // monthForward, 00484 // yearForward 00485 }; 00486 const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]); 00487 int count; 00488 TQFont font; 00489 TQRect r; 00490 // ----- 00491 fontsize=s; 00492 for(count=0; count<NoOfButtons; ++count) 00493 { 00494 font=buttons[count]->font(); 00495 font.setPointSize(s); 00496 buttons[count]->setFont(font); 00497 } 00498 TQFontMetrics metrics(selectMonth->fontMetrics()); 00499 00500 for (int i = 1; ; ++i) 00501 { 00502 TQString str = KGlobal::locale()->calendar()->monthName(i, 00503 KGlobal::locale()->calendar()->year(table->getDate()), false); 00504 if (str.isNull()) break; 00505 r=metrics.boundingRect(str); 00506 maxMonthRect.setWidth(QMAX(r.width(), maxMonthRect.width())); 00507 maxMonthRect.setHeight(QMAX(r.height(), maxMonthRect.height())); 00508 } 00509 00510 TQSize metricBound = style().tqsizeFromContents(TQStyle::CT_ToolButton, 00511 selectMonth, 00512 maxMonthRect); 00513 selectMonth->setMinimumSize(metricBound); 00514 00515 table->setFontSize(s); 00516 } 00517 00518 void 00519 KDatePicker::setCloseButton( bool enable ) 00520 { 00521 if ( enable == (d->closeButton != 0L) ) 00522 return; 00523 00524 if ( enable ) { 00525 d->closeButton = new TQToolButton( this ); 00526 d->closeButton->setAutoRaise(true); 00527 d->navigationLayout->addSpacing(KDialog::spacingHint()); 00528 d->navigationLayout->addWidget(d->closeButton); 00529 TQToolTip::add(d->closeButton, i18n("Close")); 00530 d->closeButton->setPixmap( SmallIcon("remove") ); 00531 connect( d->closeButton, TQT_SIGNAL( clicked() ), 00532 topLevelWidget(), TQT_SLOT( close() ) ); 00533 } 00534 else { 00535 delete d->closeButton; 00536 d->closeButton = 0L; 00537 } 00538 00539 updateGeometry(); 00540 } 00541 00542 bool KDatePicker::hasCloseButton() const 00543 { 00544 return (d->closeButton); 00545 } 00546 00547 void KDatePicker::virtual_hook( int /*id*/, void* /*data*/ ) 00548 { /*BASE::virtual_hook( id, data );*/ } 00549