1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datectlg.cpp
3 // Purpose: generic wxDatePickerCtrlGeneric implementation
4 // Author: Andreas Pflug
8 // Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
26 #if wxUSE_DATEPICKCTRL
28 #include "wx/datectrl.h"
30 // use this version if we're explicitly requested to do it or if it's the only
32 #if !defined(wxHAS_NATIVE_DATEPICKCTRL) || \
33 (defined(wxUSE_DATEPICKCTRL_GENERIC) && wxUSE_DATEPICKCTRL_GENERIC)
36 #include "wx/dialog.h"
37 #include "wx/dcmemory.h"
39 #include "wx/textctrl.h"
40 #include "wx/valtext.h"
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"
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
51 #define _WX_DEFINE_DATE_EVENTS_
54 #include "wx/dateevt.h"
56 #include "wx/calctrl.h"
59 // ----------------------------------------------------------------------------
61 // ----------------------------------------------------------------------------
63 #if defined(__WXMSW__)
69 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
74 // ----------------------------------------------------------------------------
76 // ----------------------------------------------------------------------------
78 class wxCalendarComboPopup
: public wxCalendarCtrl
,
83 wxCalendarComboPopup() : wxCalendarCtrl(),
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
)
97 if ( !wxCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
98 wxPoint(0, 0), wxDefaultSize
,
99 wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
102 wxWindow
*yearControl
= wxCalendarCtrl::GetYearControl();
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
;
110 wxSize calSize
= wxCalendarCtrl::GetBestSize();
111 wxSize yearSize
= yearControl
->GetSize();
114 wxPoint yearPosition
= yearControl
->GetPosition();
116 SetFormat(wxT("%x"));
118 width
= yearPosition
.x
+ yearSize
.x
+2+CALBORDER
/2;
119 if (width
< calSize
.x
-4)
122 int calPos
= (width
-calSize
.x
)/2;
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);
133 m_useSize
.x
= width
+CALBORDER
/2;
134 m_useSize
.y
= calSize
.y
-2+CALBORDER
;
136 wxWindow
* tx
= m_combo
->GetTextCtrl();
140 tx
->Connect(wxEVT_KILL_FOCUS
,
141 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
147 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
148 int WXUNUSED(prefHeight
),
149 int WXUNUSED(maxHeight
))
154 virtual wxWindow
*GetControl() { return this; }
156 void SetDateValue(const wxDateTime
& date
)
158 if ( date
.IsValid() )
160 m_combo
->SetText(date
.Format(m_format
));
164 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
165 _T("this control must have a valid date") );
167 m_combo
->SetText(wxEmptyString
);
170 m_currentDate
= date
;
173 const wxDateTime
& GetDateValue() const
175 return m_currentDate
;
178 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
184 pDt
->ParseFormat(s
, m_format
);
185 if ( !pDt
->IsValid() )
192 void SendDateEvent(const wxDateTime
& dt
)
195 // Sends both wxCalendarEvent and wxDateEvent
196 wxWindow
* datePicker
= m_combo
->GetParent();
198 wxCalendarEvent
cev((wxCalendarCtrl
*) this, wxEVT_CALENDAR_SEL_CHANGED
);
199 cev
.SetEventObject(datePicker
);
200 cev
.SetId(datePicker
->GetId());
202 GetParent()->ProcessEvent(cev
);
204 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
205 datePicker
->GetParent()->ProcessEvent(event
);
210 void OnCalKey(wxKeyEvent
& ev
)
212 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
218 void OnSelChange(wxCalendarEvent
&ev
)
220 m_currentDate
= wxCalendarCtrl::GetDate();
221 m_combo
->SetText(m_currentDate
.Format(m_format
));
223 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
228 SendDateEvent(m_currentDate
);
231 void OnKillTextFocus(wxFocusEvent
&ev
)
236 wxString value
= m_combo
->GetValue();
237 if ( !ParseDateTime(value
, &dt
) )
239 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
244 m_combo
->SetText(dt
.Format(m_format
));
246 m_combo
->SetText(wxEmptyString
);
248 // notify that we had to change the date after validation
249 if ( (dt
.IsValid() && (!m_currentDate
.IsValid() || m_currentDate
!= dt
)) ||
250 (!dt
.IsValid() && m_currentDate
.IsValid()) )
257 bool HasDPFlag(int flag
)
259 return m_combo
->GetParent()->HasFlag(flag
);
262 bool SetFormat(const wxChar
*fmt
)
267 dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
268 wxString
str(dt
.Format(fmt
));
270 const wxChar
*p
= str
.c_str();
274 if (n
== dt
.GetDay())
276 m_format
.Append(wxT("%d"));
279 else if (n
== (int)dt
.GetMonth()+1)
281 m_format
.Append(wxT("%m"));
284 else if (n
== dt
.GetYear())
286 m_format
.Append(wxT("%Y"));
289 else if (n
== (dt
.GetYear() % 100))
291 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
292 m_format
.Append(wxT("%Y"));
294 m_format
.Append(wxT("%y"));
298 m_format
.Append(*p
++);
303 wxArrayString allowedChars
;
304 for ( wxChar c
= _T('0'); c
<= _T('9'); c
++ )
305 allowedChars
.Add(wxString(c
, 1));
307 const wxChar
*p2
= m_format
.c_str();
313 allowedChars
.Add(wxString(*p2
++, 1));
317 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
318 tv
.SetIncludes(allowedChars
);
319 m_combo
->SetValidator(tv
);
322 if (m_currentDate
.IsValid())
323 m_combo
->SetText(m_currentDate
.Format(m_format
));
329 virtual void SetStringValue(const wxString
& s
)
332 if ( ParseDateTime(s
, &dt
) )
334 else if ( HasDPFlag(wxDP_ALLOWNONE
) )
338 virtual wxString
GetStringValue() const
340 if ( !m_currentDate
.IsValid() )
341 return wxEmptyString
;
343 return m_currentDate
.Format(m_format
);
350 wxDateTime m_currentDate
;
352 DECLARE_EVENT_TABLE()
356 BEGIN_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
)
366 // ============================================================================
367 // wxDatePickerCtrlGeneric implementation
368 // ============================================================================
370 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
371 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
372 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
375 #ifndef wxHAS_NATIVE_DATEPICKCTRL
376 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
379 // ----------------------------------------------------------------------------
381 // ----------------------------------------------------------------------------
383 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
385 const wxDateTime
& date
,
389 const wxValidator
& validator
,
390 const wxString
& name
)
392 wxASSERT_MSG( !(style
& wxDP_SPIN
),
393 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
395 if ( !wxControl::Create(parent
, id
, pos
, size
,
396 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
404 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
405 wxDefaultPosition
, wxDefaultSize
);
407 m_popup
= new wxCalendarComboPopup();
409 #if defined(__WXMSW__)
410 // without this keyboard navigation in month control doesn't work
411 m_combo
->UseAltPopupWindow();
413 m_combo
->SetPopupControl(m_popup
);
417 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
419 SetBestFittingSize(size
);
425 void wxDatePickerCtrlGeneric::Init()
432 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
436 bool wxDatePickerCtrlGeneric::Destroy()
445 return wxControl::Destroy();
448 // ----------------------------------------------------------------------------
449 // overridden base class methods
450 // ----------------------------------------------------------------------------
452 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
454 return m_combo
->GetBestSize();
457 // ----------------------------------------------------------------------------
458 // wxDatePickerCtrlGeneric API
459 // ----------------------------------------------------------------------------
462 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
463 const wxDateTime
& upperdate
)
465 return m_cal
->SetDateRange(lowerdate
, upperdate
);
469 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
471 return m_popup
->GetDateValue();
475 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
477 m_popup
->SetDateValue(date
);
481 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
484 *dt1
= m_cal
->GetLowerDateLimit();
486 *dt2
= m_cal
->GetUpperDateLimit();
492 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
494 m_cal
->SetDateRange(dt1
, dt2
);
497 // ----------------------------------------------------------------------------
499 // ----------------------------------------------------------------------------
502 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
505 m_combo
->SetSize(GetClientSize());
511 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
513 ev
.SetEventObject(this);
515 GetParent()->ProcessEvent(ev
);
517 // We'll create an additional event if the date is valid.
518 // If the date isn't valid, the user's probably in the middle of typing
520 if ( !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
523 m_popup
->SendDateEvent(dt
);
527 #endif // wxUSE_DATEPICKCTRL_GENERIC
529 #endif // wxUSE_DATEPICKCTRL