]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datectlg.cpp
Return NULL from wxWindow::GetCapture() when the capture is being lost.
[wxWidgets.git] / src / generic / datectlg.cpp
CommitLineData
39df3acd 1/////////////////////////////////////////////////////////////////////////////
68f2155b 2// Name: src/generic/datectlg.cpp
7ae712f5 3// Purpose: generic wxDatePickerCtrlGeneric implementation
39df3acd
VZ
4// Author: Andreas Pflug
5// Modified by:
6// Created: 2005-01-19
39df3acd
VZ
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
7ae712f5
VZ
25#if wxUSE_DATEPICKCTRL
26
a962d4e0 27#ifndef WX_PRECOMP
a962d4e0
KH
28 #include "wx/dialog.h"
29 #include "wx/dcmemory.h"
4d4c704c 30 #include "wx/intl.h"
a962d4e0
KH
31 #include "wx/panel.h"
32 #include "wx/textctrl.h"
33 #include "wx/valtext.h"
34#endif
35
4a40cd9b
VZ
36#include "wx/calctrl.h"
37#include "wx/combo.h"
91edf16c 38
4a40cd9b 39#include "wx/datectrl.h"
df16cb09 40#include "wx/generic/datectrl.h"
39df3acd
VZ
41
42// ----------------------------------------------------------------------------
43// constants
44// ----------------------------------------------------------------------------
45
930fb29e 46
aa74ad5b
VZ
47// ----------------------------------------------------------------------------
48// global variables
49// ----------------------------------------------------------------------------
50
930fb29e 51
1721a8c0
VZ
52// ----------------------------------------------------------------------------
53// local classes
54// ----------------------------------------------------------------------------
55
c245a012 56class wxCalendarComboPopup : public wxCalendarCtrl,
85fa9d60 57 public wxComboPopup
38511687
VZ
58{
59public:
38511687 60
c245a012 61 wxCalendarComboPopup() : wxCalendarCtrl(),
85fa9d60 62 wxComboPopup()
caad7637 63 {
caad7637
JS
64 }
65
85fa9d60
VZ
66 virtual void Init()
67 {
68 }
caad7637 69
85fa9d60
VZ
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 {
c245a012 75 if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
85fa9d60 76 wxPoint(0, 0), wxDefaultSize,
6c70323f 77 wxCAL_SEQUENTIAL_MONTH_SELECTION
c245a012 78 | wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
85fa9d60 79 return false;
caad7637 80
6c70323f 81 SetFormat(GetLocaleDateFormat());
caad7637 82
c245a012 83 m_useSize = wxCalendarCtrl::GetBestSize();
caad7637 84
85fa9d60
VZ
85 wxWindow* tx = m_combo->GetTextCtrl();
86 if ( !tx )
87 tx = m_combo;
caad7637 88
85fa9d60
VZ
89 tx->Connect(wxEVT_KILL_FOCUS,
90 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
91 NULL, this);
caad7637 92
85fa9d60
VZ
93 return true;
94 }
38511687 95
85fa9d60
VZ
96 virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
97 int WXUNUSED(prefHeight),
98 int WXUNUSED(maxHeight))
99 {
100 return m_useSize;
101 }
38511687 102
85fa9d60 103 virtual wxWindow *GetControl() { return this; }
930fb29e 104
85fa9d60
VZ
105 void SetDateValue(const wxDateTime& date)
106 {
107 if ( date.IsValid() )
108 {
109 m_combo->SetText(date.Format(m_format));
d6781628 110 SetDate(date);
85fa9d60
VZ
111 }
112 else // invalid date
113 {
114 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
9a83f860 115 wxT("this control must have a valid date") );
caad7637 116
85fa9d60
VZ
117 m_combo->SetText(wxEmptyString);
118 }
85fa9d60 119 }
94ab4d92 120
2ccc6650
VZ
121 bool IsTextEmpty() const
122 {
123 return m_combo->GetTextCtrl()->IsEmpty();
124 }
125
85fa9d60
VZ
126 bool ParseDateTime(const wxString& s, wxDateTime* pDt)
127 {
128 wxASSERT(pDt);
38511687 129
85fa9d60
VZ
130 if ( !s.empty() )
131 {
af184f1d 132 pDt->ParseFormat(s, m_format);
85fa9d60
VZ
133 if ( !pDt->IsValid() )
134 return false;
135 }
38511687 136
85fa9d60
VZ
137 return true;
138 }
38511687 139
85fa9d60
VZ
140 void SendDateEvent(const wxDateTime& dt)
141 {
85fa9d60
VZ
142 // Sends both wxCalendarEvent and wxDateEvent
143 wxWindow* datePicker = m_combo->GetParent();
38511687 144
628e155d 145 wxCalendarEvent cev(datePicker, dt, wxEVT_CALENDAR_SEL_CHANGED);
d1b736b7 146 datePicker->GetEventHandler()->ProcessEvent(cev);
38511687 147
85fa9d60 148 wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
d1b736b7 149 datePicker->GetEventHandler()->ProcessEvent(event);
85fa9d60 150 }
caad7637 151
85fa9d60 152private:
caad7637 153
85fa9d60 154 void OnCalKey(wxKeyEvent & ev)
caad7637 155 {
85fa9d60
VZ
156 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
157 Dismiss();
158 else
159 ev.Skip();
caad7637 160 }
caad7637 161
85fa9d60
VZ
162 void OnSelChange(wxCalendarEvent &ev)
163 {
2fda1fe5 164 m_combo->SetText(GetDate().Format(m_format));
caad7637 165
85fa9d60
VZ
166 if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
167 {
168 Dismiss();
169 }
caad7637 170
2fda1fe5 171 SendDateEvent(GetDate());
38511687
VZ
172 }
173
85fa9d60
VZ
174 void OnKillTextFocus(wxFocusEvent &ev)
175 {
176 ev.Skip();
caad7637 177
2fda1fe5
VZ
178 const wxDateTime& dtOld = GetDate();
179
85fa9d60
VZ
180 wxDateTime dt;
181 wxString value = m_combo->GetValue();
182 if ( !ParseDateTime(value, &dt) )
183 {
184 if ( !HasDPFlag(wxDP_ALLOWNONE) )
2fda1fe5 185 dt = dtOld;
85fa9d60 186 }
caad7637 187
2fda1fe5 188 m_combo->SetText(GetStringValueFor(dt));
38511687 189
d6781628
RD
190 if ( !dt.IsValid() && HasDPFlag(wxDP_ALLOWNONE) )
191 return;
6c70323f 192
85fa9d60 193 // notify that we had to change the date after validation
2fda1fe5
VZ
194 if ( (dt.IsValid() && (!dtOld.IsValid() || dt != dtOld)) ||
195 (!dt.IsValid() && dtOld.IsValid()) )
85fa9d60 196 {
2fda1fe5 197 SetDate(dt);
85fa9d60
VZ
198 SendDateEvent(dt);
199 }
200 }
38511687 201
6c70323f 202 bool HasDPFlag(int flag) const
caad7637 203 {
85fa9d60 204 return m_combo->GetParent()->HasFlag(flag);
caad7637 205 }
caad7637 206
4d4c704c
VZ
207 // Return the format to be used for the dates shown by the control. This
208 // functions honours wxDP_SHOWCENTURY flag.
6c70323f 209 wxString GetLocaleDateFormat() const
85fa9d60 210 {
88def163 211#if wxUSE_INTL
4d4c704c
VZ
212 wxString fmt = wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT);
213 if ( HasDPFlag(wxDP_SHOWCENTURY) )
214 fmt.Replace("%y", "%Y");
caad7637 215
4d4c704c 216 return fmt;
88def163
DS
217#else // !wxUSE_INTL
218 return wxT("x");
219#endif // wxUSE_INTL/!wxUSE_INTL
6c70323f
VZ
220 }
221
222 bool SetFormat(const wxString& fmt)
223 {
224 m_format = fmt;
225
85fa9d60
VZ
226 if ( m_combo )
227 {
228 wxArrayString allowedChars;
9a83f860 229 for ( wxChar c = wxT('0'); c <= wxT('9'); c++ )
85fa9d60
VZ
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
2fda1fe5
VZ
247 if ( GetDate().IsValid() )
248 m_combo->SetText(GetDate().Format(m_format));
85fa9d60 249 }
1721a8c0 250
85fa9d60 251 return true;
1721a8c0
VZ
252 }
253
85fa9d60 254 virtual void SetStringValue(const wxString& s)
1721a8c0 255 {
85fa9d60 256 wxDateTime dt;
d6781628 257 if ( !s.empty() && ParseDateTime(s, &dt) )
2fda1fe5 258 SetDate(dt);
d6781628 259 //else: keep the old value
1721a8c0 260 }
1721a8c0 261
85fa9d60 262 virtual wxString GetStringValue() const
1721a8c0 263 {
2fda1fe5 264 return GetStringValueFor(GetDate());
1721a8c0
VZ
265 }
266
85fa9d60 267private:
2fda1fe5
VZ
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 }
85fa9d60
VZ
278
279 wxSize m_useSize;
280 wxString m_format;
85fa9d60
VZ
281
282 DECLARE_EVENT_TABLE()
1721a8c0
VZ
283};
284
85fa9d60 285
c245a012 286BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
85fa9d60
VZ
287 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
288 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
628e155d 289 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
85fa9d60
VZ
290 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
291END_EVENT_TABLE()
292
1721a8c0 293
39df3acd 294// ============================================================================
7ae712f5 295// wxDatePickerCtrlGeneric implementation
39df3acd
VZ
296// ============================================================================
297
7ae712f5 298BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
85fa9d60 299 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
caad7637 300 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
4427c0a3 301 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
39df3acd
VZ
302END_EVENT_TABLE()
303
b6292ff0 304#ifndef wxHAS_NATIVE_DATEPICKCTRL
94ab4d92 305 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
b6292ff0 306#endif
39df3acd
VZ
307
308// ----------------------------------------------------------------------------
309// creation
310// ----------------------------------------------------------------------------
311
7ae712f5
VZ
312bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
313 wxWindowID id,
314 const wxDateTime& date,
315 const wxPoint& pos,
316 const wxSize& size,
317 long style,
807f5038 318 const wxValidator& validator,
7ae712f5 319 const wxString& name)
39df3acd 320{
29c86948 321 wxASSERT_MSG( !(style & wxDP_SPIN),
9a83f860 322 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
29c86948 323
39df3acd 324 if ( !wxControl::Create(parent, id, pos, size,
85fa9d60 325 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
807f5038 326 validator, name) )
39df3acd
VZ
327 {
328 return false;
329 }
330
39df3acd
VZ
331 InheritAttributes();
332
85fa9d60
VZ
333 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
334 wxDefaultPosition, wxDefaultSize);
39df3acd 335
4427c0a3
RR
336 m_combo->SetCtrlMainWnd(this);
337
85fa9d60 338 m_popup = new wxCalendarComboPopup();
39df3acd 339
06077aaf
VZ
340#if defined(__WXMSW__)
341 // without this keyboard navigation in month control doesn't work
342 m_combo->UseAltPopupWindow();
343#endif
85fa9d60 344 m_combo->SetPopupControl(m_popup);
39df3acd 345
85fa9d60 346 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
1721a8c0 347
170acdc9 348 SetInitialSize(size);
930fb29e 349
3a0c6181 350 return true;
39df3acd
VZ
351}
352
353
7ae712f5 354void wxDatePickerCtrlGeneric::Init()
39df3acd 355{
85fa9d60 356 m_combo = NULL;
85fa9d60 357 m_popup = NULL;
39df3acd
VZ
358}
359
a9d13e15
JS
360wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
361{
a9d13e15 362}
39df3acd 363
7ae712f5 364bool wxDatePickerCtrlGeneric::Destroy()
39df3acd 365{
85fa9d60
VZ
366 if ( m_combo )
367 m_combo->Destroy();
39df3acd 368
85fa9d60 369 m_combo = NULL;
85fa9d60 370 m_popup = NULL;
39df3acd
VZ
371
372 return wxControl::Destroy();
373}
374
375// ----------------------------------------------------------------------------
376// overridden base class methods
377// ----------------------------------------------------------------------------
378
7ae712f5 379wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
39df3acd 380{
2060e34c
VZ
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;
39df3acd
VZ
394}
395
a9e41db7
VZ
396wxWindowList wxDatePickerCtrlGeneric::GetCompositeWindowParts() const
397{
398 wxWindowList parts;
162e221f
VZ
399 parts.push_back(m_combo);
400 parts.push_back(m_popup);
a9e41db7
VZ
401 return parts;
402}
403
39df3acd 404// ----------------------------------------------------------------------------
7ae712f5 405// wxDatePickerCtrlGeneric API
39df3acd
VZ
406// ----------------------------------------------------------------------------
407
4b134bb2 408bool
7ae712f5
VZ
409wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
410 const wxDateTime& upperdate)
4b134bb2 411{
2fda1fe5 412 return m_popup->SetDateRange(lowerdate, upperdate);
4b134bb2
VZ
413}
414
39df3acd 415
7ae712f5 416wxDateTime wxDatePickerCtrlGeneric::GetValue() const
39df3acd 417{
2ccc6650
VZ
418 if ( HasFlag(wxDP_ALLOWNONE) && m_popup->IsTextEmpty() )
419 return wxInvalidDateTime;
2fda1fe5 420 return m_popup->GetDate();
39df3acd
VZ
421}
422
423
7ae712f5 424void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
39df3acd 425{
85fa9d60 426 m_popup->SetDateValue(date);
39df3acd
VZ
427}
428
429
7ae712f5 430bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
39df3acd 431{
c245a012 432 return m_popup->GetDateRange(dt1, dt2);
39df3acd
VZ
433}
434
435
7ae712f5
VZ
436void
437wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
39df3acd 438{
2fda1fe5
VZ
439 m_popup->SetDateRange(dt1, dt2);
440}
441
c245a012 442wxCalendarCtrl *wxDatePickerCtrlGeneric::GetCalendar() const
2fda1fe5
VZ
443{
444 return m_popup;
39df3acd
VZ
445}
446
447// ----------------------------------------------------------------------------
448// event handlers
449// ----------------------------------------------------------------------------
450
39df3acd 451
caad7637
JS
452void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
453{
85fa9d60
VZ
454 if ( m_combo )
455 m_combo->SetSize(GetClientSize());
caad7637
JS
456
457 event.Skip();
458}
459
460
7ae712f5 461void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
39df3acd
VZ
462{
463 ev.SetEventObject(this);
464 ev.SetId(GetId());
eafd76b0 465 GetParent()->GetEventHandler()->ProcessEvent(ev);
39df3acd
VZ
466
467 // We'll create an additional event if the date is valid.
1721a8c0 468 // If the date isn't valid, the user's probably in the middle of typing
39df3acd 469 wxDateTime dt;
b0fb790b 470 if ( !m_popup || !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
85fa9d60 471 return;
39df3acd 472
85fa9d60 473 m_popup->SendDateEvent(dt);
39df3acd
VZ
474}
475
476
4427c0a3
RR
477void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
478{
479 m_combo->SetFocus();
480}
481
482
7ae712f5 483#endif // wxUSE_DATEPICKCTRL
85fa9d60 484