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
)
373 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus
)
376 #ifndef wxHAS_NATIVE_DATEPICKCTRL
377 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
380 // ----------------------------------------------------------------------------
382 // ----------------------------------------------------------------------------
384 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
386 const wxDateTime
& date
,
390 const wxValidator
& validator
,
391 const wxString
& name
)
393 wxASSERT_MSG( !(style
& wxDP_SPIN
),
394 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
396 if ( !wxControl::Create(parent
, id
, pos
, size
,
397 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
405 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
406 wxDefaultPosition
, wxDefaultSize
);
408 m_combo
->SetCtrlMainWnd(this);
410 m_popup
= new wxCalendarComboPopup();
412 #if defined(__WXMSW__)
413 // without this keyboard navigation in month control doesn't work
414 m_combo
->UseAltPopupWindow();
416 m_combo
->SetPopupControl(m_popup
);
420 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
422 SetInitialSize(size
);
428 void wxDatePickerCtrlGeneric::Init()
435 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
439 bool wxDatePickerCtrlGeneric::Destroy()
448 return wxControl::Destroy();
451 // ----------------------------------------------------------------------------
452 // overridden base class methods
453 // ----------------------------------------------------------------------------
455 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
457 return m_combo
->GetBestSize();
460 // ----------------------------------------------------------------------------
461 // wxDatePickerCtrlGeneric API
462 // ----------------------------------------------------------------------------
465 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
466 const wxDateTime
& upperdate
)
468 return m_cal
->SetDateRange(lowerdate
, upperdate
);
472 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
474 return m_popup
->GetDateValue();
478 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
480 m_popup
->SetDateValue(date
);
484 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
487 *dt1
= m_cal
->GetLowerDateLimit();
489 *dt2
= m_cal
->GetUpperDateLimit();
495 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
497 m_cal
->SetDateRange(dt1
, dt2
);
500 // ----------------------------------------------------------------------------
502 // ----------------------------------------------------------------------------
505 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
508 m_combo
->SetSize(GetClientSize());
514 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
516 ev
.SetEventObject(this);
518 GetParent()->ProcessEvent(ev
);
520 // We'll create an additional event if the date is valid.
521 // If the date isn't valid, the user's probably in the middle of typing
523 if ( !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
526 m_popup
->SendDateEvent(dt
);
530 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent
& WXUNUSED(event
))
536 #endif // wxUSE_DATEPICKCTRL_GENERIC
538 #endif // wxUSE_DATEPICKCTRL