-rw-r--r-- | libkdepim/kdateedit.cpp | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/libkdepim/kdateedit.cpp b/libkdepim/kdateedit.cpp new file mode 100644 index 0000000..60bd2cf --- a/dev/null +++ b/libkdepim/kdateedit.cpp | |||
@@ -0,0 +1,486 @@ | |||
1 | /* | ||
2 | This file is part of libkdepim. | ||
3 | |||
4 | Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org> | ||
5 | |||
6 | This program is free software; you can redistribute it and/or modify | ||
7 | it under the terms of the GNU General Public License as published by | ||
8 | the Free Software Foundation; either version 2 of the License, or | ||
9 | (at your option) any later version. | ||
10 | |||
11 | This program 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 | ||
14 | GNU General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU General Public License | ||
17 | along with this program; if not, write to the Free Software | ||
18 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | |||
20 | As a special exception, permission is given to link this program | ||
21 | with any edition of Qt, and distribute the resulting executable, | ||
22 | without including the source code for Qt in the source distribution. | ||
23 | */ | ||
24 | |||
25 | #include <qapplication.h> | ||
26 | #include <qevent.h> | ||
27 | #include <qlineedit.h> | ||
28 | #include <qpixmap.h> | ||
29 | #include <qpushbutton.h> | ||
30 | |||
31 | #include <kdatepicker.h> | ||
32 | #include <kdebug.h> | ||
33 | #include <kglobal.h> | ||
34 | #include <kiconloader.h> | ||
35 | #include <klocale.h> | ||
36 | #include <kmessagebox.h> | ||
37 | #include <knotifyclient.h> | ||
38 | #include <qpalette.h> | ||
39 | |||
40 | #include "kdateedit.h" | ||
41 | #include "kdateedit.moc" | ||
42 | |||
43 | KDateEdit::KDateEdit(QWidget *parent, const char *name, bool withoutDP ) | ||
44 | : QHBox(parent, name) | ||
45 | { | ||
46 | dateFormShort = true; | ||
47 | withoutDp = withoutDP; | ||
48 | mDateEdit = new QLineEdit(this); | ||
49 | mDateEdit->setText(KGlobal::locale()->formatDate(QDate::currentDate(),dateFormShort)); | ||
50 | setFocusProxy(mDateEdit); | ||
51 | mDateEdit->installEventFilter(this); | ||
52 | |||
53 | // Highlight Background and Textcolor | ||
54 | QPalette palette = QWidget::palette(); | ||
55 | unsigned char red, green, blue; | ||
56 | red = palette.color( QPalette::Normal , QColorGroup::Background ).red() - 10; | ||
57 | green = palette.color( QPalette::Normal , QColorGroup::Background ).green() - 10; | ||
58 | blue = palette.color( QPalette::Normal , QColorGroup::Background ).blue() - 10; | ||
59 | palette.setColor( QColorGroup::Highlight, QColor(red,green,blue) ); | ||
60 | palette.setColor( QColorGroup::HighlightedText, palette.color( QPalette::Normal , QColorGroup::Foreground ) ); | ||
61 | mDateEdit->setPalette( palette ); | ||
62 | |||
63 | if ( withoutDP ) { | ||
64 | mDateFrame = 0; | ||
65 | mDateButton = 0; | ||
66 | mDatePicker = 0; | ||
67 | } else { | ||
68 | QPixmap pixmap = SmallIcon("smallcal"); | ||
69 | mDateButton = new QPushButton(this); | ||
70 | mDateButton->setPixmap(pixmap); | ||
71 | |||
72 | mDateFrame = new QVBox(0,0,WType_Popup); | ||
73 | // mDateFrame->setFrameStyle(QFrame::PopupPanel | QFrame::Raised); | ||
74 | mDateFrame->setFrameStyle( QFrame::WinPanel |QFrame::Raised ); | ||
75 | mDateFrame->setLineWidth(3); | ||
76 | mDateFrame->hide(); | ||
77 | |||
78 | mDatePicker = new KDatePicker(mDateFrame,QDate::currentDate()); | ||
79 | connect(mDatePicker,SIGNAL(dateEntered(QDate)),SLOT(setDate(QDate))); | ||
80 | connect(mDatePicker,SIGNAL(dateEntered(QDate)),SIGNAL(dateChanged(QDate))); | ||
81 | connect(mDatePicker,SIGNAL(dateSelected(QDate)),SLOT(setDate(QDate))); | ||
82 | connect(mDatePicker,SIGNAL(dateSelected(QDate)),SIGNAL(dateChanged(QDate))); | ||
83 | connect(mDatePicker,SIGNAL(dateSelected(QDate)),mDateFrame,SLOT(hide())); | ||
84 | connect(mDateButton,SIGNAL(clicked()),SLOT(toggleDatePicker())); | ||
85 | |||
86 | //mDateFrame->resize( 400, 300 ); | ||
87 | |||
88 | } | ||
89 | connect(mDateEdit,SIGNAL(returnPressed()),SLOT(lineEnterPressed())); | ||
90 | connect(mDateEdit,SIGNAL(textChanged(const QString &)), | ||
91 | SLOT(textChanged(const QString &))); | ||
92 | |||
93 | // Create the keyword list. This will be used to match against when the user | ||
94 | // enters information. | ||
95 | mKeywordMap[i18n("tomorrow")] = 1; | ||
96 | mKeywordMap[i18n("today")] = 0; | ||
97 | mKeywordMap[i18n("yesterday")] = -1; | ||
98 | |||
99 | /* | ||
100 | * This loop uses some math tricks to figure out the offset in days | ||
101 | * to the next date the given day of the week occurs. There | ||
102 | * are two cases, that the new day is >= the current day, which means | ||
103 | * the new day has not occured yet or that the new day < the current day, | ||
104 | * which means the new day is already passed (so we need to find the | ||
105 | * day in the next week). | ||
106 | */ | ||
107 | QString dayName; | ||
108 | int currentDay = QDate::currentDate().dayOfWeek(); | ||
109 | for (int i = 1; i <= 7; ++i) | ||
110 | { | ||
111 | dayName = KGlobal::locale()->weekDayName(i).lower(); | ||
112 | if (i >= currentDay) | ||
113 | mKeywordMap[dayName] = i - currentDay; | ||
114 | else | ||
115 | mKeywordMap[dayName] = 7 - currentDay + i; | ||
116 | } | ||
117 | |||
118 | mTextChanged = false; | ||
119 | mHandleInvalid = false; | ||
120 | QWidget::setTabOrder( mDateEdit, mDateButton ); | ||
121 | } | ||
122 | |||
123 | KDateEdit::~KDateEdit() | ||
124 | { | ||
125 | delete mDateFrame; | ||
126 | } | ||
127 | |||
128 | void KDateEdit::setDate(QDate newDate) | ||
129 | { | ||
130 | if (!newDate.isValid() && !mHandleInvalid) | ||
131 | return; | ||
132 | if ( readDate() == newDate ) | ||
133 | return; | ||
134 | QString dateString = ""; | ||
135 | if(newDate.isValid()) | ||
136 | dateString = KGlobal::locale()->formatDate( newDate, dateFormShort ); | ||
137 | |||
138 | mTextChanged = false; | ||
139 | |||
140 | // We do not want to generate a signal here, since we explicity setting | ||
141 | // the date | ||
142 | bool b = mDateEdit->signalsBlocked(); | ||
143 | mDateEdit->blockSignals(true); | ||
144 | mDateEdit->setText(dateString); | ||
145 | mDateEdit->blockSignals(b); | ||
146 | } | ||
147 | |||
148 | void KDateEdit::setDate( QDate date,int *cpos,const int key ,const bool dateFormShort) | ||
149 | { | ||
150 | QString dateForm = dateFormShort ? | ||
151 | KGlobal::locale()->dateFormatShort() : | ||
152 | KGlobal::locale()->dateFormat(); | ||
153 | |||
154 | int begin = dateForm.find("%"); | ||
155 | int space = 0; | ||
156 | int allStrLength = 0; | ||
157 | int strLength = 0; | ||
158 | int repeat = 0; | ||
159 | |||
160 | // witch? Day, Month or Year switch? | ||
161 | while(1){ | ||
162 | switch ( dateForm.at(begin + 1).latin1() ) | ||
163 | { | ||
164 | case 'd':// 16 (month day) | ||
165 | strLength = 2; //Ok | ||
166 | break; | ||
167 | case 'm':// 01 (month) | ||
168 | strLength = 2; //Ok | ||
169 | break; | ||
170 | case 'a':// Mon (Weekday) | ||
171 | strLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), true).length(); | ||
172 | break; | ||
173 | case 'A':// Monday (Weekday) | ||
174 | strLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), false).length(); | ||
175 | break; | ||
176 | case 'b':// Jan (monthName) | ||
177 | strLength = KGlobal::locale()->monthName(date.month(), true).length(); | ||
178 | break; | ||
179 | case 'B':// January (monthName) | ||
180 | strLength = KGlobal::locale()->monthName(date.month(), false).length(); | ||
181 | break; | ||
182 | case 'y':// 04 (year short) | ||
183 | strLength = 2; //Ok | ||
184 | break; | ||
185 | case 'Y':// 2004 (year) | ||
186 | strLength = 4; //Ok | ||
187 | break; | ||
188 | default: | ||
189 | break; | ||
190 | } | ||
191 | space = begin - (repeat++ * 2); | ||
192 | // all select? then dayswitch | ||
193 | if( (mDateEdit->text().length() == mDateEdit->markedText().length() ) && | ||
194 | ( (dateForm.at(begin + 1).latin1() == 'd') || | ||
195 | (dateForm.at(begin + 1).latin1() == 'a') || | ||
196 | (dateForm.at(begin + 1).latin1() == 'A') ) ) { | ||
197 | break; | ||
198 | } | ||
199 | // mDateEdit-StringPos == CursorPosition(cpos) then break and set date | ||
200 | if( ( (space + allStrLength) <= *cpos && *cpos <= (space + allStrLength + strLength) ) || *cpos < begin ) { | ||
201 | break; | ||
202 | } | ||
203 | allStrLength += strLength; | ||
204 | begin = dateForm.find("%", begin +1); | ||
205 | } | ||
206 | |||
207 | // set date | ||
208 | switch ( dateForm.at(begin + 1).latin1() ) { | ||
209 | case 'd': | ||
210 | case 'a': | ||
211 | case 'A': | ||
212 | if(key == Key_Up) { | ||
213 | setDate( date.addDays( 1 ) ); | ||
214 | } | ||
215 | else if(key == Key_Down) { | ||
216 | setDate( date.addDays( -1 ) ); | ||
217 | } | ||
218 | maxDay = readDate().day(); | ||
219 | break; | ||
220 | case 'm': | ||
221 | case 'b': | ||
222 | case 'B': | ||
223 | if(key == Key_Up) { | ||
224 | int year = ((date.month()+1)>12)?date.year()+1:date.year(); | ||
225 | int month = ((date.month()+1)>12)?1:date.month()+1; | ||
226 | int day = (QDate(year,month,1).daysInMonth()<maxDay)?QDate(year,month,1).daysInMonth():maxDay; | ||
227 | setDate( QDate( year, month, day ) ); | ||
228 | } else if(key == Key_Down) { | ||
229 | int year = ((date.month()-1)<1)?date.year()-1:date.year(); | ||
230 | int month = ((date.month()-1)<1)?12:date.month()-1; | ||
231 | int day = (QDate(year,month,1).daysInMonth()<maxDay)?QDate(year,month,1).daysInMonth():maxDay; | ||
232 | setDate( QDate( year, month, day ) ); | ||
233 | } | ||
234 | break; | ||
235 | case 'y': | ||
236 | case 'Y': | ||
237 | if(key == Key_Up) { | ||
238 | setDate( QDate( date.year() + 1, date.month() , date.day()) ); | ||
239 | } | ||
240 | else if(key == Key_Down) { | ||
241 | setDate( QDate( date.year() - 1, date.month() , date.day()) ); | ||
242 | } | ||
243 | break; | ||
244 | /* default: | ||
245 | if(key == Key_Up) { | ||
246 | setDate( date.addDays( 1 ) ); | ||
247 | } else if(key == Key_Down) { | ||
248 | setDate( date.addDays( -1 ) ); | ||
249 | } | ||
250 | break;*/ | ||
251 | } | ||
252 | |||
253 | date = readDate(); | ||
254 | begin = dateForm.find("%"); | ||
255 | int allSelectStrLength = 0; | ||
256 | int selectStrLength = 0; | ||
257 | |||
258 | // set selection do new date an set cursor at end of selection | ||
259 | for(int i = 0; i < repeat; i++){ | ||
260 | switch ( dateForm.at(begin + 1).latin1() ) | ||
261 | { | ||
262 | case 'd':// 16 (month day) | ||
263 | selectStrLength = 2; //Ok | ||
264 | break; | ||
265 | case 'm':// 01 (month) | ||
266 | selectStrLength = 2; //Ok | ||
267 | break; | ||
268 | case 'a':// Mon (Weekday short) | ||
269 | selectStrLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), true).length(); | ||
270 | break; | ||
271 | case 'A':// Monday (Weekday) | ||
272 | selectStrLength = KGlobal::locale()->weekDayName(date.dayOfWeek(), false).length(); | ||
273 | break; | ||
274 | case 'b':// Jan (monthName short) | ||
275 | selectStrLength = KGlobal::locale()->monthName(date.month(), true).length(); | ||
276 | break; | ||
277 | case 'B':// January (monthName) | ||
278 | selectStrLength = KGlobal::locale()->monthName(date.month(), false).length(); | ||
279 | break; | ||
280 | case 'y':// 04 (year short) | ||
281 | selectStrLength = 2; //Ok | ||
282 | break; | ||
283 | case 'Y':// 2004 (year) | ||
284 | selectStrLength = 4; //Ok | ||
285 | break; | ||
286 | default: | ||
287 | break; | ||
288 | } | ||
289 | space = begin - (i * 2); | ||
290 | allSelectStrLength += selectStrLength; | ||
291 | begin = dateForm.find("%", begin +1); | ||
292 | } | ||
293 | // set selection from begin of date | ||
294 | setSelect( space + allSelectStrLength - selectStrLength , selectStrLength); | ||
295 | *cpos = space + allSelectStrLength; | ||
296 | emit(dateChanged(date)); | ||
297 | |||
298 | return; | ||
299 | } | ||
300 | |||
301 | void KDateEdit::setHandleInvalid(bool handleInvalid) | ||
302 | { | ||
303 | mHandleInvalid = handleInvalid; | ||
304 | } | ||
305 | |||
306 | void KDateEdit::setEnabled(bool on) | ||
307 | { | ||
308 | mDateEdit->setEnabled(on); | ||
309 | mDateButton->setEnabled(on); | ||
310 | } | ||
311 | |||
312 | QDate KDateEdit::date() const | ||
313 | { | ||
314 | QDate date = readDate(); | ||
315 | |||
316 | if (date.isValid() || mHandleInvalid) { | ||
317 | return date; | ||
318 | } else { | ||
319 | KNotifyClient::beep(); | ||
320 | return QDate::currentDate(); | ||
321 | } | ||
322 | } | ||
323 | |||
324 | void KDateEdit::keyPressEvent(QKeyEvent *e) | ||
325 | { | ||
326 | QDate date = readDate(); | ||
327 | int cpos = mDateEdit->cursorPosition(); | ||
328 | |||
329 | switch(e->key()) | ||
330 | { | ||
331 | case Key_Escape: | ||
332 | mDateEdit->deselect(); | ||
333 | case Key_Tab: | ||
334 | QHBox::keyPressEvent(e); | ||
335 | break; | ||
336 | case Key_Up: | ||
337 | // when date invalid then set to currend and return | ||
338 | if(!date.isValid()) { | ||
339 | date = QDate::currentDate(); | ||
340 | setDate(date); | ||
341 | mDateEdit->setCursorPosition(cpos); | ||
342 | emit(dateChanged(date)); | ||
343 | QString text = i18n( "You entered an invalid date!\n Date changed to current date." ); | ||
344 | KMessageBox::information( 0, text ); | ||
345 | return; | ||
346 | } | ||
347 | setDate(date, &cpos, Key_Up, dateFormShort); | ||
348 | break; | ||
349 | case Key_Down: | ||
350 | // when date invalid then set to current and return | ||
351 | if(!date.isValid()) { | ||
352 | date = QDate::currentDate(); | ||
353 | setDate(date); | ||
354 | mDateEdit->setCursorPosition(cpos); | ||
355 | emit(dateChanged(date)); | ||
356 | QString text = i18n( "You entered an invalid date!\n Date changed to current date." ); | ||
357 | KMessageBox::information( 0, text ); | ||
358 | return; | ||
359 | } | ||
360 | setDate(date, &cpos, Key_Down, dateFormShort); | ||
361 | break; | ||
362 | default: | ||
363 | QHBox::keyPressEvent(e); | ||
364 | break; | ||
365 | } // switch | ||
366 | mDateEdit->setCursorPosition(cpos); | ||
367 | } | ||
368 | |||
369 | void KDateEdit::setSelect( int from, int to ) | ||
370 | { | ||
371 | // return; | ||
372 | mDateEdit->setSelection( from , to ); | ||
373 | } | ||
374 | |||
375 | void KDateEdit::toggleDatePicker() | ||
376 | { | ||
377 | if( mDateFrame->isVisible() ) { | ||
378 | mDateFrame->hide(); | ||
379 | } else { | ||
380 | QPoint tmpPoint = mapToGlobal(mDateButton->geometry().bottomRight()); | ||
381 | QSize datepickersize = mDatePicker->sizeHint(); | ||
382 | |||
383 | if ( tmpPoint.x() < 7+datepickersize.width() ) tmpPoint.setX( 7+datepickersize.width() ); | ||
384 | |||
385 | int h = QApplication::desktop()->height(); | ||
386 | |||
387 | if ( tmpPoint.y() + datepickersize.height() > h ) tmpPoint.setY( h - datepickersize.height() ); | ||
388 | |||
389 | mDateFrame->setGeometry(tmpPoint.x()-datepickersize.width()-7, tmpPoint.y(), | ||
390 | datepickersize.width()+2*mDateFrame->lineWidth(), datepickersize.height()+2*mDateFrame->lineWidth()); | ||
391 | |||
392 | QDate date = readDate(); | ||
393 | if(date.isValid()) { | ||
394 | mDatePicker->setDate(date); | ||
395 | } else { | ||
396 | mDatePicker->setDate(QDate::currentDate()); | ||
397 | } | ||
398 | mDateFrame->show(); | ||
399 | } | ||
400 | } | ||
401 | |||
402 | |||
403 | void KDateEdit::lineEnterPressed() | ||
404 | { | ||
405 | QDate date = readDate(); | ||
406 | |||
407 | if(date.isValid()) | ||
408 | { | ||
409 | // Update the edit. This is needed if the user has entered a | ||
410 | // word rather than the actual date. | ||
411 | setDate(date); | ||
412 | emit(dateChanged(date)); | ||
413 | emit returnPressed(); | ||
414 | } | ||
415 | else | ||
416 | { | ||
417 | if ( withoutDp ) { | ||
418 | KNotifyClient::beep(); | ||
419 | } else { | ||
420 | if ( !mDateEdit->text().isEmpty() ) { | ||
421 | mTextChanged = false; | ||
422 | QString text = i18n( "You entered an invalid date!\n Will use current date instead." ); | ||
423 | if ( KMessageBox::warningContinueCancel( 0, text ) == KMessageBox::Continue ) { | ||
424 | setDate( QDate::currentDate() ); | ||
425 | emit dateChanged( QDate::currentDate() ); | ||
426 | } | ||
427 | } | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | |||
432 | bool KDateEdit::inputIsValid() | ||
433 | { | ||
434 | return readDate().isValid(); | ||
435 | } | ||
436 | |||
437 | QDate KDateEdit::readDate() const | ||
438 | { | ||
439 | QString text = mDateEdit->text(); | ||
440 | QDate date; | ||
441 | |||
442 | if (mKeywordMap.contains(text.lower())) | ||
443 | { | ||
444 | date = QDate::currentDate().addDays(mKeywordMap[text.lower()]); | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | date = KGlobal::locale()->readDate(text); | ||
449 | } | ||
450 | |||
451 | return date; | ||
452 | } | ||
453 | |||
454 | bool KDateEdit::eventFilter(QObject *, QEvent *e) | ||
455 | { | ||
456 | // We only process the focus out event if the text has changed | ||
457 | // since we got focus | ||
458 | if ((e->type() == QEvent::FocusOut) && mTextChanged) | ||
459 | { | ||
460 | lineEnterPressed(); | ||
461 | mTextChanged = false; | ||
462 | } | ||
463 | // switch dateFormShort by double klick with mouse | ||
464 | else if (e->type() == QEvent::MouseButtonDblClick) | ||
465 | { | ||
466 | dateFormShort = dateFormShort?false:true; | ||
467 | mDateEdit->setText(KGlobal::locale()->formatDate(readDate(),dateFormShort)); | ||
468 | } | ||
469 | else if (e->type() == QEvent::FocusIn) | ||
470 | { | ||
471 | maxDay = readDate().day(); | ||
472 | } | ||
473 | |||
474 | return false; | ||
475 | } | ||
476 | |||
477 | void KDateEdit::textChanged(const QString &) | ||
478 | { | ||
479 | if(mHandleInvalid && mDateEdit->text().stripWhiteSpace().isEmpty()) { | ||
480 | QDate date; //invalid date | ||
481 | emit(dateChanged(date)); | ||
482 | } else { | ||
483 | mTextChanged = true; | ||
484 | } | ||
485 | maxDay = readDate().day(); | ||
486 | } | ||