kplato

kptcalendarpanel.cc

00001 /* This file is part of the KDE project
00002     Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org)
00003               (C) 1998-2001 Mirko Boehm (mirko@kde.org)
00004               (C) 2004 Dag Andersen <danders@get2net.dk>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019  * Boston, MA 02110-1301, USA.
00020 */
00021 
00022 /* This is based on KDatePicker. */
00023 
00024 #include "kptcalendarpanel.h"
00025 #include "kptdatetable.h"
00026 #include "kptcalendar.h"
00027 
00028 
00029 #include <kglobal.h>
00030 #include <kapplication.h>
00031 #include <klocale.h>
00032 #include <kcalendarsystem.h> 
00033 #include <kiconloader.h>
00034 #include <qframe.h>
00035 #include <qpainter.h>
00036 #include <qdialog.h>
00037 #include <qstyle.h>
00038 #include <qtoolbutton.h>
00039 #include <qtooltip.h>
00040 #include <qfont.h>
00041 #include <klineedit.h>
00042 #include <qvalidator.h>
00043 #include <kdebug.h>
00044 #include <knotifyclient.h>
00045 
00046 namespace KPlato
00047 {
00048 
00049 class CalendarPanel::CalendarPanelPrivate
00050 {
00051 public:
00052     CalendarPanelPrivate() : closeButton(0L), selectWeek(0L) {}
00053 
00054     QToolButton *closeButton;
00055     QToolButton *selectWeek;
00056 };
00057 
00058 
00059 CalendarPanel::CalendarPanel(QWidget *parent, QDate dt, const char *name, WFlags f)
00060   : QFrame(parent,name, f)
00061 {
00062   init( dt );
00063 }
00064 
00065 CalendarPanel::CalendarPanel( QWidget *parent, const char *name )
00066   : QFrame(parent,name)
00067 {
00068     init( QDate::currentDate() );
00069 }
00070 
00071 void CalendarPanel::init( const QDate &dt )
00072 {
00073   yearForward = new QToolButton(this);
00074   yearBackward = new QToolButton(this);
00075   monthForward = new QToolButton(this);
00076   monthBackward = new QToolButton(this);
00077   selectMonth = new QToolButton(this);
00078   selectYear = new QToolButton(this);
00079   line = new KLineEdit(this);
00080   val = new DateValidator(this);
00081   table = new DateTable(this, dt, "Calendar table", 0);
00082   fontsize = 10;
00083 
00084   d = new CalendarPanelPrivate();
00085   d->selectWeek = new QToolButton( this );
00086 
00087   QToolTip::add(yearForward, i18n("Next year"));
00088   QToolTip::add(yearBackward, i18n("Previous year"));
00089   QToolTip::add(monthForward, i18n("Next month"));
00090   QToolTip::add(monthBackward, i18n("Previous month"));
00091   QToolTip::add(d->selectWeek, i18n("Select a week"));
00092   QToolTip::add(selectMonth, i18n("Select a month"));
00093   QToolTip::add(selectYear, i18n("Select a year"));
00094 
00095   // -----
00096   setFontSize(10);
00097   line->setValidator(val);
00098   line->installEventFilter( this );
00099   yearForward->setPixmap(BarIcon(QString::fromLatin1("2rightarrow")));
00100   yearBackward->setPixmap(BarIcon(QString::fromLatin1("2leftarrow")));
00101   monthForward->setPixmap(BarIcon(QString::fromLatin1("1rightarrow")));
00102   monthBackward->setPixmap(BarIcon(QString::fromLatin1("1leftarrow")));
00103   setDate(dt); // set button texts
00104   connect(table, SIGNAL(dateChanged(QDate)), SLOT(dateChangedSlot(QDate)));
00105   connect(table, SIGNAL(tableClicked()), SLOT(tableClickedSlot()));
00106   connect(monthForward, SIGNAL(clicked()), SLOT(monthForwardClicked()));
00107   connect(monthBackward, SIGNAL(clicked()), SLOT(monthBackwardClicked()));
00108   connect(yearForward, SIGNAL(clicked()), SLOT(yearForwardClicked()));
00109   connect(yearBackward, SIGNAL(clicked()), SLOT(yearBackwardClicked()));
00110   connect(d->selectWeek, SIGNAL(clicked()), SLOT(selectWeekClicked()));
00111   connect(selectMonth, SIGNAL(clicked()), SLOT(selectMonthClicked()));
00112   connect(selectYear, SIGNAL(clicked()), SLOT(selectYearClicked()));
00113   connect(line, SIGNAL(returnPressed()), SLOT(lineEnterPressed()));
00114 
00115   connect(table, SIGNAL(weekdaySelected(int)), SLOT(slotWeekdaySelected(int)));
00116   connect(table, SIGNAL(weekSelected(int, int)), SLOT(slotWeekSelected(int, int)));
00117   connect(table, SIGNAL(selectionCleared()), SLOT(slotSelectionCleared()));
00118 
00119   table->setFocus();
00120 }
00121 
00122 CalendarPanel::~CalendarPanel()
00123 {
00124   delete d;
00125 }
00126 
00127 bool
00128 CalendarPanel::eventFilter(QObject *o, QEvent *e )
00129 {
00130    if ( e->type() == QEvent::KeyPress ) {
00131       QKeyEvent *k = (QKeyEvent *)e;
00132 
00133       if ( (k->key() == Qt::Key_Prior) ||
00134            (k->key() == Qt::Key_Next)  ||
00135            (k->key() == Qt::Key_Up)    ||
00136            (k->key() == Qt::Key_Down) )
00137        {
00138           QApplication::sendEvent( table, e );
00139           table->setFocus();
00140           return TRUE; // eat event
00141        }
00142    }
00143    return QFrame::eventFilter( o, e );
00144 }
00145 
00146 void
00147 CalendarPanel::resizeEvent(QResizeEvent*)
00148 {
00149     QWidget *buttons[] = {
00150     yearBackward,
00151         monthBackward,
00152         selectMonth,
00153         selectYear,
00154         monthForward,
00155         yearForward,
00156         d->closeButton
00157     };
00158     const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
00159     QSize sizes[NoOfButtons];
00160     int buttonHeight=0;
00161     int count;
00162     int w=0;
00163     int x=0;
00164     // ----- calculate button row height:
00165     for(count=0; count<NoOfButtons; ++count) {
00166         if ( buttons[count] ) { // closeButton may be 0L
00167             sizes[count]=buttons[count]->sizeHint();
00168             buttonHeight=QMAX(buttonHeight, sizes[count].height());
00169         }
00170         else
00171             sizes[count] = QSize(0,0); // closeButton
00172     }
00173 
00174     // ----- calculate size of the month button:
00175     for(count=0; count<NoOfButtons; ++count) {
00176         if(buttons[count]==selectMonth) {
00177             QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect);
00178             sizes[count].setWidth(QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin)));
00179         }
00180     }
00181     // ----- center buttons
00182     w=0;
00183     for(count=0; count<NoOfButtons; ++count)
00184     {
00185         w +=sizes[count].width();
00186     }
00187     x = (QMAX(w, width())-w)/2;
00188 
00189     // ----- place the buttons:
00190     for(count=0; count<NoOfButtons; ++count)
00191     {
00192         w=sizes[count].width();
00193         if ( buttons[count] )
00194             buttons[count]->setGeometry(x, 0, w, buttonHeight);
00195         x+=w;
00196     }
00197     // ----- place the line edit for direct input:
00198     sizes[0]=line->sizeHint();
00199     int week_width=d->selectWeek->fontMetrics().width(i18n("Week XX"))+((d->closeButton != 0L) ? 50 : 20);
00200     line->setGeometry(0, height()-sizes[0].height(), width()-week_width, sizes[0].height());
00201     d->selectWeek->setGeometry(width()-week_width, height()-sizes[0].height(), week_width, sizes[0].height());
00202     // ----- adjust the table:
00203     table->setGeometry(0, buttonHeight, width(),
00204                height()-buttonHeight-sizes[0].height());
00205 }
00206 
00207 void
00208 CalendarPanel::dateChangedSlot(QDate date)
00209 {
00210     //kdDebug() << "CalendarPanel::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl;
00211     line->setText(KGlobal::locale()->formatDate(date, true));
00212     d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date)));
00213     selectMonth->setText(KGlobal::locale()->calendar()->monthName(date.month(), false));
00214     selectYear->setText(date.toString("yyyy"));
00215     emit(dateChanged(date));
00216 }
00217 
00218 void
00219 CalendarPanel::tableClickedSlot()
00220 {
00221   //kdDebug() << "CalendarPanel::tableClickedSlot: table clicked." << endl;
00222   emit(dateSelected(table->getDate()));
00223   emit(tableClicked());
00224 }
00225 
00226 const QDate&
00227 CalendarPanel::getDate() const
00228 {
00229   return table->getDate();
00230 }
00231 
00232 const QDate &
00233 CalendarPanel::date() const
00234 {
00235     return table->getDate();
00236 }
00237 
00238 bool
00239 CalendarPanel::setDate(const QDate& date)
00240 {
00241     if(date.isValid()) {
00242     QString temp;
00243     // -----
00244     table->setDate(date);
00245     d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date)));
00246     selectMonth->setText(KGlobal::locale()->calendar()->monthName(date.month(), false));
00247     temp.setNum(date.year());
00248     selectYear->setText(temp);
00249     line->setText(KGlobal::locale()->formatDate(date, true));
00250     return true;
00251     } else {
00252     kdDebug() << "CalendarPanel::setDate: refusing to set invalid date." << endl;
00253     return false;
00254     }
00255 }
00256 
00257 void
00258 CalendarPanel::monthForwardClicked()
00259 {
00260     setDate( table->getDate().addMonths(1) );
00261 }
00262 
00263 void
00264 CalendarPanel::monthBackwardClicked()
00265 {
00266     setDate( table->getDate().addMonths(-1) );
00267 }
00268 
00269 void
00270 CalendarPanel::yearForwardClicked()
00271 {
00272     setDate( table->getDate().addYears(1) );
00273 }
00274 
00275 void
00276 CalendarPanel::yearBackwardClicked()
00277 {
00278     setDate( table->getDate().addYears(-1) );
00279 }
00280 
00281 void
00282 CalendarPanel::selectWeekClicked()
00283 {
00284   int week;
00285   PopupFrame* popup = new PopupFrame(this);
00286   DateInternalWeekSelector* picker = new DateInternalWeekSelector(fontsize, popup);
00287   // -----
00288   picker->resize(picker->sizeHint());
00289   popup->setMainWidget(picker);
00290   connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int)));
00291   picker->setFocus();
00292   if(popup->exec(d->selectWeek->mapToGlobal(QPoint(0, d->selectWeek->height()))))
00293     {
00294       QDate date;
00295       int year;
00296       // -----
00297       week=picker->getWeek();
00298       date=table->getDate();
00299       year=date.year();
00300       // ----- find the first selectable day in this week (hacky solution :)
00301       date.setYMD(year, 1, 1);
00302       while (weekOfYear(date)>50)
00303           date=date.addDays(1);
00304       while (weekOfYear(date)<week && (week!=53 || (week==53 &&
00305             (weekOfYear(date)!=52 || weekOfYear(date.addDays(1))!=1))))
00306           date=date.addDays(1);
00307       if (week==53 && weekOfYear(date)==52)
00308           while (weekOfYear(date.addDays(-1))==52)
00309               date=date.addDays(-1);
00310       // ----- set this date
00311       setDate(date);
00312     } else {
00313          KNotifyClient::beep();
00314     }
00315   delete popup;
00316 }
00317 
00318 void
00319 CalendarPanel::selectMonthClicked()
00320 {
00321   int month;
00322   PopupFrame* popup = new PopupFrame(this);
00323   DateInternalMonthPicker* picker = new DateInternalMonthPicker(fontsize, popup);
00324   // -----
00325   picker->resize(picker->sizeHint());
00326   popup->setMainWidget(picker);
00327   picker->setFocus();
00328   connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int)));
00329   if(popup->exec(selectMonth->mapToGlobal(QPoint(0, selectMonth->height()))))
00330     {
00331       QDate date;
00332       int day;
00333       // -----
00334       month=picker->getResult();
00335       date=table->getDate();
00336       day=date.day();
00337       // ----- construct a valid date in this month:
00338       date.setYMD(date.year(), month, 1);
00339       date.setYMD(date.year(), month, QMIN(day, date.daysInMonth()));
00340       // ----- set this month
00341       setDate(date);
00342     } else {
00343       KNotifyClient::beep();
00344     }
00345   delete popup;
00346 }
00347 
00348 void
00349 CalendarPanel::selectYearClicked()
00350 {
00351   int year;
00352   PopupFrame* popup = new PopupFrame(this);
00353   DateInternalYearSelector* picker = new DateInternalYearSelector(fontsize, popup);
00354   // -----
00355   picker->resize(picker->sizeHint());
00356   popup->setMainWidget(picker);
00357   connect(picker, SIGNAL(closeMe(int)), popup, SLOT(close(int)));
00358   picker->setFocus();
00359   if(popup->exec(selectYear->mapToGlobal(QPoint(0, selectMonth->height()))))
00360     {
00361       QDate date;
00362       int day;
00363       // -----
00364       year=picker->getYear();
00365       date=table->getDate();
00366       day=date.day();
00367       // ----- construct a valid date in this month:
00368       date.setYMD(year, date.month(), 1);
00369       date.setYMD(year, date.month(), QMIN(day, date.daysInMonth()));
00370       // ----- set this month
00371       setDate(date);
00372     } else {
00373       KNotifyClient::beep();
00374     }
00375   delete popup;
00376 }
00377 
00378 void
00379 CalendarPanel::setEnabled(bool enable)
00380 {
00381   QWidget *widgets[]= {
00382     yearForward, yearBackward, monthForward, monthBackward,
00383     selectMonth, selectYear,
00384     line, table, d->selectWeek };
00385   const int Size=sizeof(widgets)/sizeof(widgets[0]);
00386   int count;
00387   // -----
00388   for(count=0; count<Size; ++count)
00389     {
00390       widgets[count]->setEnabled(enable);
00391     }
00392     table->setEnabled(enable);
00393 }
00394 
00395 void
00396 CalendarPanel::lineEnterPressed()
00397 {
00398   QDate temp;
00399   // -----
00400   if(val->date(line->text(), temp)==QValidator::Acceptable)
00401     {
00402         //kdDebug() << "CalendarPanel::lineEnterPressed: valid date entered." << endl;
00403         emit(dateEntered(temp));
00404         setDate(temp);
00405     } else {
00406       KNotifyClient::beep();
00407       //kdDebug() << "CalendarPanel::lineEnterPressed: invalid date entered." << endl;
00408     }
00409 }
00410 
00411 QSize
00412 CalendarPanel::sizeHint() const
00413 {
00414   QSize tableSize=table->sizeHint();
00415   QWidget *buttons[]={
00416     yearBackward,
00417     monthBackward,
00418     selectMonth,
00419     selectYear,
00420     monthForward,
00421     yearForward,
00422     d->closeButton
00423   };
00424   const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
00425   QSize sizes[NoOfButtons];
00426   int cx=0, cy=0, count;
00427   // ----- store the size hints:
00428     for(count=0; count<NoOfButtons; ++count) {
00429         if ( buttons[count] )
00430             sizes[count]=buttons[count]->sizeHint();
00431         else
00432             sizes[count] = QSize(0,0);
00433 
00434         if(buttons[count]==selectMonth) {
00435             QSize metricBound = style().sizeFromContents(QStyle::CT_ToolButton, selectMonth, maxMonthRect);
00436             cx+=QMAX(metricBound.width(), maxMonthRect.width()+2*QApplication::style().pixelMetric(QStyle::PM_ButtonMargin));
00437         } else {
00438             cx+=sizes[count].width();
00439         }
00440         cy=QMAX(sizes[count].height(), cy);
00441     }
00442     // ----- calculate width hint:
00443     cx=QMAX(cx, tableSize.width()); // line edit ignored
00444     // ----- calculate height hint:
00445     cy+=tableSize.height()+line->sizeHint().height();
00446     return QSize(cx, cy);
00447 }
00448 
00449 void
00450 CalendarPanel::setFontSize(int s)
00451 {
00452   QWidget *buttons[]= {
00453     // yearBackward,
00454     // monthBackward,
00455     selectMonth,
00456     selectYear,
00457     // monthForward,
00458     // yearForward
00459   };
00460   const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
00461   int count;
00462   QFont font;
00463   QRect r;
00464   // -----
00465   fontsize=s;
00466   for(count=0; count<NoOfButtons; ++count)
00467     {
00468       font=buttons[count]->font();
00469       font.setPointSize(s);
00470       buttons[count]->setFont(font);
00471     }
00472   QFontMetrics metrics(selectMonth->fontMetrics());
00473   for(int i=1; i <= 12; ++i)
00474     { // maxMonthRect is used by sizeHint()
00475       r=metrics.boundingRect(KGlobal::locale()->calendar()->monthName(i, false));
00476       maxMonthRect.setWidth(QMAX(r.width(), maxMonthRect.width()));
00477       maxMonthRect.setHeight(QMAX(r.height(),  maxMonthRect.height()));
00478     }
00479   table->setFontSize(s);
00480 }
00481 
00482 void
00483 CalendarPanel::setCloseButton( bool enable )
00484 {
00485     if ( enable == (d->closeButton != 0L) )
00486         return;
00487 
00488     if ( enable ) {
00489         d->closeButton = new QToolButton( this );
00490         QToolTip::add(d->closeButton, i18n("Close"));
00491         d->closeButton->setPixmap( SmallIcon("remove") );
00492         connect( d->closeButton, SIGNAL( clicked() ),
00493                  topLevelWidget(), SLOT( close() ) );
00494     }
00495     else {
00496         delete d->closeButton;
00497         d->closeButton = 0L;
00498     }
00499     
00500     updateGeometry();
00501 }
00502 
00503 bool CalendarPanel::hasCloseButton() const
00504 {
00505     return (d->closeButton != 0L);
00506 }
00507 
00508 int CalendarPanel::weekOfYear(QDate date)
00509 {
00510     // Calculate ISO 8601 week number (taken from glibc/Gnumeric)
00511     int year, week, wday, jan1wday, nextjan1wday;
00512     QDate jan1date, nextjan1date;
00513 
00514     year=date.year();
00515     wday=date.dayOfWeek();
00516 
00517     jan1date=QDate(year,1,1);
00518     jan1wday=jan1date.dayOfWeek();
00519 
00520     week = (date.dayOfYear()-1 + jan1wday-1)/7 + ((jan1wday-1) == 0 ? 1 : 0);
00521 
00522     /* Does date belong to last week of previous year? */
00523     if ((week == 0) && (jan1wday > 4 /*THURSDAY*/)) {
00524         QDate tmpdate=QDate(year-1,12,31);
00525         return weekOfYear(tmpdate);
00526     }
00527 
00528     if ((jan1wday <= 4 /*THURSDAY*/) && (jan1wday > 1 /*MONDAY*/))
00529         week++;
00530 
00531     if (week == 53) {
00532         nextjan1date=QDate(year+1, 1, 1);
00533         nextjan1wday = nextjan1date.dayOfWeek();
00534         if (nextjan1wday <= 4 /*THURSDAY*/)
00535             week = 1;
00536     }
00537 
00538     return week;
00539 }
00540 
00541 void CalendarPanel::slotWeekdaySelected(int day) {
00542     //kdDebug()<<k_funcinfo<<endl;
00543     emit weekdaySelected(day);
00544 }
00545 
00546 void CalendarPanel::slotWeekSelected(int week, int year) {
00547     //kdDebug()<<k_funcinfo<<endl;
00548     emit weekSelected(week, year);
00549 }
00550 
00551 void CalendarPanel::setCalendar(Calendar *cal) {
00552     //kdDebug()<<k_funcinfo<<endl;
00553     table->clear();
00554     if (cal) {
00555         table->setMarkedWeekdays(cal->weekdaysMap());
00556         QPtrListIterator<CalendarDay> it = cal->days();
00557         //kdDebug()<<k_funcinfo<<"Days="<<it.count()<<endl;
00558         for (; it.current(); ++it) {
00559             if (it.current()->state() != Map::None) {
00560                 table->addMarkedDate(it.current()->date(), it.current()->state());
00561             //kdDebug()<<k_funcinfo<<"Added day: "<<it.current()->date().toString()<<"="<<it.current()->state()<<endl;
00562             }
00563         }
00564         setEnabled(true);
00565         table->setFocus();
00566     }
00567 }
00568 
00569 DateMap  CalendarPanel::selectedDates() {
00570     return table->selectedDates();
00571 }
00572 
00573 IntMap  CalendarPanel::selectedWeekdays() {
00574     return table->selectedWeekdays();
00575 }
00576 
00577 DateMap  CalendarPanel::markedDates() {
00578     return table->markedDates();
00579 }
00580 
00581 IntMap  CalendarPanel::markedWeekdays() {
00582     return table->markedWeekdays();
00583 }
00584 
00585 void CalendarPanel::clear() {
00586     table->clear();
00587     setEnabled(false);
00588 }
00589 
00590 void CalendarPanel::markSelected(int state) {
00591     table->markSelected(state);
00592  }
00593 
00594 void CalendarPanel::slotSelectionCleared() {
00595     emit selectionCleared();
00596  }
00597 
00598 void CalendarPanel::virtual_hook( int /*id*/, void* /*data*/ )
00599 { /*BASE::virtual_hook( id, data );*/ }
00600 
00601 }  //KPlato namespace
00602 
00603 #include "kptcalendarpanel.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys