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