libtdepim

kdateedit.cpp
1/*
2 This file is part of libtdepim.
3
4 Copyright (c) 2002 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2002 David Jarvie <software@astrojar.org.uk>
6 Copyright (c) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
7 Copyright (c) 2004 Tobias Koenig <tokoe@kde.org>
8
9 This library is free software; you can redistribute it and/or
10 modify it under the terms of the GNU Library General Public
11 License as published by the Free Software Foundation; either
12 version 2 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Library General Public License for more details.
18
19 You should have received a copy of the GNU Library General Public License
20 along with this library; see the file COPYING.LIB. If not, write to
21 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 Boston, MA 02110-1301, USA.
23*/
24
25#include <tqapplication.h>
26#include <tqlineedit.h>
27#include <tqlistbox.h>
28#include <tqvalidator.h>
29
30#include <kcalendarsystem.h>
31#include <tdeglobal.h>
32#include <tdeglobalsettings.h>
33#include <tdelocale.h>
34
35#include "kdateedit.h"
36
37class DateValidator : public TQValidator
38{
39 public:
40 DateValidator( const TQStringList &keywords, TQWidget* parent, const char* name = 0 )
41 : TQValidator( parent, name ), mKeywords( keywords )
42 {}
43
44 virtual State validate( TQString &str, int& ) const
45 {
46 int length = str.length();
47
48 // empty string is intermediate so one can clear the edit line and start from scratch
49 if ( length <= 0 )
50 return Intermediate;
51
52 if ( mKeywords.contains( str.lower() ) )
53 return Acceptable;
54
55 bool ok = false;
56 TDEGlobal::locale()->readDate( str, &ok );
57 if ( ok )
58 return Acceptable;
59 else
60 return Intermediate;
61 }
62
63 private:
64 TQStringList mKeywords;
65};
66
67KDateEdit::KDateEdit( TQWidget *parent, const char *name )
68 : TQComboBox( true, parent, name ),
69 mReadOnly( false ),
70 mDiscardNextMousePress( false )
71{
72 // need at least one entry for popup to work
73 setMaxCount( 1 );
74
75 mDate = TQDate::currentDate();
76 TQString today = TDEGlobal::locale()->formatDate( mDate, true );
77
78 insertItem( today );
79 setCurrentItem( 0 );
80 changeItem( today, 0 );
81 setMinimumSize( sizeHint() );
82
83 connect( lineEdit(), TQ_SIGNAL( returnPressed() ),
84 this, TQ_SLOT( lineEnterPressed() ) );
85 connect( this, TQ_SIGNAL( textChanged( const TQString& ) ),
86 TQ_SLOT( slotTextChanged( const TQString& ) ) );
87
88 mPopup = new KDatePickerPopup( KDatePickerPopup::DatePicker | KDatePickerPopup::Words );
89 mPopup->hide();
90 mPopup->installEventFilter( this );
91
92 connect( mPopup, TQ_SIGNAL( dateChanged( TQDate ) ),
93 TQ_SLOT( dateSelected( TQDate ) ) );
94
95 // handle keyword entry
96 setupKeywords();
97 lineEdit()->installEventFilter( this );
98
99 setValidator( new DateValidator( mKeywordMap.keys(), this ) );
100
101 mTextChanged = false;
102}
103
104KDateEdit::~KDateEdit()
105{
106 delete mPopup;
107 mPopup = 0;
108}
109
110void KDateEdit::setDate( const TQDate& date )
111{
112 assignDate( date );
113 updateView();
114}
115
116TQDate KDateEdit::date() const
117{
118 return mDate;
119}
120
121void KDateEdit::setReadOnly( bool readOnly )
122{
123 mReadOnly = readOnly;
124 lineEdit()->setReadOnly( readOnly );
125}
126
128{
129 return mReadOnly;
130}
131
132void KDateEdit::popup()
133{
134 if ( mReadOnly )
135 return;
136
137 TQRect desk = TDEGlobalSettings::desktopGeometry( this );
138
139 TQPoint popupPoint = mapToGlobal( TQPoint( 0,0 ) );
140
141 int dateFrameHeight = mPopup->sizeHint().height();
142 if ( popupPoint.y() + height() + dateFrameHeight > desk.bottom() )
143 popupPoint.setY( popupPoint.y() - dateFrameHeight );
144 else
145 popupPoint.setY( popupPoint.y() + height() );
146
147 int dateFrameWidth = mPopup->sizeHint().width();
148 if ( popupPoint.x() + dateFrameWidth > desk.right() )
149 popupPoint.setX( desk.right() - dateFrameWidth );
150
151 if ( popupPoint.x() < desk.left() )
152 popupPoint.setX( desk.left() );
153
154 if ( popupPoint.y() < desk.top() )
155 popupPoint.setY( desk.top() );
156
157 if ( mDate.isValid() )
158 mPopup->setDate( mDate );
159 else
160 mPopup->setDate( TQDate::currentDate() );
161
162 mPopup->popup( popupPoint );
163
164 // The combo box is now shown pressed. Make it show not pressed again
165 // by causing its (invisible) list box to emit a 'selected' signal.
166 // First, ensure that the list box contains the date currently displayed.
167 TQDate date = parseDate();
168 assignDate( date );
169 updateView();
170 // Now, simulate an Enter to unpress it
171 TQListBox *lb = listBox();
172 if (lb) {
173 lb->setCurrentItem(0);
174 TQKeyEvent* keyEvent = new TQKeyEvent(TQEvent::KeyPress, TQt::Key_Enter, 0, 0);
175 TQApplication::postEvent(lb, keyEvent);
176 }
177}
178
179void KDateEdit::dateSelected( TQDate date )
180{
181 if (assignDate( date ) ) {
182 updateView();
183 emit dateChanged( date );
184 emit dateEntered( date );
185
186 if ( date.isValid() ) {
187 mPopup->hide();
188 }
189 }
190}
191
192void KDateEdit::lineEnterPressed()
193{
194 bool replaced = false;
195
196 TQDate date = parseDate( &replaced );
197
198 if (assignDate( date ) ) {
199 if ( replaced )
200 updateView();
201
202 emit dateChanged( date );
203 emit dateEntered( date );
204 }
205}
206
207TQDate KDateEdit::parseDate( bool *replaced ) const
208{
209 TQString text = currentText();
210 TQDate result;
211
212 if ( replaced )
213 (*replaced) = false;
214
215 if ( text.isEmpty() )
216 result = TQDate();
217 else if ( mKeywordMap.contains( text.lower() ) ) {
218 TQDate today = TQDate::currentDate();
219 int i = mKeywordMap[ text.lower() ];
220 if ( i >= 100 ) {
221 /* A day name has been entered. Convert to offset from today.
222 * This uses some math tricks to figure out the offset in days
223 * to the next date the given day of the week occurs. There
224 * are two cases, that the new day is >= the current day, which means
225 * the new day has not occurred yet or that the new day < the current day,
226 * which means the new day is already passed (so we need to find the
227 * day in the next week).
228 */
229 i -= 100;
230 int currentDay = today.dayOfWeek();
231 if ( i >= currentDay )
232 i -= currentDay;
233 else
234 i += 7 - currentDay;
235 }
236
237 result = today.addDays( i );
238 if ( replaced )
239 (*replaced) = true;
240 } else {
241 result = TDEGlobal::locale()->readDate( text );
242 }
243
244 return result;
245}
246
247bool KDateEdit::eventFilter( TQObject *object, TQEvent *event )
248{
249 if ( object == lineEdit() ) {
250 // We only process the focus out event if the text has changed
251 // since we got focus
252 if ( (event->type() == TQEvent::FocusOut) && mTextChanged ) {
253 lineEnterPressed();
254 mTextChanged = false;
255 } else if ( event->type() == TQEvent::KeyPress ) {
256 // Up and down arrow keys step the date
257 TQKeyEvent* keyEvent = (TQKeyEvent*)event;
258
259 if ( keyEvent->key() == TQt::Key_Return ) {
260 lineEnterPressed();
261 return true;
262 }
263
264 int step = 0;
265 if ( keyEvent->key() == TQt::Key_Up )
266 step = 1;
267 else if ( keyEvent->key() == TQt::Key_Down )
268 step = -1;
269 // TODO: If it's not an input key, but something like Return, Enter, Tab, etc..., don't eat the keypress, but handle it through to the default eventfilter!
270 if ( step && !mReadOnly ) {
271 TQDate date = parseDate();
272 if ( date.isValid() ) {
273 date = date.addDays( step );
274 if ( assignDate( date ) ) {
275 updateView();
276 emit dateChanged( date );
277 emit dateEntered( date );
278 return true;
279 }
280 }
281 }
282 }
283 } else {
284 // It's a date picker event
285 switch ( event->type() ) {
286 case TQEvent::MouseButtonDblClick:
287 case TQEvent::MouseButtonPress: {
288 TQMouseEvent *mouseEvent = (TQMouseEvent*)event;
289 if ( !mPopup->rect().contains( mouseEvent->pos() ) ) {
290 TQPoint globalPos = mPopup->mapToGlobal( mouseEvent->pos() );
291 if ( TQApplication::widgetAt( globalPos, true ) == this ) {
292 // The date picker is being closed by a click on the
293 // KDateEdit widget. Avoid popping it up again immediately.
294 mDiscardNextMousePress = true;
295 }
296 }
297
298 break;
299 }
300 default:
301 break;
302 }
303 }
304
305 return false;
306}
307
308void KDateEdit::mousePressEvent( TQMouseEvent *event )
309{
310 if ( event->button() == TQt::LeftButton && mDiscardNextMousePress ) {
311 mDiscardNextMousePress = false;
312 return;
313 }
314
315 TQComboBox::mousePressEvent( event );
316}
317
318void KDateEdit::slotTextChanged( const TQString& )
319{
320 TQDate date = parseDate();
321
322 if ( assignDate( date ) )
323 emit dateChanged( date );
324
325 mTextChanged = true;
326}
327
329{
330 // Create the keyword list. This will be used to match against when the user
331 // enters information.
332 mKeywordMap.insert( i18n( "tomorrow" ), 1 );
333 mKeywordMap.insert( i18n( "today" ), 0 );
334 mKeywordMap.insert( i18n( "yesterday" ), -1 );
335
336 TQString dayName;
337 for ( int i = 1; i <= 7; ++i ) {
338 dayName = TDEGlobal::locale()->calendar()->weekDayName( i ).lower();
339 mKeywordMap.insert( dayName, i + 100 );
340 }
341}
342
343bool KDateEdit::assignDate( const TQDate& date )
344{
345 mDate = date;
346 mTextChanged = false;
347 return true;
348}
349
350void KDateEdit::updateView()
351{
352 TQString dateString;
353 if ( mDate.isValid() )
354 dateString = TDEGlobal::locale()->formatDate( mDate, true );
355
356 // We do not want to generate a signal here,
357 // since we explicitly setting the date
358 bool blocked = signalsBlocked();
359 blockSignals( true );
360 changeItem( dateString, 0 );
361 blockSignals( blocked );
362}
363
364#include "kdateedit.moc"
bool isReadOnly() const
Definition: kdateedit.cpp:127
virtual bool assignDate(const TQDate &date)
Sets the date, without altering the display.
Definition: kdateedit.cpp:343
void setupKeywords()
Fills the keyword map.
Definition: kdateedit.cpp:328
void setDate(const TQDate &date)
Sets the date.
Definition: kdateedit.cpp:110
TQDate date() const
Definition: kdateedit.cpp:116
void setReadOnly(bool readOnly)
Sets whether the widget is read-only for the user.
Definition: kdateedit.cpp:121
void dateEntered(const TQDate &date)
This signal is emitted whenever the user has entered a new date.
void dateChanged(const TQDate &date)
This signal is emitted whenever the user modifies the date.
This menu helps the user to select a date quickly.