]> git.saurik.com Git - wxWidgets.git/blob - src/generic/datectlg.cpp
invalidate the best size when adding or deleting items
[wxWidgets.git] / src / generic / datectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datectlg.cpp
3 // Purpose: generic wxDatePickerCtrlGeneric implementation
4 // Author: Andreas Pflug
5 // Modified by:
6 // Created: 2005-01-19
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #if wxUSE_DATEPICKCTRL
27
28 #include "wx/datectrl.h"
29
30 // use this version if we're explicitly requested to do it or if it's the only
31 // one we have
32 #if !defined(wxHAS_NATIVE_DATEPICKCTRL) || \
33 (defined(wxUSE_DATEPICKCTRL_GENERIC) && wxUSE_DATEPICKCTRL_GENERIC)
34
35 #ifndef WX_PRECOMP
36 #include "wx/dialog.h"
37 #include "wx/dcmemory.h"
38 #include "wx/panel.h"
39 #include "wx/textctrl.h"
40 #include "wx/valtext.h"
41 #endif
42
43 #ifdef wxHAS_NATIVE_DATEPICKCTRL
44 // this header is not included from wx/datectrl.h if we have a native
45 // version, but we do need it here
46 #include "wx/generic/datectrl.h"
47 #else
48 // we need to define _WX_DEFINE_DATE_EVENTS_ before including wx/dateevt.h to
49 // define the event types we use if we're the only date picker control version
50 // being compiled -- otherwise it's defined in the native version implementation
51 #define _WX_DEFINE_DATE_EVENTS_
52 #endif
53
54 #include "wx/dateevt.h"
55
56 #include "wx/calctrl.h"
57 #include "wx/combo.h"
58
59 // ----------------------------------------------------------------------------
60 // constants
61 // ----------------------------------------------------------------------------
62
63 #if defined(__WXMSW__)
64 #define CALBORDER 0
65 #else
66 #define CALBORDER 4
67 #endif
68
69 // ----------------------------------------------------------------------------
70 // global variables
71 // ----------------------------------------------------------------------------
72
73
74 // ----------------------------------------------------------------------------
75 // local classes
76 // ----------------------------------------------------------------------------
77
78 class wxCalendarComboPopup : public wxCalendarCtrl,
79 public wxComboPopup
80 {
81 public:
82
83 wxCalendarComboPopup() : wxCalendarCtrl(),
84 wxComboPopup()
85 {
86 }
87
88 virtual void Init()
89 {
90 }
91
92 // NB: Don't create lazily since it didn't work that way before
93 // wxComboCtrl was used, and changing behaviour would almost
94 // certainly introduce new bugs.
95 virtual bool Create(wxWindow* parent)
96 {
97 if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
98 wxPoint(0, 0), wxDefaultSize,
99 wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
100 return false;
101
102 wxWindow *yearControl = wxCalendarCtrl::GetYearControl();
103
104 wxClientDC dc(yearControl);
105 dc.SetFont(yearControl->GetFont());
106 wxCoord width, dummy;
107 dc.GetTextExtent(wxT("2000"), &width, &dummy);
108 width += ConvertDialogToPixels(wxSize(20, 0)).x;
109
110 wxSize calSize = wxCalendarCtrl::GetBestSize();
111 wxSize yearSize = yearControl->GetSize();
112 yearSize.x = width;
113
114 wxPoint yearPosition = yearControl->GetPosition();
115
116 SetFormat(wxT("%x"));
117
118 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
119 if (width < calSize.x-4)
120 width = calSize.x-4;
121
122 int calPos = (width-calSize.x)/2;
123 if (calPos == -1)
124 {
125 calPos = 0;
126 width += 2;
127 }
128 wxCalendarCtrl::SetSize(calPos, 0, calSize.x, calSize.y);
129 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
130 yearSize.x, yearSize.y);
131 wxCalendarCtrl::GetMonthControl()->Move(0, 0);
132
133 m_useSize.x = width+CALBORDER/2;
134 m_useSize.y = calSize.y-2+CALBORDER;
135
136 wxWindow* tx = m_combo->GetTextCtrl();
137 if ( !tx )
138 tx = m_combo;
139
140 tx->Connect(wxEVT_KILL_FOCUS,
141 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
142 NULL, this);
143
144 return true;
145 }
146
147 virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
148 int WXUNUSED(prefHeight),
149 int WXUNUSED(maxHeight))
150 {
151 return m_useSize;
152 }
153
154 virtual wxWindow *GetControl() { return this; }
155
156 void SetDateValue(const wxDateTime& date)
157 {
158 if ( date.IsValid() )
159 {
160 m_combo->SetText(date.Format(m_format));
161 }
162 else // invalid date
163 {
164 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
165 _T("this control must have a valid date") );
166
167 m_combo->SetText(wxEmptyString);
168 }
169
170 m_currentDate = date;
171 }
172
173 const wxDateTime& GetDateValue() const
174 {
175 return m_currentDate;
176 }
177
178 bool ParseDateTime(const wxString& s, wxDateTime* pDt)
179 {
180 wxASSERT(pDt);
181
182 if ( !s.empty() )
183 {
184 pDt->ParseFormat(s, m_format);
185 if ( !pDt->IsValid() )
186 return false;
187 }
188
189 return true;
190 }
191
192 void SendDateEvent(const wxDateTime& dt)
193 {
194 //
195 // Sends both wxCalendarEvent and wxDateEvent
196 wxWindow* datePicker = m_combo->GetParent();
197
198 wxCalendarEvent cev((wxCalendarCtrl*) this, wxEVT_CALENDAR_SEL_CHANGED);
199 cev.SetEventObject(datePicker);
200 cev.SetId(datePicker->GetId());
201 cev.SetDate(dt);
202 GetParent()->GetEventHandler()->ProcessEvent(cev);
203
204 wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
205 datePicker->GetParent()->GetEventHandler()->ProcessEvent(event);
206 }
207
208 private:
209
210 void OnCalKey(wxKeyEvent & ev)
211 {
212 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
213 Dismiss();
214 else
215 ev.Skip();
216 }
217
218 void OnSelChange(wxCalendarEvent &ev)
219 {
220 m_currentDate = wxCalendarCtrl::GetDate();
221 m_combo->SetText(m_currentDate.Format(m_format));
222
223 if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
224 {
225 Dismiss();
226 }
227
228 SendDateEvent(m_currentDate);
229 }
230
231 void OnKillTextFocus(wxFocusEvent &ev)
232 {
233 ev.Skip();
234
235 wxDateTime dt;
236 wxString value = m_combo->GetValue();
237 if ( !ParseDateTime(value, &dt) )
238 {
239 if ( !HasDPFlag(wxDP_ALLOWNONE) )
240 dt = m_currentDate;
241 }
242
243 if ( dt.IsValid() )
244 m_combo->SetText(dt.Format(m_format));
245 else
246 m_combo->SetText(wxEmptyString);
247
248 // notify that we had to change the date after validation
249 if ( (dt.IsValid() && (!m_currentDate.IsValid() || m_currentDate != dt)) ||
250 (!dt.IsValid() && m_currentDate.IsValid()) )
251 {
252 m_currentDate = dt;
253 SendDateEvent(dt);
254 }
255 }
256
257 bool HasDPFlag(int flag)
258 {
259 return m_combo->GetParent()->HasFlag(flag);
260 }
261
262 bool SetFormat(const wxChar *fmt)
263 {
264 m_format.clear();
265
266 wxDateTime dt;
267 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
268 wxString str(dt.Format(fmt));
269
270 const wxChar *p = str.c_str();
271 while ( *p )
272 {
273 int n=wxAtoi(p);
274 if (n == dt.GetDay())
275 {
276 m_format.Append(wxT("%d"));
277 p += 2;
278 }
279 else if (n == (int)dt.GetMonth()+1)
280 {
281 m_format.Append(wxT("%m"));
282 p += 2;
283 }
284 else if (n == dt.GetYear())
285 {
286 m_format.Append(wxT("%Y"));
287 p += 4;
288 }
289 else if (n == (dt.GetYear() % 100))
290 {
291 if ( HasDPFlag(wxDP_SHOWCENTURY) )
292 m_format.Append(wxT("%Y"));
293 else
294 m_format.Append(wxT("%y"));
295 p += 2;
296 }
297 else
298 m_format.Append(*p++);
299 }
300
301 if ( m_combo )
302 {
303 wxArrayString allowedChars;
304 for ( wxChar c = _T('0'); c <= _T('9'); c++ )
305 allowedChars.Add(wxString(c, 1));
306
307 const wxChar *p2 = m_format.c_str();
308 while ( *p2 )
309 {
310 if ( *p2 == '%')
311 p2 += 2;
312 else
313 allowedChars.Add(wxString(*p2++, 1));
314 }
315
316 #if wxUSE_VALIDATORS
317 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
318 tv.SetIncludes(allowedChars);
319 m_combo->SetValidator(tv);
320 #endif
321
322 if (m_currentDate.IsValid())
323 m_combo->SetText(m_currentDate.Format(m_format));
324 }
325
326 return true;
327 }
328
329 virtual void SetStringValue(const wxString& s)
330 {
331 wxDateTime dt;
332 if ( ParseDateTime(s, &dt) )
333 m_currentDate = dt;
334 else if ( HasDPFlag(wxDP_ALLOWNONE) )
335 m_currentDate = dt;
336 }
337
338 virtual wxString GetStringValue() const
339 {
340 if ( !m_currentDate.IsValid() )
341 return wxEmptyString;
342
343 return m_currentDate.Format(m_format);
344 }
345
346 private:
347
348 wxSize m_useSize;
349 wxString m_format;
350 wxDateTime m_currentDate;
351
352 DECLARE_EVENT_TABLE()
353 };
354
355
356 BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
357 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
358 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
359 EVT_CALENDAR_DAY(wxID_ANY, wxCalendarComboPopup::OnSelChange)
360 EVT_CALENDAR_MONTH(wxID_ANY, wxCalendarComboPopup::OnSelChange)
361 EVT_CALENDAR_YEAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
362 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
363 END_EVENT_TABLE()
364
365
366 // ============================================================================
367 // wxDatePickerCtrlGeneric implementation
368 // ============================================================================
369
370 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
371 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
372 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
373 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
374 END_EVENT_TABLE()
375
376 #ifndef wxHAS_NATIVE_DATEPICKCTRL
377 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
378 #endif
379
380 // ----------------------------------------------------------------------------
381 // creation
382 // ----------------------------------------------------------------------------
383
384 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
385 wxWindowID id,
386 const wxDateTime& date,
387 const wxPoint& pos,
388 const wxSize& size,
389 long style,
390 const wxValidator& validator,
391 const wxString& name)
392 {
393 wxASSERT_MSG( !(style & wxDP_SPIN),
394 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
395
396 if ( !wxControl::Create(parent, id, pos, size,
397 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
398 validator, name) )
399 {
400 return false;
401 }
402
403 InheritAttributes();
404
405 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
406 wxDefaultPosition, wxDefaultSize);
407
408 m_combo->SetCtrlMainWnd(this);
409
410 m_popup = new wxCalendarComboPopup();
411
412 #if defined(__WXMSW__)
413 // without this keyboard navigation in month control doesn't work
414 m_combo->UseAltPopupWindow();
415 #endif
416 m_combo->SetPopupControl(m_popup);
417
418 m_cal = m_popup;
419
420 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
421
422 SetInitialSize(size);
423
424 return true;
425 }
426
427
428 void wxDatePickerCtrlGeneric::Init()
429 {
430 m_combo = NULL;
431 m_cal = NULL;
432 m_popup = NULL;
433 }
434
435 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
436 {
437 }
438
439 bool wxDatePickerCtrlGeneric::Destroy()
440 {
441 if ( m_combo )
442 m_combo->Destroy();
443
444 m_combo = NULL;
445 m_cal = NULL;
446 m_popup = NULL;
447
448 return wxControl::Destroy();
449 }
450
451 // ----------------------------------------------------------------------------
452 // overridden base class methods
453 // ----------------------------------------------------------------------------
454
455 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
456 {
457 return m_combo->GetBestSize();
458 }
459
460 // ----------------------------------------------------------------------------
461 // wxDatePickerCtrlGeneric API
462 // ----------------------------------------------------------------------------
463
464 bool
465 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
466 const wxDateTime& upperdate)
467 {
468 return m_cal->SetDateRange(lowerdate, upperdate);
469 }
470
471
472 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
473 {
474 return m_popup->GetDateValue();
475 }
476
477
478 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
479 {
480 m_popup->SetDateValue(date);
481 }
482
483
484 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
485 {
486 if (dt1)
487 *dt1 = m_cal->GetLowerDateLimit();
488 if (dt2)
489 *dt2 = m_cal->GetUpperDateLimit();
490 return true;
491 }
492
493
494 void
495 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
496 {
497 m_cal->SetDateRange(dt1, dt2);
498 }
499
500 // ----------------------------------------------------------------------------
501 // event handlers
502 // ----------------------------------------------------------------------------
503
504
505 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
506 {
507 if ( m_combo )
508 m_combo->SetSize(GetClientSize());
509
510 event.Skip();
511 }
512
513
514 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
515 {
516 ev.SetEventObject(this);
517 ev.SetId(GetId());
518 GetParent()->GetEventHandler()->ProcessEvent(ev);
519
520 // We'll create an additional event if the date is valid.
521 // If the date isn't valid, the user's probably in the middle of typing
522 wxDateTime dt;
523 if ( !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
524 return;
525
526 m_popup->SendDateEvent(dt);
527 }
528
529
530 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
531 {
532 m_combo->SetFocus();
533 }
534
535
536 #endif // wxUSE_DATEPICKCTRL_GENERIC
537
538 #endif // wxUSE_DATEPICKCTRL
539