Added validator to generic date picker control
[wxWidgets.git] / src / generic / datectlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: 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 wxUSE_DATEPICKCTRL_GENERIC || !defined(wxHAS_NATIVE_DATEPICKCTRL)
33
34 #ifndef WX_PRECOMP
35 #include "wx/bmpbuttn.h"
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 // otherwise it's defined in the native version implementation
44 #ifndef wxHAS_NATIVE_DATEPICKCTRL
45 #define _WX_DEFINE_DATE_EVENTS_
46 #endif
47
48 #include "wx/dateevt.h"
49 #include "wx/generic/datectrl.h"
50
51 #include "wx/calctrl.h"
52 #include "wx/popupwin.h"
53 #include "wx/renderer.h"
54
55 // ----------------------------------------------------------------------------
56 // constants
57 // ----------------------------------------------------------------------------
58
59 enum
60 {
61 CTRLID_TXT = 101,
62 CTRLID_CAL,
63 CTRLID_BTN,
64 CTRLID_PAN
65 };
66
67 #ifndef DEFAULT_ITEM_WIDTH
68 #define DEFAULT_ITEM_WIDTH 100
69 #endif
70
71 // ============================================================================
72 // wxDatePickerCtrlGeneric implementation
73 // ============================================================================
74
75 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
76 EVT_BUTTON(CTRLID_BTN, wxDatePickerCtrlGeneric::OnClick)
77 EVT_TEXT(CTRLID_TXT, wxDatePickerCtrlGeneric::OnText)
78 EVT_CHILD_FOCUS(wxDatePickerCtrlGeneric::OnChildSetFocus)
79 END_EVENT_TABLE()
80
81 #ifndef wxHAS_NATIVE_DATEPICKCTRL
82 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxDatePickerCtrlBase)
83 #endif
84
85 // ----------------------------------------------------------------------------
86 // creation
87 // ----------------------------------------------------------------------------
88
89 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
90 wxWindowID id,
91 const wxDateTime& date,
92 const wxPoint& pos,
93 const wxSize& size,
94 long style,
95 const wxValidator& validator,
96 const wxString& name)
97 {
98 wxASSERT_MSG( !(style & wxDP_SPIN),
99 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
100
101 if ( !wxControl::Create(parent, id, pos, size,
102 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
103 validator, name) )
104
105 {
106 return false;
107 }
108
109 InheritAttributes();
110
111 m_txt = new wxTextCtrl(this, CTRLID_TXT);
112 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
113 (wxObjectEventFunction)
114 (wxEventFunction)
115 (wxCharEventFunction)
116 &wxDatePickerCtrlGeneric::OnEditKey,
117 0, this);
118 m_txt->Connect(wxID_ANY, wxID_ANY, wxEVT_KILL_FOCUS,
119 (wxObjectEventFunction)
120 (wxEventFunction)
121 (wxFocusEventFunction)
122 &wxDatePickerCtrlGeneric::OnKillFocus,
123 0, this);
124
125 const int height = m_txt->GetBestSize().y - 4; // FIXME: fudge
126 wxBitmap bmp(height, height);
127 {
128 wxMemoryDC dc;
129 dc.SelectObject(bmp);
130 wxRendererNative::Get().DrawComboBoxDropButton
131 (
132 this,
133 dc,
134 wxRect(0, 0, height, height)
135 );
136 }
137
138 wxBitmapButton *btn = new wxBitmapButton(this, CTRLID_BTN, bmp);
139 btn->SetMargins(0, 0);
140 m_btn = btn;
141
142 m_popup = new wxPopupWindow(this);
143 m_popup->SetFont(GetFont());
144
145 wxPanel *panel=new wxPanel(m_popup, CTRLID_PAN,
146 wxPoint(0, 0), wxDefaultSize,
147 wxSUNKEN_BORDER);
148 m_cal = new wxCalendarCtrl(panel, CTRLID_CAL, wxDefaultDateTime,
149 wxPoint(0,0), wxDefaultSize,
150 wxCAL_SHOW_HOLIDAYS | wxSUNKEN_BORDER);
151 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_SEL_CHANGED,
152 (wxObjectEventFunction)
153 (wxEventFunction)
154 (wxCalendarEventFunction)
155 &wxDatePickerCtrlGeneric::OnSelChange,
156 0, this);
157 m_cal->Connect(wxID_ANY, wxID_ANY, wxEVT_KEY_DOWN,
158 (wxObjectEventFunction)
159 (wxEventFunction)
160 (wxCharEventFunction)
161 &wxDatePickerCtrlGeneric::OnCalKey,
162 0, this);
163 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DOUBLECLICKED,
164 (wxObjectEventFunction)
165 (wxEventFunction)
166 (wxCalendarEventFunction)
167 &wxDatePickerCtrlGeneric::OnSelChange,
168 0, this);
169 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_DAY_CHANGED,
170 (wxObjectEventFunction)
171 (wxEventFunction)
172 (wxCalendarEventFunction)
173 &wxDatePickerCtrlGeneric::OnSelChange,
174 0, this);
175 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_MONTH_CHANGED,
176 (wxObjectEventFunction)
177 (wxEventFunction)
178 (wxCalendarEventFunction)
179 &wxDatePickerCtrlGeneric::OnSelChange,
180 0, this);
181 m_cal->Connect(CTRLID_CAL, CTRLID_CAL, wxEVT_CALENDAR_YEAR_CHANGED,
182 (wxObjectEventFunction)
183 (wxEventFunction)
184 (wxCalendarEventFunction)
185 &wxDatePickerCtrlGeneric::OnSelChange,
186 0, this);
187
188 wxWindow *yearControl = m_cal->GetYearControl();
189
190 Connect(wxID_ANY, wxID_ANY, wxEVT_SET_FOCUS,
191 (wxObjectEventFunction)
192 (wxEventFunction)
193 (wxFocusEventFunction)
194 &wxDatePickerCtrlGeneric::OnSetFocus);
195
196 wxClientDC dc(yearControl);
197 dc.SetFont(m_font);
198 wxCoord width, dummy;
199 dc.GetTextExtent(wxT("2000"), &width, &dummy);
200 width += ConvertDialogToPixels(wxSize(20,0)).x;
201
202 wxSize calSize = m_cal->GetBestSize();
203 wxSize yearSize = yearControl->GetSize();
204 yearSize.x = width;
205
206 wxPoint yearPosition = yearControl->GetPosition();
207
208 SetFormat(wxT("%x"));
209
210 if (date.IsValid())
211 m_txt->SetValue(date.Format(m_format));
212
213
214 #ifdef __WXMSW__
215 #define CALBORDER 0
216 #else
217 #define CALBORDER 4
218 #endif
219
220 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
221 if (width < calSize.x-4)
222 width = calSize.x-4;
223
224 int calPos = (width-calSize.x)/2;
225 if (calPos == -1)
226 {
227 calPos = 0;
228 width += 2;
229 }
230 m_cal->SetSize(calPos, 0, calSize.x, calSize.y);
231 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
232 yearSize.x, yearSize.y);
233 m_cal->GetMonthControl()->Move(0, 0);
234
235
236
237 panel->SetClientSize(width+CALBORDER/2, calSize.y-2+CALBORDER);
238 m_popup->SetClientSize(panel->GetSize());
239 m_popup->Hide();
240
241 return true;
242 }
243
244
245 void wxDatePickerCtrlGeneric::Init()
246 {
247 m_popup = NULL;
248 m_txt = NULL;
249 m_cal = NULL;
250 m_btn = NULL;
251
252 m_dropped = false;
253 m_ignoreDrop = false;
254 }
255
256
257 bool wxDatePickerCtrlGeneric::Destroy()
258 {
259 if (m_cal)
260 m_cal->Destroy();
261 if (m_popup)
262 m_popup->Destroy();
263 if (m_txt)
264 m_txt->Destroy();
265 if (m_btn)
266 m_btn->Destroy();
267
268 m_popup = NULL;
269 m_txt = NULL;
270 m_cal = NULL;
271 m_btn = NULL;
272
273 return wxControl::Destroy();
274 }
275
276 // ----------------------------------------------------------------------------
277 // overridden base class methods
278 // ----------------------------------------------------------------------------
279
280 void wxDatePickerCtrlGeneric::DoMoveWindow(int x, int y, int w, int h)
281 {
282 wxControl::DoMoveWindow(x, y, w, h);
283 wxSize bs=m_btn->GetBestSize();
284 int eh=m_txt->GetBestSize().y;
285
286 m_txt->SetSize(0, 0, w-bs.x-1, h > eh ? eh : h);
287 m_btn->SetSize(w - bs.x, 0, bs.x, h > bs.y ? bs.y : h);
288
289 if (m_dropped)
290 DropDown();
291 }
292
293 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
294 {
295 int bh=m_btn->GetBestSize().y;
296 int eh=m_txt->GetBestSize().y;
297 return wxSize(DEFAULT_ITEM_WIDTH, bh > eh ? bh : eh);
298 }
299
300
301 bool wxDatePickerCtrlGeneric::Show(bool show)
302 {
303 if ( !wxControl::Show(show) )
304 {
305 return false;
306 }
307
308 if (!show)
309 {
310 if (m_popup)
311 {
312 m_popup->Hide();
313 m_dropped = false;
314 }
315 }
316
317 return true;
318 }
319
320
321 bool wxDatePickerCtrlGeneric::Enable(bool enable)
322 {
323 if ( !wxControl::Enable(enable) )
324 {
325 return false;
326 }
327
328 if (!enable)
329 {
330 if (m_cal)
331 m_cal->Hide();
332 }
333 if (m_btn)
334 m_btn->Enable(enable);
335 return true;
336 }
337
338 // ----------------------------------------------------------------------------
339 // wxDatePickerCtrlGeneric API
340 // ----------------------------------------------------------------------------
341
342 bool
343 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
344 const wxDateTime& upperdate)
345 {
346 return m_cal->SetDateRange(lowerdate, upperdate);
347 }
348
349 bool wxDatePickerCtrlGeneric::SetFormat(const wxChar *fmt)
350 {
351 wxString currentText;
352 wxDateTime currentDate;
353 if (m_txt)
354 {
355 currentText = m_txt->GetValue();
356 if (!currentText.empty())
357 currentDate.ParseFormat(currentText, m_format);
358 }
359 wxDateTime dt;
360 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
361 wxString str=dt.Format(fmt);
362 wxChar *p=(wxChar*)str.c_str();
363
364 m_format=wxEmptyString;
365
366 while (*p)
367 {
368 int n=wxAtoi(p);
369 if (n == dt.GetDay())
370 {
371 m_format.Append(wxT("%d"));
372 p += 2;
373 }
374 else if (n == (int)dt.GetMonth()+1)
375 {
376 m_format.Append(wxT("%m"));
377 p += 2;
378 }
379 else if (n == dt.GetYear())
380 {
381 m_format.Append(wxT("%Y"));
382 p += 4;
383 }
384 else if (n == (dt.GetYear() % 100))
385 {
386 m_format.Append(wxT("%y"));
387 p += 2;
388 }
389 else
390 m_format.Append(*p++);
391 }
392
393 if (m_txt)
394 {
395 wxStringList valList;
396 wxChar c;
397 for (c='0'; c <= '9'; c++)
398 valList.Add(wxString(c, 1));
399 wxChar *p=(wxChar*)m_format.c_str();
400 while (*p)
401 {
402 if (*p == '%')
403 p += 2;
404 else
405 valList.Add(wxString(*p++, 1));
406 }
407 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
408 tv.SetIncludeList(valList);
409
410 m_txt->SetValidator(tv);
411
412 if (!currentText.empty())
413 m_txt->SetValue(currentDate.Format(m_format));
414 }
415 return true;
416 }
417
418
419 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
420 {
421 wxDateTime dt;
422 wxString txt=m_txt->GetValue();
423
424 if (!txt.empty())
425 dt.ParseFormat(txt, m_format);
426
427 return dt;
428 }
429
430
431 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
432 {
433 if (m_cal)
434 {
435 if (date.IsValid())
436 m_txt->SetValue(date.Format(m_format));
437 else
438 m_txt->SetValue(wxEmptyString);
439 }
440 }
441
442
443 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
444 {
445 if (dt1)
446 *dt1 = m_cal->GetLowerDateLimit();
447 if (dt1)
448 *dt2 = m_cal->GetUpperDateLimit();
449 return true;
450 }
451
452
453 void
454 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
455 {
456 m_cal->SetDateRange(dt1, dt2);
457 }
458
459 // ----------------------------------------------------------------------------
460 // event handlers
461 // ----------------------------------------------------------------------------
462
463 void wxDatePickerCtrlGeneric::DropDown(bool down)
464 {
465 if (m_popup)
466 {
467 if (down)
468 {
469 wxDateTime dt;
470 if (!m_txt->GetValue().empty())
471 dt.ParseFormat(m_txt->GetValue(), m_format);
472
473 if (dt.IsValid())
474 m_cal->SetDate(dt);
475 else
476 m_cal->SetDate(wxDateTime::Today());
477
478 wxPoint pos=GetParent()->ClientToScreen(GetPosition());
479 m_popup->Move(pos.x, pos.y + GetSize().y);
480 m_popup->Show();
481 m_dropped = true;
482 }
483 else
484 {
485 if (m_dropped)
486 m_popup->Hide();
487 m_dropped = false;
488 }
489 }
490 }
491
492
493 void wxDatePickerCtrlGeneric::OnChildSetFocus(wxChildFocusEvent &ev)
494 {
495 ev.Skip();
496 m_ignoreDrop = false;
497
498 wxWindow *w=(wxWindow*)ev.GetEventObject();
499 while (w)
500 {
501 if (w == m_popup)
502 return;
503 w = w->GetParent();
504 }
505
506 if (m_dropped)
507 {
508 DropDown(false);
509 if (ev.GetEventObject() == m_btn)
510 m_ignoreDrop = true;
511 }
512 }
513
514
515 void wxDatePickerCtrlGeneric::OnClick(wxCommandEvent& WXUNUSED(event))
516 {
517 if (m_ignoreDrop)
518 {
519 m_ignoreDrop = false;
520 m_txt->SetFocus();
521 }
522 else
523 {
524 DropDown();
525 m_cal->SetFocus();
526 }
527 }
528
529
530 void wxDatePickerCtrlGeneric::OnSetFocus(wxFocusEvent& WXUNUSED(ev))
531 {
532 if (m_txt)
533 {
534 m_txt->SetFocus();
535 m_txt->SetSelection(0, 100);
536 }
537 }
538
539
540 void wxDatePickerCtrlGeneric::OnKillFocus(wxFocusEvent &ev)
541 {
542 ev.Skip();
543
544 wxDateTime dt;
545 dt.ParseFormat(m_txt->GetValue(), m_format);
546 if (!dt.IsValid())
547 m_txt->SetValue(wxEmptyString);
548 else
549 m_txt->SetValue(dt.Format(m_format));
550 }
551
552
553 void wxDatePickerCtrlGeneric::OnSelChange(wxCalendarEvent &ev)
554 {
555 if (m_cal)
556 {
557 m_txt->SetValue(m_cal->GetDate().Format(m_format));
558 if (ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED)
559 {
560 DropDown(false);
561 m_txt->SetFocus();
562 }
563 }
564 ev.SetEventObject(this);
565 ev.SetId(GetId());
566 GetParent()->ProcessEvent(ev);
567 }
568
569
570 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
571 {
572 ev.SetEventObject(this);
573 ev.SetId(GetId());
574 GetParent()->ProcessEvent(ev);
575
576 // We'll create an additional event if the date is valid.
577 // If the date isn't valid, the user's probable in the middle of typing
578 wxString txt=m_txt->GetValue();
579 wxDateTime dt;
580 if (!txt.empty())
581 {
582 dt.ParseFormat(txt, m_format);
583 if (!dt.IsValid())
584 return;
585 }
586
587 wxCalendarEvent cev(m_cal, wxEVT_CALENDAR_SEL_CHANGED);
588 cev.SetEventObject(this);
589 cev.SetId(GetId());
590 cev.SetDate(dt);
591
592 GetParent()->ProcessEvent(cev);
593 }
594
595
596 void wxDatePickerCtrlGeneric::OnEditKey(wxKeyEvent & ev)
597 {
598 if (ev.GetKeyCode() == WXK_DOWN && !ev.HasModifiers())
599 DropDown();
600 else
601 ev.Skip();
602 }
603
604
605 void wxDatePickerCtrlGeneric::OnCalKey(wxKeyEvent & ev)
606 {
607 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
608 DropDown(false);
609 else
610 ev.Skip();
611 }
612
613 #endif // wxUSE_DATEPICKCTRL_GENERIC
614
615 #endif // wxUSE_DATEPICKCTRL
616