Use wxLocale::GetInfo() to get date format in wxDatePickerCtrlGeneric.
[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 #ifndef WX_PRECOMP
29 #include "wx/dialog.h"
30 #include "wx/dcmemory.h"
31 #include "wx/intl.h"
32 #include "wx/panel.h"
33 #include "wx/textctrl.h"
34 #include "wx/valtext.h"
35 #endif
36
37 #include "wx/datectrl.h"
38 #include "wx/generic/datectrl.h"
39
40
41 // ----------------------------------------------------------------------------
42 // constants
43 // ----------------------------------------------------------------------------
44
45
46 // ----------------------------------------------------------------------------
47 // global variables
48 // ----------------------------------------------------------------------------
49
50
51 // ----------------------------------------------------------------------------
52 // local classes
53 // ----------------------------------------------------------------------------
54
55 class wxCalendarComboPopup : public wxCalendarCtrl,
56 public wxComboPopup
57 {
58 public:
59
60 wxCalendarComboPopup() : wxCalendarCtrl(),
61 wxComboPopup()
62 {
63 }
64
65 virtual void Init()
66 {
67 }
68
69 // NB: Don't create lazily since it didn't work that way before
70 // wxComboCtrl was used, and changing behaviour would almost
71 // certainly introduce new bugs.
72 virtual bool Create(wxWindow* parent)
73 {
74 if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
75 wxPoint(0, 0), wxDefaultSize,
76 wxCAL_SEQUENTIAL_MONTH_SELECTION
77 | wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
78 return false;
79
80 SetFormat(GetLocaleDateFormat());
81
82 m_useSize = wxCalendarCtrl::GetBestSize();
83
84 wxWindow* tx = m_combo->GetTextCtrl();
85 if ( !tx )
86 tx = m_combo;
87
88 tx->Connect(wxEVT_KILL_FOCUS,
89 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
90 NULL, this);
91
92 return true;
93 }
94
95 virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
96 int WXUNUSED(prefHeight),
97 int WXUNUSED(maxHeight))
98 {
99 return m_useSize;
100 }
101
102 virtual wxWindow *GetControl() { return this; }
103
104 void SetDateValue(const wxDateTime& date)
105 {
106 if ( date.IsValid() )
107 {
108 m_combo->SetText(date.Format(m_format));
109 SetDate(date);
110 }
111 else // invalid date
112 {
113 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
114 wxT("this control must have a valid date") );
115
116 m_combo->SetText(wxEmptyString);
117 }
118 }
119
120 bool IsTextEmpty() const
121 {
122 return m_combo->GetTextCtrl()->IsEmpty();
123 }
124
125 bool ParseDateTime(const wxString& s, wxDateTime* pDt)
126 {
127 wxASSERT(pDt);
128
129 if ( !s.empty() )
130 {
131 pDt->ParseFormat(s.c_str(), m_format);
132 if ( !pDt->IsValid() )
133 return false;
134 }
135
136 return true;
137 }
138
139 void SendDateEvent(const wxDateTime& dt)
140 {
141 // Sends both wxCalendarEvent and wxDateEvent
142 wxWindow* datePicker = m_combo->GetParent();
143
144 wxCalendarEvent cev(datePicker, dt, wxEVT_CALENDAR_SEL_CHANGED);
145 datePicker->GetEventHandler()->ProcessEvent(cev);
146
147 wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
148 datePicker->GetEventHandler()->ProcessEvent(event);
149 }
150
151 private:
152
153 void OnCalKey(wxKeyEvent & ev)
154 {
155 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
156 Dismiss();
157 else
158 ev.Skip();
159 }
160
161 void OnSelChange(wxCalendarEvent &ev)
162 {
163 m_combo->SetText(GetDate().Format(m_format));
164
165 if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
166 {
167 Dismiss();
168 }
169
170 SendDateEvent(GetDate());
171 }
172
173 void OnKillTextFocus(wxFocusEvent &ev)
174 {
175 ev.Skip();
176
177 const wxDateTime& dtOld = GetDate();
178
179 wxDateTime dt;
180 wxString value = m_combo->GetValue();
181 if ( !ParseDateTime(value, &dt) )
182 {
183 if ( !HasDPFlag(wxDP_ALLOWNONE) )
184 dt = dtOld;
185 }
186
187 m_combo->SetText(GetStringValueFor(dt));
188
189 if ( !dt.IsValid() && HasDPFlag(wxDP_ALLOWNONE) )
190 return;
191
192 // notify that we had to change the date after validation
193 if ( (dt.IsValid() && (!dtOld.IsValid() || dt != dtOld)) ||
194 (!dt.IsValid() && dtOld.IsValid()) )
195 {
196 SetDate(dt);
197 SendDateEvent(dt);
198 }
199 }
200
201 bool HasDPFlag(int flag) const
202 {
203 return m_combo->GetParent()->HasFlag(flag);
204 }
205
206 // Return the format to be used for the dates shown by the control. This
207 // functions honours wxDP_SHOWCENTURY flag.
208 wxString GetLocaleDateFormat() const
209 {
210 wxString fmt = wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT);
211 if ( HasDPFlag(wxDP_SHOWCENTURY) )
212 fmt.Replace("%y", "%Y");
213
214 return fmt;
215 }
216
217 bool SetFormat(const wxString& fmt)
218 {
219 m_format = fmt;
220
221 if ( m_combo )
222 {
223 wxArrayString allowedChars;
224 for ( wxChar c = wxT('0'); c <= wxT('9'); c++ )
225 allowedChars.Add(wxString(c, 1));
226
227 const wxChar *p2 = m_format.c_str();
228 while ( *p2 )
229 {
230 if ( *p2 == '%')
231 p2 += 2;
232 else
233 allowedChars.Add(wxString(*p2++, 1));
234 }
235
236 #if wxUSE_VALIDATORS
237 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
238 tv.SetIncludes(allowedChars);
239 m_combo->SetValidator(tv);
240 #endif
241
242 if ( GetDate().IsValid() )
243 m_combo->SetText(GetDate().Format(m_format));
244 }
245
246 return true;
247 }
248
249 virtual void SetStringValue(const wxString& s)
250 {
251 wxDateTime dt;
252 if ( !s.empty() && ParseDateTime(s, &dt) )
253 SetDate(dt);
254 //else: keep the old value
255 }
256
257 virtual wxString GetStringValue() const
258 {
259 return GetStringValueFor(GetDate());
260 }
261
262 private:
263 // returns either the given date representation using the current format or
264 // an empty string if it's invalid
265 wxString GetStringValueFor(const wxDateTime& dt) const
266 {
267 wxString val;
268 if ( dt.IsValid() )
269 val = dt.Format(m_format);
270
271 return val;
272 }
273
274 wxSize m_useSize;
275 wxString m_format;
276
277 DECLARE_EVENT_TABLE()
278 };
279
280
281 BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
282 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
283 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
284 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
285 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
286 END_EVENT_TABLE()
287
288
289 // ============================================================================
290 // wxDatePickerCtrlGeneric implementation
291 // ============================================================================
292
293 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
294 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
295 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
296 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
297 END_EVENT_TABLE()
298
299 #ifndef wxHAS_NATIVE_DATEPICKCTRL
300 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
301 #endif
302
303 // ----------------------------------------------------------------------------
304 // creation
305 // ----------------------------------------------------------------------------
306
307 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
308 wxWindowID id,
309 const wxDateTime& date,
310 const wxPoint& pos,
311 const wxSize& size,
312 long style,
313 const wxValidator& validator,
314 const wxString& name)
315 {
316 wxASSERT_MSG( !(style & wxDP_SPIN),
317 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
318
319 if ( !wxControl::Create(parent, id, pos, size,
320 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
321 validator, name) )
322 {
323 return false;
324 }
325
326 InheritAttributes();
327
328 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
329 wxDefaultPosition, wxDefaultSize);
330
331 m_combo->SetCtrlMainWnd(this);
332
333 m_popup = new wxCalendarComboPopup();
334
335 #if defined(__WXMSW__)
336 // without this keyboard navigation in month control doesn't work
337 m_combo->UseAltPopupWindow();
338 #endif
339 m_combo->SetPopupControl(m_popup);
340
341 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
342
343 SetInitialSize(size);
344
345 return true;
346 }
347
348
349 void wxDatePickerCtrlGeneric::Init()
350 {
351 m_combo = NULL;
352 m_popup = NULL;
353 }
354
355 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
356 {
357 }
358
359 bool wxDatePickerCtrlGeneric::Destroy()
360 {
361 if ( m_combo )
362 m_combo->Destroy();
363
364 m_combo = NULL;
365 m_popup = NULL;
366
367 return wxControl::Destroy();
368 }
369
370 // ----------------------------------------------------------------------------
371 // overridden base class methods
372 // ----------------------------------------------------------------------------
373
374 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
375 {
376 return m_combo->GetBestSize();
377 }
378
379 // ----------------------------------------------------------------------------
380 // wxDatePickerCtrlGeneric API
381 // ----------------------------------------------------------------------------
382
383 bool
384 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
385 const wxDateTime& upperdate)
386 {
387 return m_popup->SetDateRange(lowerdate, upperdate);
388 }
389
390
391 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
392 {
393 if ( HasFlag(wxDP_ALLOWNONE) && m_popup->IsTextEmpty() )
394 return wxInvalidDateTime;
395 return m_popup->GetDate();
396 }
397
398
399 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
400 {
401 m_popup->SetDateValue(date);
402 }
403
404
405 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
406 {
407 return m_popup->GetDateRange(dt1, dt2);
408 }
409
410
411 void
412 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
413 {
414 m_popup->SetDateRange(dt1, dt2);
415 }
416
417 wxCalendarCtrl *wxDatePickerCtrlGeneric::GetCalendar() const
418 {
419 return m_popup;
420 }
421
422 // ----------------------------------------------------------------------------
423 // event handlers
424 // ----------------------------------------------------------------------------
425
426
427 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
428 {
429 if ( m_combo )
430 m_combo->SetSize(GetClientSize());
431
432 event.Skip();
433 }
434
435
436 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
437 {
438 ev.SetEventObject(this);
439 ev.SetId(GetId());
440 GetParent()->GetEventHandler()->ProcessEvent(ev);
441
442 // We'll create an additional event if the date is valid.
443 // If the date isn't valid, the user's probably in the middle of typing
444 wxDateTime dt;
445 if ( !m_popup || !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
446 return;
447
448 m_popup->SendDateEvent(dt);
449 }
450
451
452 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
453 {
454 m_combo->SetFocus();
455 }
456
457
458 #endif // wxUSE_DATEPICKCTRL
459