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