kplato

kptcalendarpanel.cpp
1/* This file is part of the KDE project
2 Copyright (C) 1997 Tim D. Gilman (tdgilman@best.org)
3 (C) 1998-2001 Mirko Boehm (mirko@kde.org)
4 (C) 2004 Dag Andersen <danders@get2net.dk>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20*/
21
22/* This is based on KDatePicker. */
23
24#include "kptcalendarpanel.h"
25#include "kptdatetable.h"
26#include "kptcalendar.h"
27
28
29#include <tdeglobal.h>
30#include <tdeapplication.h>
31#include <tdelocale.h>
32#include <kcalendarsystem.h>
33#include <kiconloader.h>
34#include <tqframe.h>
35#include <tqpainter.h>
36#include <tqdialog.h>
37#include <tqstyle.h>
38#include <tqtoolbutton.h>
39#include <tqtooltip.h>
40#include <tqfont.h>
41#include <klineedit.h>
42#include <tqvalidator.h>
43#include <kdebug.h>
44#include <knotifyclient.h>
45
46namespace KPlato
47{
48
49class CalendarPanel::CalendarPanelPrivate
50{
51public:
52 CalendarPanelPrivate() : closeButton(0L), selectWeek(0L) {}
53
54 TQToolButton *closeButton;
55 TQToolButton *selectWeek;
56};
57
58
59CalendarPanel::CalendarPanel(TQWidget *parent, TQDate dt, const char *name, WFlags f)
60 : TQFrame(parent,name, f)
61{
62 init( dt );
63}
64
65CalendarPanel::CalendarPanel( TQWidget *parent, const char *name )
66 : TQFrame(parent,name)
67{
68 init( TQDate::currentDate() );
69}
70
71void CalendarPanel::init( const TQDate &dt )
72{
73 yearForward = new TQToolButton(this);
74 yearBackward = new TQToolButton(this);
75 monthForward = new TQToolButton(this);
76 monthBackward = new TQToolButton(this);
77 selectMonth = new TQToolButton(this);
78 selectYear = new TQToolButton(this);
79 line = new KLineEdit(this);
80 val = new DateValidator(this);
81 table = new DateTable(this, dt, "Calendar table", 0);
82 fontsize = 10;
83
84 d = new CalendarPanelPrivate();
85 d->selectWeek = new TQToolButton( this );
86
87 TQToolTip::add(yearForward, i18n("Next year"));
88 TQToolTip::add(yearBackward, i18n("Previous year"));
89 TQToolTip::add(monthForward, i18n("Next month"));
90 TQToolTip::add(monthBackward, i18n("Previous month"));
91 TQToolTip::add(d->selectWeek, i18n("Select a week"));
92 TQToolTip::add(selectMonth, i18n("Select a month"));
93 TQToolTip::add(selectYear, i18n("Select a year"));
94
95 // -----
96 setFontSize(10);
97 line->setValidator(val);
98 line->installEventFilter( this );
99 yearForward->setPixmap(BarIcon(TQString::fromLatin1("2rightarrow")));
100 yearBackward->setPixmap(BarIcon(TQString::fromLatin1("2leftarrow")));
101 monthForward->setPixmap(BarIcon(TQString::fromLatin1("1rightarrow")));
102 monthBackward->setPixmap(BarIcon(TQString::fromLatin1("1leftarrow")));
103 setDate(dt); // set button texts
104 connect(table, TQ_SIGNAL(dateChanged(TQDate)), TQ_SLOT(dateChangedSlot(TQDate)));
105 connect(table, TQ_SIGNAL(tableClicked()), TQ_SLOT(tableClickedSlot()));
106 connect(monthForward, TQ_SIGNAL(clicked()), TQ_SLOT(monthForwardClicked()));
107 connect(monthBackward, TQ_SIGNAL(clicked()), TQ_SLOT(monthBackwardClicked()));
108 connect(yearForward, TQ_SIGNAL(clicked()), TQ_SLOT(yearForwardClicked()));
109 connect(yearBackward, TQ_SIGNAL(clicked()), TQ_SLOT(yearBackwardClicked()));
110 connect(d->selectWeek, TQ_SIGNAL(clicked()), TQ_SLOT(selectWeekClicked()));
111 connect(selectMonth, TQ_SIGNAL(clicked()), TQ_SLOT(selectMonthClicked()));
112 connect(selectYear, TQ_SIGNAL(clicked()), TQ_SLOT(selectYearClicked()));
113 connect(line, TQ_SIGNAL(returnPressed()), TQ_SLOT(lineEnterPressed()));
114
115 connect(table, TQ_SIGNAL(weekdaySelected(int)), TQ_SLOT(slotWeekdaySelected(int)));
116 connect(table, TQ_SIGNAL(weekSelected(int, int)), TQ_SLOT(slotWeekSelected(int, int)));
117 connect(table, TQ_SIGNAL(selectionCleared()), TQ_SLOT(slotSelectionCleared()));
118
119 table->setFocus();
120}
121
123{
124 delete d;
125}
126
127bool
128CalendarPanel::eventFilter(TQObject *o, TQEvent *e )
129{
130 if ( e->type() == TQEvent::KeyPress ) {
131 TQKeyEvent *k = (TQKeyEvent *)e;
132
133 if ( (k->key() == TQt::Key_Prior) ||
134 (k->key() == TQt::Key_Next) ||
135 (k->key() == TQt::Key_Up) ||
136 (k->key() == TQt::Key_Down) )
137 {
138 TQApplication::sendEvent( table, e );
139 table->setFocus();
140 return true; // eat event
141 }
142 }
143 return TQFrame::eventFilter( o, e );
144}
145
146void
148{
149 TQWidget *buttons[] = {
156 d->closeButton
157 };
158 const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
159 TQSize sizes[NoOfButtons];
160 int buttonHeight=0;
161 int count;
162 int w=0;
163 int x=0;
164 // ----- calculate button row height:
165 for(count=0; count<NoOfButtons; ++count) {
166 if ( buttons[count] ) { // closeButton may be 0L
167 sizes[count]=buttons[count]->sizeHint();
168 buttonHeight=TQMAX(buttonHeight, sizes[count].height());
169 }
170 else
171 sizes[count] = TQSize(0,0); // closeButton
172 }
173
174 // ----- calculate size of the month button:
175 for(count=0; count<NoOfButtons; ++count) {
176 if(buttons[count]==selectMonth) {
177 TQSize metricBound = style().sizeFromContents(TQStyle::CT_ToolButton, selectMonth, maxMonthRect);
178 sizes[count].setWidth(TQMAX(metricBound.width(), maxMonthRect.width()+2*TQApplication::style().pixelMetric(TQStyle::PM_ButtonMargin)));
179 }
180 }
181 // ----- center buttons
182 w=0;
183 for(count=0; count<NoOfButtons; ++count)
184 {
185 w +=sizes[count].width();
186 }
187 x = (TQMAX(w, width())-w)/2;
188
189 // ----- place the buttons:
190 for(count=0; count<NoOfButtons; ++count)
191 {
192 w=sizes[count].width();
193 if ( buttons[count] )
194 buttons[count]->setGeometry(x, 0, w, buttonHeight);
195 x+=w;
196 }
197 // ----- place the line edit for direct input:
198 sizes[0]=line->sizeHint();
199 int week_width=d->selectWeek->fontMetrics().width(i18n("Week XX"))+((d->closeButton != 0L) ? 50 : 20);
200 line->setGeometry(0, height()-sizes[0].height(), width()-week_width, sizes[0].height());
201 d->selectWeek->setGeometry(width()-week_width, height()-sizes[0].height(), week_width, sizes[0].height());
202 // ----- adjust the table:
203 table->setGeometry(0, buttonHeight, width(),
204 height()-buttonHeight-sizes[0].height());
205}
206
207void
208CalendarPanel::dateChangedSlot(TQDate date)
209{
210 //kdDebug() << "CalendarPanel::dateChangedSlot: date changed (" << date.year() << "/" << date.month() << "/" << date.day() << ")." << endl;
211 line->setText(TDEGlobal::locale()->formatDate(date, true));
212 d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date)));
213 selectMonth->setText(TDEGlobal::locale()->calendar()->monthName(date.month(), false));
214 selectYear->setText(date.toString("yyyy"));
215 emit(dateChanged(date));
216}
217
218void
219CalendarPanel::tableClickedSlot()
220{
221 //kdDebug() << "CalendarPanel::tableClickedSlot: table clicked." << endl;
222 emit(dateSelected(table->getDate()));
223 emit(tableClicked());
224}
225
226const TQDate&
228{
229 return table->getDate();
230}
231
232const TQDate &
234{
235 return table->getDate();
236}
237
238bool
240{
241 if(date.isValid()) {
242 TQString temp;
243 // -----
244 table->setDate(date);
245 d->selectWeek->setText(i18n("Week %1").arg(weekOfYear(date)));
246 selectMonth->setText(TDEGlobal::locale()->calendar()->monthName(date.month(), false));
247 temp.setNum(date.year());
248 selectYear->setText(temp);
249 line->setText(TDEGlobal::locale()->formatDate(date, true));
250 return true;
251 } else {
252 kdDebug() << "CalendarPanel::setDate: refusing to set invalid date." << endl;
253 return false;
254 }
255}
256
257void
258CalendarPanel::monthForwardClicked()
259{
260 setDate( table->getDate().addMonths(1) );
261}
262
263void
264CalendarPanel::monthBackwardClicked()
265{
266 setDate( table->getDate().addMonths(-1) );
267}
268
269void
270CalendarPanel::yearForwardClicked()
271{
272 setDate( table->getDate().addYears(1) );
273}
274
275void
276CalendarPanel::yearBackwardClicked()
277{
278 setDate( table->getDate().addYears(-1) );
279}
280
281void
282CalendarPanel::selectWeekClicked()
283{
284 int week;
285 PopupFrame* popup = new PopupFrame(this);
286 DateInternalWeekSelector* picker = new DateInternalWeekSelector(fontsize, popup);
287 // -----
288 picker->resize(picker->sizeHint());
289 popup->setMainWidget(picker);
290 connect(picker, TQ_SIGNAL(closeMe(int)), popup, TQ_SLOT(close(int)));
291 picker->setFocus();
292 if(popup->exec(d->selectWeek->mapToGlobal(TQPoint(0, d->selectWeek->height()))))
293 {
294 TQDate date;
295 int year;
296 // -----
297 week=picker->getWeek();
298 date=table->getDate();
299 year=date.year();
300 // ----- find the first selectable day in this week (hacky solution :)
301 date.setYMD(year, 1, 1);
302 while (weekOfYear(date)>50)
303 date=date.addDays(1);
304 while (weekOfYear(date)<week && (week!=53 || (week==53 &&
305 (weekOfYear(date)!=52 || weekOfYear(date.addDays(1))!=1))))
306 date=date.addDays(1);
307 if (week==53 && weekOfYear(date)==52)
308 while (weekOfYear(date.addDays(-1))==52)
309 date=date.addDays(-1);
310 // ----- set this date
311 setDate(date);
312 } else {
313 KNotifyClient::beep();
314 }
315 delete popup;
316}
317
318void
319CalendarPanel::selectMonthClicked()
320{
321 int month;
322 PopupFrame* popup = new PopupFrame(this);
323 DateInternalMonthPicker* picker = new DateInternalMonthPicker(fontsize, popup);
324 // -----
325 picker->resize(picker->sizeHint());
326 popup->setMainWidget(picker);
327 picker->setFocus();
328 connect(picker, TQ_SIGNAL(closeMe(int)), popup, TQ_SLOT(close(int)));
329 if(popup->exec(selectMonth->mapToGlobal(TQPoint(0, selectMonth->height()))))
330 {
331 TQDate date;
332 int day;
333 // -----
334 month=picker->getResult();
335 date=table->getDate();
336 day=date.day();
337 // ----- construct a valid date in this month:
338 date.setYMD(date.year(), month, 1);
339 date.setYMD(date.year(), month, TQMIN(day, date.daysInMonth()));
340 // ----- set this month
341 setDate(date);
342 } else {
343 KNotifyClient::beep();
344 }
345 delete popup;
346}
347
348void
349CalendarPanel::selectYearClicked()
350{
351 int year;
352 PopupFrame* popup = new PopupFrame(this);
353 DateInternalYearSelector* picker = new DateInternalYearSelector(fontsize, popup);
354 // -----
355 picker->resize(picker->sizeHint());
356 popup->setMainWidget(picker);
357 connect(picker, TQ_SIGNAL(closeMe(int)), popup, TQ_SLOT(close(int)));
358 picker->setFocus();
359 if(popup->exec(selectYear->mapToGlobal(TQPoint(0, selectMonth->height()))))
360 {
361 TQDate date;
362 int day;
363 // -----
364 year=picker->getYear();
365 date=table->getDate();
366 day=date.day();
367 // ----- construct a valid date in this month:
368 date.setYMD(year, date.month(), 1);
369 date.setYMD(year, date.month(), TQMIN(day, date.daysInMonth()));
370 // ----- set this month
371 setDate(date);
372 } else {
373 KNotifyClient::beep();
374 }
375 delete popup;
376}
377
378void
380{
381 TQWidget *widgets[]= {
384 line, table, d->selectWeek };
385 const int Size=sizeof(widgets)/sizeof(widgets[0]);
386 int count;
387 // -----
388 for(count=0; count<Size; ++count)
389 {
390 widgets[count]->setEnabled(enable);
391 }
392 table->setEnabled(enable);
393}
394
395void
396CalendarPanel::lineEnterPressed()
397{
398 TQDate temp;
399 // -----
400 if(val->date(line->text(), temp)==TQValidator::Acceptable)
401 {
402 //kdDebug() << "CalendarPanel::lineEnterPressed: valid date entered." << endl;
403 emit(dateEntered(temp));
404 setDate(temp);
405 } else {
406 KNotifyClient::beep();
407 //kdDebug() << "CalendarPanel::lineEnterPressed: invalid date entered." << endl;
408 }
409}
410
411TQSize
413{
414 TQSize tableSize=table->sizeHint();
415 TQWidget *buttons[]={
422 d->closeButton
423 };
424 const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
425 TQSize sizes[NoOfButtons];
426 int cx=0, cy=0, count;
427 // ----- store the size hints:
428 for(count=0; count<NoOfButtons; ++count) {
429 if ( buttons[count] )
430 sizes[count]=buttons[count]->sizeHint();
431 else
432 sizes[count] = TQSize(0,0);
433
434 if(buttons[count]==selectMonth) {
435 TQSize metricBound = style().sizeFromContents(TQStyle::CT_ToolButton, selectMonth, maxMonthRect);
436 cx+=TQMAX(metricBound.width(), maxMonthRect.width()+2*TQApplication::style().pixelMetric(TQStyle::PM_ButtonMargin));
437 } else {
438 cx+=sizes[count].width();
439 }
440 cy=TQMAX(sizes[count].height(), cy);
441 }
442 // ----- calculate width hint:
443 cx=TQMAX(cx, tableSize.width()); // line edit ignored
444 // ----- calculate height hint:
445 cy+=tableSize.height()+line->sizeHint().height();
446 return TQSize(cx, cy);
447}
448
449void
451{
452 TQWidget *buttons[]= {
453 // yearBackward,
454 // monthBackward,
457 // monthForward,
458 // yearForward
459 };
460 const int NoOfButtons=sizeof(buttons)/sizeof(buttons[0]);
461 int count;
462 TQFont font;
463 TQRect r;
464 // -----
465 fontsize=s;
466 for(count=0; count<NoOfButtons; ++count)
467 {
468 font=buttons[count]->font();
469 font.setPointSize(s);
470 buttons[count]->setFont(font);
471 }
472 TQFontMetrics metrics(selectMonth->fontMetrics());
473 for(int i=1; i <= 12; ++i)
474 { // maxMonthRect is used by sizeHint()
475 r=metrics.boundingRect(TDEGlobal::locale()->calendar()->monthName(i, false));
476 maxMonthRect.setWidth(TQMAX(r.width(), maxMonthRect.width()));
477 maxMonthRect.setHeight(TQMAX(r.height(), maxMonthRect.height()));
478 }
479 table->setFontSize(s);
480}
481
482void
484{
485 if ( enable == (d->closeButton != 0L) )
486 return;
487
488 if ( enable ) {
489 d->closeButton = new TQToolButton( this );
490 TQToolTip::add(d->closeButton, i18n("Close"));
491 d->closeButton->setPixmap( SmallIcon("remove") );
492 connect( d->closeButton, TQ_SIGNAL( clicked() ),
493 topLevelWidget(), TQ_SLOT( close() ) );
494 }
495 else {
496 delete d->closeButton;
497 d->closeButton = 0L;
498 }
499
500 updateGeometry();
501}
502
504{
505 return (d->closeButton != 0L);
506}
507
508int CalendarPanel::weekOfYear(TQDate date)
509{
510 // Calculate ISO 8601 week number (taken from glibc/Gnumeric)
511 int year, week, wday, jan1wday, nextjan1wday;
512 TQDate jan1date, nextjan1date;
513
514 year=date.year();
515 wday=date.dayOfWeek();
516
517 jan1date=TQDate(year,1,1);
518 jan1wday=jan1date.dayOfWeek();
519
520 week = (date.dayOfYear()-1 + jan1wday-1)/7 + ((jan1wday-1) == 0 ? 1 : 0);
521
522 /* Does date belong to last week of previous year? */
523 if ((week == 0) && (jan1wday > 4 /*THURSDAY*/)) {
524 TQDate tmpdate=TQDate(year-1,12,31);
525 return weekOfYear(tmpdate);
526 }
527
528 if ((jan1wday <= 4 /*THURSDAY*/) && (jan1wday > 1 /*MONDAY*/))
529 week++;
530
531 if (week == 53) {
532 nextjan1date=TQDate(year+1, 1, 1);
533 nextjan1wday = nextjan1date.dayOfWeek();
534 if (nextjan1wday <= 4 /*THURSDAY*/)
535 week = 1;
536 }
537
538 return week;
539}
540
541void CalendarPanel::slotWeekdaySelected(int day) {
542 //kdDebug()<<k_funcinfo<<endl;
543 emit weekdaySelected(day);
544}
545
546void CalendarPanel::slotWeekSelected(int week, int year) {
547 //kdDebug()<<k_funcinfo<<endl;
548 emit weekSelected(week, year);
549}
550
551void CalendarPanel::setCalendar(Calendar *cal) {
552 //kdDebug()<<k_funcinfo<<endl;
553 table->clear();
554 if (cal) {
555 table->setMarkedWeekdays(cal->weekdaysMap());
556 TQPtrListIterator<CalendarDay> it = cal->days();
557 //kdDebug()<<k_funcinfo<<"Days="<<it.count()<<endl;
558 for (; it.current(); ++it) {
559 if (it.current()->state() != Map::None) {
560 table->addMarkedDate(it.current()->date(), it.current()->state());
561 //kdDebug()<<k_funcinfo<<"Added day: "<<it.current()->date().toString()<<"="<<it.current()->state()<<endl;
562 }
563 }
564 setEnabled(true);
565 table->setFocus();
566 }
567}
568
569DateMap CalendarPanel::selectedDates() {
570 return table->selectedDates();
571}
572
573IntMap CalendarPanel::selectedWeekdays() {
574 return table->selectedWeekdays();
575}
576
577DateMap CalendarPanel::markedDates() {
578 return table->markedDates();
579}
580
581IntMap CalendarPanel::markedWeekdays() {
582 return table->markedWeekdays();
583}
584
585void CalendarPanel::clear() {
586 table->clear();
587 setEnabled(false);
588}
589
590void CalendarPanel::markSelected(int state) {
591 table->markSelected(state);
592 }
593
594void CalendarPanel::slotSelectionCleared() {
595 emit selectionCleared();
596 }
597
598void CalendarPanel::virtual_hook( int /*id*/, void* /*data*/ )
599{ /*BASE::virtual_hook( id, data );*/ }
600
601} //KPlato namespace
602
603#include "kptcalendarpanel.moc"
TQSize sizeHint() const
The size hint for date pickers.
void setCloseButton(bool enable)
By calling this method with enable = true, CalendarPanel will show a little close-button in the upper...
void setFontSize(int)
Sets the font size of the widgets elements.
CalendarPanel(TQWidget *parent=0, TQDate=TQDate::currentDate(), const char *name=0, WFlags f=0)
The usual constructor.
const TQDate & date() const
virtual void resizeEvent(TQResizeEvent *)
the resize event
virtual ~CalendarPanel()
The destructor.
TQToolButton * monthForward
the month forward button
TQLineEdit * line
the line edit to enter the date directly
void dateSelected(TQDate)
This signal is emitted each time a day has been selected by clicking on the table (hitting a day in t...
TQToolButton * selectMonth
the button for selecting the month directly
void dateEntered(TQDate)
This signal is emitted when enter is pressed and a VALID date has been entered before into the line e...
TQToolButton * yearBackward
the year backward button
TQToolButton * selectYear
the button for selecting the year directly
bool setDate(const TQDate &)
Sets the date.
TQToolButton * yearForward
the year forward button
const TQDate & getDate() const
Returns the selected date.
void setEnabled(bool)
Enables or disables the widget.
virtual bool eventFilter(TQObject *o, TQEvent *e)
to catch move keyEvents when TQLineEdit has keyFocus
void dateChanged(TQDate)
This signal is emitted each time the selected date is changed.
TQToolButton * monthBackward
the month backward button
DateTable * table
the date table
void tableClicked()
This signal is emitted when the day has been selected by clicking on it in the table.
DateValidator * val
the validator for the line edit:
void selectionCleared()
All selections have been cleared.
Calendar defines the working and nonworking days and hours.
Validates user-entered dates.