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 
37 class 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 
67 KDateEdit::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 
104 KDateEdit::~KDateEdit()
105 {
106  delete mPopup;
107  mPopup = 0;
108 }
109 
110 void KDateEdit::setDate( const TQDate& date )
111 {
112  assignDate( date );
113  updateView();
114 }
115 
116 TQDate KDateEdit::date() const
117 {
118  return mDate;
119 }
120 
121 void KDateEdit::setReadOnly( bool readOnly )
122 {
123  mReadOnly = readOnly;
124  lineEdit()->setReadOnly( readOnly );
125 }
126 
128 {
129  return mReadOnly;
130 }
131 
132 void 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 
179 void 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 
192 void 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 
207 TQDate 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 
247 bool 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 
308 void 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 
318 void 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 
343 bool KDateEdit::assignDate( const TQDate& date )
344 {
345  mDate = date;
346  mTextChanged = false;
347  return true;
348 }
349 
350 void 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.