Forward declare classes instead of including their declarations.
[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/calctrl.h"
38 #include "wx/combo.h"
39
40 #include "wx/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.c_str(), 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 wxString fmt = wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT);
212 if ( HasDPFlag(wxDP_SHOWCENTURY) )
213 fmt.Replace("%y", "%Y");
214
215 return fmt;
216 }
217
218 bool SetFormat(const wxString& fmt)
219 {
220 m_format = fmt;
221
222 if ( m_combo )
223 {
224 wxArrayString allowedChars;
225 for ( wxChar c = wxT('0'); c <= wxT('9'); c++ )
226 allowedChars.Add(wxString(c, 1));
227
228 const wxChar *p2 = m_format.c_str();
229 while ( *p2 )
230 {
231 if ( *p2 == '%')
232 p2 += 2;
233 else
234 allowedChars.Add(wxString(*p2++, 1));
235 }
236
237 #if wxUSE_VALIDATORS
238 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
239 tv.SetIncludes(allowedChars);
240 m_combo->SetValidator(tv);
241 #endif
242
243 if ( GetDate().IsValid() )
244 m_combo->SetText(GetDate().Format(m_format));
245 }
246
247 return true;
248 }
249
250 virtual void SetStringValue(const wxString& s)
251 {
252 wxDateTime dt;
253 if ( !s.empty() && ParseDateTime(s, &dt) )
254 SetDate(dt);
255 //else: keep the old value
256 }
257
258 virtual wxString GetStringValue() const
259 {
260 return GetStringValueFor(GetDate());
261 }
262
263 private:
264 // returns either the given date representation using the current format or
265 // an empty string if it's invalid
266 wxString GetStringValueFor(const wxDateTime& dt) const
267 {
268 wxString val;
269 if ( dt.IsValid() )
270 val = dt.Format(m_format);
271
272 return val;
273 }
274
275 wxSize m_useSize;
276 wxString m_format;
277
278 DECLARE_EVENT_TABLE()
279 };
280
281
282 BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
283 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
284 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
285 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
286 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
287 END_EVENT_TABLE()
288
289
290 // ============================================================================
291 // wxDatePickerCtrlGeneric implementation
292 // ============================================================================
293
294 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
295 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
296 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
297 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
298 END_EVENT_TABLE()
299
300 #ifndef wxHAS_NATIVE_DATEPICKCTRL
301 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
302 #endif
303
304 // ----------------------------------------------------------------------------
305 // creation
306 // ----------------------------------------------------------------------------
307
308 bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
309 wxWindowID id,
310 const wxDateTime& date,
311 const wxPoint& pos,
312 const wxSize& size,
313 long style,
314 const wxValidator& validator,
315 const wxString& name)
316 {
317 wxASSERT_MSG( !(style & wxDP_SPIN),
318 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
319
320 if ( !wxControl::Create(parent, id, pos, size,
321 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
322 validator, name) )
323 {
324 return false;
325 }
326
327 InheritAttributes();
328
329 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
330 wxDefaultPosition, wxDefaultSize);
331
332 m_combo->SetCtrlMainWnd(this);
333
334 m_popup = new wxCalendarComboPopup();
335
336 #if defined(__WXMSW__)
337 // without this keyboard navigation in month control doesn't work
338 m_combo->UseAltPopupWindow();
339 #endif
340 m_combo->SetPopupControl(m_popup);
341
342 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
343
344 SetInitialSize(size);
345
346 return true;
347 }
348
349
350 void wxDatePickerCtrlGeneric::Init()
351 {
352 m_combo = NULL;
353 m_popup = NULL;
354 }
355
356 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
357 {
358 }
359
360 bool wxDatePickerCtrlGeneric::Destroy()
361 {
362 if ( m_combo )
363 m_combo->Destroy();
364
365 m_combo = NULL;
366 m_popup = NULL;
367
368 return wxControl::Destroy();
369 }
370
371 // ----------------------------------------------------------------------------
372 // overridden base class methods
373 // ----------------------------------------------------------------------------
374
375 wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
376 {
377 return m_combo->GetBestSize();
378 }
379
380 // ----------------------------------------------------------------------------
381 // wxDatePickerCtrlGeneric API
382 // ----------------------------------------------------------------------------
383
384 bool
385 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
386 const wxDateTime& upperdate)
387 {
388 return m_popup->SetDateRange(lowerdate, upperdate);
389 }
390
391
392 wxDateTime wxDatePickerCtrlGeneric::GetValue() const
393 {
394 if ( HasFlag(wxDP_ALLOWNONE) && m_popup->IsTextEmpty() )
395 return wxInvalidDateTime;
396 return m_popup->GetDate();
397 }
398
399
400 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
401 {
402 m_popup->SetDateValue(date);
403 }
404
405
406 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
407 {
408 return m_popup->GetDateRange(dt1, dt2);
409 }
410
411
412 void
413 wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
414 {
415 m_popup->SetDateRange(dt1, dt2);
416 }
417
418 wxCalendarCtrl *wxDatePickerCtrlGeneric::GetCalendar() const
419 {
420 return m_popup;
421 }
422
423 // ----------------------------------------------------------------------------
424 // event handlers
425 // ----------------------------------------------------------------------------
426
427
428 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
429 {
430 if ( m_combo )
431 m_combo->SetSize(GetClientSize());
432
433 event.Skip();
434 }
435
436
437 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
438 {
439 ev.SetEventObject(this);
440 ev.SetId(GetId());
441 GetParent()->GetEventHandler()->ProcessEvent(ev);
442
443 // We'll create an additional event if the date is valid.
444 // If the date isn't valid, the user's probably in the middle of typing
445 wxDateTime dt;
446 if ( !m_popup || !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
447 return;
448
449 m_popup->SendDateEvent(dt);
450 }
451
452
453 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
454 {
455 m_combo->SetFocus();
456 }
457
458
459 #endif // wxUSE_DATEPICKCTRL
460