]> git.saurik.com Git - wxWidgets.git/blame - src/generic/datectlg.cpp
include 'Version' in version string, it's what Apple apps do
[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
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
7ae712f5
VZ
26#if wxUSE_DATEPICKCTRL
27
15c86b39 28#include "wx/datectrl.h"
7ae712f5
VZ
29
30// use this version if we're explicitly requested to do it or if it's the only
31// one we have
2a5e6376
VZ
32#if !defined(wxHAS_NATIVE_DATEPICKCTRL) || \
33 (defined(wxUSE_DATEPICKCTRL_GENERIC) && wxUSE_DATEPICKCTRL_GENERIC)
7ae712f5 34
a962d4e0 35#ifndef WX_PRECOMP
a962d4e0
KH
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
1721a8c0
VZ
43#ifdef wxHAS_NATIVE_DATEPICKCTRL
44 // this header is not included from wx/datectrl.h if we have a native
45 // version, but we do need it here
46 #include "wx/generic/datectrl.h"
930fb29e
VZ
47#else
48 // we need to define _WX_DEFINE_DATE_EVENTS_ before including wx/dateevt.h to
49 // define the event types we use if we're the only date picker control version
50 // being compiled -- otherwise it's defined in the native version implementation
7ae712f5
VZ
51 #define _WX_DEFINE_DATE_EVENTS_
52#endif
53
91edf16c
VZ
54#include "wx/dateevt.h"
55
4b134bb2 56#include "wx/calctrl.h"
85fa9d60 57#include "wx/combo.h"
39df3acd
VZ
58
59// ----------------------------------------------------------------------------
60// constants
61// ----------------------------------------------------------------------------
62
caad7637 63#if defined(__WXMSW__)
930fb29e 64 #define CALBORDER 0
930fb29e 65#else
930fb29e 66 #define CALBORDER 4
930fb29e
VZ
67#endif
68
aa74ad5b
VZ
69// ----------------------------------------------------------------------------
70// global variables
71// ----------------------------------------------------------------------------
72
930fb29e 73
1721a8c0
VZ
74// ----------------------------------------------------------------------------
75// local classes
76// ----------------------------------------------------------------------------
77
85fa9d60
VZ
78class wxCalendarComboPopup : public wxCalendarCtrl,
79 public wxComboPopup
38511687
VZ
80{
81public:
38511687 82
85fa9d60
VZ
83 wxCalendarComboPopup() : wxCalendarCtrl(),
84 wxComboPopup()
caad7637 85 {
caad7637
JS
86 }
87
85fa9d60
VZ
88 virtual void Init()
89 {
90 }
caad7637 91
85fa9d60
VZ
92 // NB: Don't create lazily since it didn't work that way before
93 // wxComboCtrl was used, and changing behaviour would almost
94 // certainly introduce new bugs.
95 virtual bool Create(wxWindow* parent)
96 {
97 if ( !wxCalendarCtrl::Create(parent, wxID_ANY, wxDefaultDateTime,
98 wxPoint(0, 0), wxDefaultSize,
99 wxCAL_SHOW_HOLIDAYS | wxBORDER_SUNKEN) )
100 return false;
caad7637 101
85fa9d60 102 wxWindow *yearControl = wxCalendarCtrl::GetYearControl();
caad7637 103
85fa9d60
VZ
104 wxClientDC dc(yearControl);
105 dc.SetFont(yearControl->GetFont());
106 wxCoord width, dummy;
107 dc.GetTextExtent(wxT("2000"), &width, &dummy);
108 width += ConvertDialogToPixels(wxSize(20, 0)).x;
caad7637 109
85fa9d60
VZ
110 wxSize calSize = wxCalendarCtrl::GetBestSize();
111 wxSize yearSize = yearControl->GetSize();
112 yearSize.x = width;
caad7637 113
85fa9d60 114 wxPoint yearPosition = yearControl->GetPosition();
caad7637 115
330e8ab9 116 SetFormat("%x");
caad7637 117
85fa9d60
VZ
118 width = yearPosition.x + yearSize.x+2+CALBORDER/2;
119 if (width < calSize.x-4)
120 width = calSize.x-4;
caad7637 121
85fa9d60
VZ
122 int calPos = (width-calSize.x)/2;
123 if (calPos == -1)
124 {
125 calPos = 0;
126 width += 2;
127 }
128 wxCalendarCtrl::SetSize(calPos, 0, calSize.x, calSize.y);
129 yearControl->SetSize(width-yearSize.x-CALBORDER/2, yearPosition.y,
130 yearSize.x, yearSize.y);
131 wxCalendarCtrl::GetMonthControl()->Move(0, 0);
caad7637 132
85fa9d60
VZ
133 m_useSize.x = width+CALBORDER/2;
134 m_useSize.y = calSize.y-2+CALBORDER;
caad7637 135
85fa9d60
VZ
136 wxWindow* tx = m_combo->GetTextCtrl();
137 if ( !tx )
138 tx = m_combo;
caad7637 139
85fa9d60
VZ
140 tx->Connect(wxEVT_KILL_FOCUS,
141 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus),
142 NULL, this);
caad7637 143
85fa9d60
VZ
144 return true;
145 }
38511687 146
85fa9d60
VZ
147 virtual wxSize GetAdjustedSize(int WXUNUSED(minWidth),
148 int WXUNUSED(prefHeight),
149 int WXUNUSED(maxHeight))
150 {
151 return m_useSize;
152 }
38511687 153
85fa9d60 154 virtual wxWindow *GetControl() { return this; }
930fb29e 155
85fa9d60
VZ
156 void SetDateValue(const wxDateTime& date)
157 {
158 if ( date.IsValid() )
159 {
160 m_combo->SetText(date.Format(m_format));
161 }
162 else // invalid date
163 {
164 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE),
165 _T("this control must have a valid date") );
caad7637 166
85fa9d60
VZ
167 m_combo->SetText(wxEmptyString);
168 }
38511687 169
2fda1fe5 170 SetDate(date);
85fa9d60 171 }
94ab4d92 172
85fa9d60
VZ
173 bool ParseDateTime(const wxString& s, wxDateTime* pDt)
174 {
175 wxASSERT(pDt);
38511687 176
85fa9d60
VZ
177 if ( !s.empty() )
178 {
e0a050e3 179 pDt->ParseFormat(s.c_str(), m_format);
85fa9d60
VZ
180 if ( !pDt->IsValid() )
181 return false;
182 }
38511687 183
85fa9d60
VZ
184 return true;
185 }
38511687 186
85fa9d60
VZ
187 void SendDateEvent(const wxDateTime& dt)
188 {
189 //
190 // Sends both wxCalendarEvent and wxDateEvent
191 wxWindow* datePicker = m_combo->GetParent();
38511687 192
85fa9d60
VZ
193 wxCalendarEvent cev((wxCalendarCtrl*) this, wxEVT_CALENDAR_SEL_CHANGED);
194 cev.SetEventObject(datePicker);
195 cev.SetId(datePicker->GetId());
196 cev.SetDate(dt);
d1b736b7 197 datePicker->GetEventHandler()->ProcessEvent(cev);
38511687 198
85fa9d60 199 wxDateEvent event(datePicker, dt, wxEVT_DATE_CHANGED);
d1b736b7 200 datePicker->GetEventHandler()->ProcessEvent(event);
85fa9d60 201 }
caad7637 202
85fa9d60 203private:
caad7637 204
85fa9d60 205 void OnCalKey(wxKeyEvent & ev)
caad7637 206 {
85fa9d60
VZ
207 if (ev.GetKeyCode() == WXK_ESCAPE && !ev.HasModifiers())
208 Dismiss();
209 else
210 ev.Skip();
caad7637 211 }
caad7637 212
85fa9d60
VZ
213 void OnSelChange(wxCalendarEvent &ev)
214 {
2fda1fe5 215 m_combo->SetText(GetDate().Format(m_format));
caad7637 216
85fa9d60
VZ
217 if ( ev.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED )
218 {
219 Dismiss();
220 }
caad7637 221
2fda1fe5 222 SendDateEvent(GetDate());
38511687
VZ
223 }
224
85fa9d60
VZ
225 void OnKillTextFocus(wxFocusEvent &ev)
226 {
227 ev.Skip();
caad7637 228
2fda1fe5
VZ
229 const wxDateTime& dtOld = GetDate();
230
85fa9d60
VZ
231 wxDateTime dt;
232 wxString value = m_combo->GetValue();
233 if ( !ParseDateTime(value, &dt) )
234 {
235 if ( !HasDPFlag(wxDP_ALLOWNONE) )
2fda1fe5 236 dt = dtOld;
85fa9d60 237 }
caad7637 238
2fda1fe5 239 m_combo->SetText(GetStringValueFor(dt));
38511687 240
85fa9d60 241 // notify that we had to change the date after validation
2fda1fe5
VZ
242 if ( (dt.IsValid() && (!dtOld.IsValid() || dt != dtOld)) ||
243 (!dt.IsValid() && dtOld.IsValid()) )
85fa9d60 244 {
2fda1fe5 245 SetDate(dt);
85fa9d60
VZ
246 SendDateEvent(dt);
247 }
248 }
38511687 249
85fa9d60 250 bool HasDPFlag(int flag)
caad7637 251 {
85fa9d60 252 return m_combo->GetParent()->HasFlag(flag);
caad7637 253 }
caad7637 254
330e8ab9 255 bool SetFormat(const wxString& fmt)
85fa9d60
VZ
256 {
257 m_format.clear();
caad7637 258
85fa9d60
VZ
259 wxDateTime dt;
260 dt.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
261 wxString str(dt.Format(fmt));
1721a8c0 262
85fa9d60
VZ
263 const wxChar *p = str.c_str();
264 while ( *p )
265 {
266 int n=wxAtoi(p);
267 if (n == dt.GetDay())
268 {
269 m_format.Append(wxT("%d"));
270 p += 2;
271 }
272 else if (n == (int)dt.GetMonth()+1)
273 {
274 m_format.Append(wxT("%m"));
275 p += 2;
276 }
277 else if (n == dt.GetYear())
278 {
279 m_format.Append(wxT("%Y"));
280 p += 4;
281 }
282 else if (n == (dt.GetYear() % 100))
283 {
284 if ( HasDPFlag(wxDP_SHOWCENTURY) )
285 m_format.Append(wxT("%Y"));
286 else
287 m_format.Append(wxT("%y"));
288 p += 2;
289 }
290 else
291 m_format.Append(*p++);
292 }
1721a8c0 293
85fa9d60
VZ
294 if ( m_combo )
295 {
296 wxArrayString allowedChars;
297 for ( wxChar c = _T('0'); c <= _T('9'); c++ )
298 allowedChars.Add(wxString(c, 1));
299
300 const wxChar *p2 = m_format.c_str();
301 while ( *p2 )
302 {
303 if ( *p2 == '%')
304 p2 += 2;
305 else
306 allowedChars.Add(wxString(*p2++, 1));
307 }
308
309 #if wxUSE_VALIDATORS
310 wxTextValidator tv(wxFILTER_INCLUDE_CHAR_LIST);
311 tv.SetIncludes(allowedChars);
312 m_combo->SetValidator(tv);
313 #endif
314
2fda1fe5
VZ
315 if ( GetDate().IsValid() )
316 m_combo->SetText(GetDate().Format(m_format));
85fa9d60 317 }
1721a8c0 318
85fa9d60 319 return true;
1721a8c0
VZ
320 }
321
85fa9d60 322 virtual void SetStringValue(const wxString& s)
1721a8c0 323 {
85fa9d60
VZ
324 wxDateTime dt;
325 if ( ParseDateTime(s, &dt) )
2fda1fe5 326 SetDate(dt);
85fa9d60 327 else if ( HasDPFlag(wxDP_ALLOWNONE) )
2fda1fe5
VZ
328 SetDate(wxInvalidDateTime);
329 //else: !wxDP_ALLOWNONE, keep the old value
1721a8c0 330 }
1721a8c0 331
85fa9d60 332 virtual wxString GetStringValue() const
1721a8c0 333 {
2fda1fe5 334 return GetStringValueFor(GetDate());
1721a8c0
VZ
335 }
336
85fa9d60 337private:
2fda1fe5
VZ
338 // returns either the given date representation using the current format or
339 // an empty string if it's invalid
340 wxString GetStringValueFor(const wxDateTime& dt) const
341 {
342 wxString val;
343 if ( dt.IsValid() )
344 val = dt.Format(m_format);
345
346 return val;
347 }
85fa9d60
VZ
348
349 wxSize m_useSize;
350 wxString m_format;
85fa9d60
VZ
351
352 DECLARE_EVENT_TABLE()
1721a8c0
VZ
353};
354
85fa9d60
VZ
355
356BEGIN_EVENT_TABLE(wxCalendarComboPopup, wxCalendarCtrl)
357 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey)
358 EVT_CALENDAR_SEL_CHANGED(wxID_ANY, wxCalendarComboPopup::OnSelChange)
359 EVT_CALENDAR_DAY(wxID_ANY, wxCalendarComboPopup::OnSelChange)
360 EVT_CALENDAR_MONTH(wxID_ANY, wxCalendarComboPopup::OnSelChange)
361 EVT_CALENDAR_YEAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
362 EVT_CALENDAR(wxID_ANY, wxCalendarComboPopup::OnSelChange)
363END_EVENT_TABLE()
364
1721a8c0 365
39df3acd 366// ============================================================================
7ae712f5 367// wxDatePickerCtrlGeneric implementation
39df3acd
VZ
368// ============================================================================
369
7ae712f5 370BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric, wxDatePickerCtrlBase)
85fa9d60 371 EVT_TEXT(wxID_ANY, wxDatePickerCtrlGeneric::OnText)
caad7637 372 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize)
4427c0a3 373 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus)
39df3acd
VZ
374END_EVENT_TABLE()
375
b6292ff0 376#ifndef wxHAS_NATIVE_DATEPICKCTRL
94ab4d92 377 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
b6292ff0 378#endif
39df3acd
VZ
379
380// ----------------------------------------------------------------------------
381// creation
382// ----------------------------------------------------------------------------
383
7ae712f5
VZ
384bool wxDatePickerCtrlGeneric::Create(wxWindow *parent,
385 wxWindowID id,
386 const wxDateTime& date,
387 const wxPoint& pos,
388 const wxSize& size,
389 long style,
807f5038 390 const wxValidator& validator,
7ae712f5 391 const wxString& name)
39df3acd 392{
29c86948
VZ
393 wxASSERT_MSG( !(style & wxDP_SPIN),
394 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
395
39df3acd 396 if ( !wxControl::Create(parent, id, pos, size,
85fa9d60 397 style | wxCLIP_CHILDREN | wxWANTS_CHARS | wxBORDER_NONE,
807f5038 398 validator, name) )
39df3acd
VZ
399 {
400 return false;
401 }
402
39df3acd
VZ
403 InheritAttributes();
404
85fa9d60
VZ
405 m_combo = new wxComboCtrl(this, -1, wxEmptyString,
406 wxDefaultPosition, wxDefaultSize);
39df3acd 407
4427c0a3
RR
408 m_combo->SetCtrlMainWnd(this);
409
85fa9d60 410 m_popup = new wxCalendarComboPopup();
39df3acd 411
06077aaf
VZ
412#if defined(__WXMSW__)
413 // without this keyboard navigation in month control doesn't work
414 m_combo->UseAltPopupWindow();
415#endif
85fa9d60 416 m_combo->SetPopupControl(m_popup);
39df3acd 417
85fa9d60 418 m_popup->SetDateValue(date.IsValid() ? date : wxDateTime::Today());
1721a8c0 419
170acdc9 420 SetInitialSize(size);
930fb29e 421
3a0c6181 422 return true;
39df3acd
VZ
423}
424
425
7ae712f5 426void wxDatePickerCtrlGeneric::Init()
39df3acd 427{
85fa9d60 428 m_combo = NULL;
85fa9d60 429 m_popup = NULL;
39df3acd
VZ
430}
431
a9d13e15
JS
432wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
433{
a9d13e15 434}
39df3acd 435
7ae712f5 436bool wxDatePickerCtrlGeneric::Destroy()
39df3acd 437{
85fa9d60
VZ
438 if ( m_combo )
439 m_combo->Destroy();
39df3acd 440
85fa9d60 441 m_combo = NULL;
85fa9d60 442 m_popup = NULL;
39df3acd
VZ
443
444 return wxControl::Destroy();
445}
446
447// ----------------------------------------------------------------------------
448// overridden base class methods
449// ----------------------------------------------------------------------------
450
7ae712f5 451wxSize wxDatePickerCtrlGeneric::DoGetBestSize() const
39df3acd 452{
85fa9d60 453 return m_combo->GetBestSize();
39df3acd
VZ
454}
455
456// ----------------------------------------------------------------------------
7ae712f5 457// wxDatePickerCtrlGeneric API
39df3acd
VZ
458// ----------------------------------------------------------------------------
459
4b134bb2 460bool
7ae712f5
VZ
461wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime& lowerdate,
462 const wxDateTime& upperdate)
4b134bb2 463{
2fda1fe5 464 return m_popup->SetDateRange(lowerdate, upperdate);
4b134bb2
VZ
465}
466
39df3acd 467
7ae712f5 468wxDateTime wxDatePickerCtrlGeneric::GetValue() const
39df3acd 469{
2fda1fe5 470 return m_popup->GetDate();
39df3acd
VZ
471}
472
473
7ae712f5 474void wxDatePickerCtrlGeneric::SetValue(const wxDateTime& date)
39df3acd 475{
85fa9d60 476 m_popup->SetDateValue(date);
39df3acd
VZ
477}
478
479
7ae712f5 480bool wxDatePickerCtrlGeneric::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
39df3acd
VZ
481{
482 if (dt1)
2fda1fe5 483 *dt1 = m_popup->GetLowerDateLimit();
40a33c46 484 if (dt2)
2fda1fe5 485 *dt2 = m_popup->GetUpperDateLimit();
39df3acd
VZ
486 return true;
487}
488
489
7ae712f5
VZ
490void
491wxDatePickerCtrlGeneric::SetRange(const wxDateTime &dt1, const wxDateTime &dt2)
39df3acd 492{
2fda1fe5
VZ
493 m_popup->SetDateRange(dt1, dt2);
494}
495
496wxCalendarCtrl *wxDatePickerCtrlGeneric::GetCalendar() const
497{
498 return m_popup;
39df3acd
VZ
499}
500
501// ----------------------------------------------------------------------------
502// event handlers
503// ----------------------------------------------------------------------------
504
39df3acd 505
caad7637
JS
506void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent& event)
507{
85fa9d60
VZ
508 if ( m_combo )
509 m_combo->SetSize(GetClientSize());
caad7637
JS
510
511 event.Skip();
512}
513
514
7ae712f5 515void wxDatePickerCtrlGeneric::OnText(wxCommandEvent &ev)
39df3acd
VZ
516{
517 ev.SetEventObject(this);
518 ev.SetId(GetId());
eafd76b0 519 GetParent()->GetEventHandler()->ProcessEvent(ev);
39df3acd
VZ
520
521 // We'll create an additional event if the date is valid.
1721a8c0 522 // If the date isn't valid, the user's probably in the middle of typing
39df3acd 523 wxDateTime dt;
85fa9d60
VZ
524 if ( !m_popup->ParseDateTime(m_combo->GetValue(), &dt) )
525 return;
39df3acd 526
85fa9d60 527 m_popup->SendDateEvent(dt);
39df3acd
VZ
528}
529
530
4427c0a3
RR
531void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent& WXUNUSED(event))
532{
533 m_combo->SetFocus();
534}
535
536
7ae712f5
VZ
537#endif // wxUSE_DATEPICKCTRL_GENERIC
538
539#endif // wxUSE_DATEPICKCTRL
85fa9d60 540