1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/generic/datectlg.cpp
3 // Purpose: generic wxDatePickerCtrlGeneric implementation
4 // Author: Andreas Pflug
7 // Copyright: (c) 2005 Andreas Pflug <pgadmin@pse-consulting.de>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
11 // ============================================================================
13 // ============================================================================
15 // ----------------------------------------------------------------------------
17 // ----------------------------------------------------------------------------
19 #include "wx/wxprec.h"
25 #if wxUSE_DATEPICKCTRL
28 #include "wx/dialog.h"
29 #include "wx/dcmemory.h"
32 #include "wx/textctrl.h"
33 #include "wx/valtext.h"
36 #include "wx/calctrl.h"
39 #include "wx/datectrl.h"
40 #include "wx/generic/datectrl.h"
42 // ----------------------------------------------------------------------------
44 // ----------------------------------------------------------------------------
47 // ----------------------------------------------------------------------------
49 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 // ----------------------------------------------------------------------------
56 class wxCalendarComboPopup
: public wxCalendarCtrl
,
61 wxCalendarComboPopup() : wxCalendarCtrl(),
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
)
75 if ( !wxCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
76 wxPoint(0, 0), wxDefaultSize
,
77 wxCAL_SEQUENTIAL_MONTH_SELECTION
78 | wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
81 SetFormat(GetLocaleDateFormat());
83 m_useSize
= wxCalendarCtrl::GetBestSize();
85 wxWindow
* tx
= m_combo
->GetTextCtrl();
89 tx
->Connect(wxEVT_KILL_FOCUS
,
90 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
96 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
97 int WXUNUSED(prefHeight
),
98 int WXUNUSED(maxHeight
))
103 virtual wxWindow
*GetControl() { return this; }
105 void SetDateValue(const wxDateTime
& date
)
107 if ( date
.IsValid() )
109 m_combo
->SetText(date
.Format(m_format
));
114 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
115 wxT("this control must have a valid date") );
117 m_combo
->SetText(wxEmptyString
);
121 bool IsTextEmpty() const
123 return m_combo
->GetTextCtrl()->IsEmpty();
126 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
132 pDt
->ParseFormat(s
, m_format
);
133 if ( !pDt
->IsValid() )
140 void SendDateEvent(const wxDateTime
& dt
)
142 // Sends both wxCalendarEvent and wxDateEvent
143 wxWindow
* datePicker
= m_combo
->GetParent();
145 wxCalendarEvent
cev(datePicker
, dt
, wxEVT_CALENDAR_SEL_CHANGED
);
146 datePicker
->GetEventHandler()->ProcessEvent(cev
);
148 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
149 datePicker
->GetEventHandler()->ProcessEvent(event
);
154 void OnCalKey(wxKeyEvent
& ev
)
156 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
162 void OnSelChange(wxCalendarEvent
&ev
)
164 m_combo
->SetText(GetDate().Format(m_format
));
166 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
171 SendDateEvent(GetDate());
174 void OnKillTextFocus(wxFocusEvent
&ev
)
178 const wxDateTime
& dtOld
= GetDate();
181 wxString value
= m_combo
->GetValue();
182 if ( !ParseDateTime(value
, &dt
) )
184 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
188 m_combo
->SetText(GetStringValueFor(dt
));
190 if ( !dt
.IsValid() && HasDPFlag(wxDP_ALLOWNONE
) )
193 // notify that we had to change the date after validation
194 if ( (dt
.IsValid() && (!dtOld
.IsValid() || dt
!= dtOld
)) ||
195 (!dt
.IsValid() && dtOld
.IsValid()) )
202 bool HasDPFlag(int flag
) const
204 return m_combo
->GetParent()->HasFlag(flag
);
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
212 wxString fmt
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
);
213 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
214 fmt
.Replace("%y", "%Y");
219 #endif // wxUSE_INTL/!wxUSE_INTL
222 bool SetFormat(const wxString
& fmt
)
228 wxArrayString allowedChars
;
229 for ( wxChar c
= wxT('0'); c
<= wxT('9'); c
++ )
230 allowedChars
.Add(wxString(c
, 1));
232 const wxChar
*p2
= m_format
.c_str();
238 allowedChars
.Add(wxString(*p2
++, 1));
242 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
243 tv
.SetIncludes(allowedChars
);
244 m_combo
->SetValidator(tv
);
247 if ( GetDate().IsValid() )
248 m_combo
->SetText(GetDate().Format(m_format
));
254 virtual void SetStringValue(const wxString
& s
)
257 if ( !s
.empty() && ParseDateTime(s
, &dt
) )
259 //else: keep the old value
262 virtual wxString
GetStringValue() const
264 return GetStringValueFor(GetDate());
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
274 val
= dt
.Format(m_format
);
282 DECLARE_EVENT_TABLE()
286 BEGIN_EVENT_TABLE(wxCalendarComboPopup
, wxCalendarCtrl
)
287 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey
)
288 EVT_CALENDAR_SEL_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
289 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
290 EVT_CALENDAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
294 // ============================================================================
295 // wxDatePickerCtrlGeneric implementation
296 // ============================================================================
298 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
299 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
300 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
301 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus
)
304 #ifndef wxHAS_NATIVE_DATEPICKCTRL
305 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
308 // ----------------------------------------------------------------------------
310 // ----------------------------------------------------------------------------
312 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
314 const wxDateTime
& date
,
318 const wxValidator
& validator
,
319 const wxString
& name
)
321 wxASSERT_MSG( !(style
& wxDP_SPIN
),
322 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
324 if ( !wxControl::Create(parent
, id
, pos
, size
,
325 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
333 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
334 wxDefaultPosition
, wxDefaultSize
);
336 m_combo
->SetCtrlMainWnd(this);
338 m_popup
= new wxCalendarComboPopup();
340 #if defined(__WXMSW__)
341 // without this keyboard navigation in month control doesn't work
342 m_combo
->UseAltPopupWindow();
344 m_combo
->SetPopupControl(m_popup
);
346 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
348 SetInitialSize(size
);
354 void wxDatePickerCtrlGeneric::Init()
360 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
364 bool wxDatePickerCtrlGeneric::Destroy()
372 return wxControl::Destroy();
375 // ----------------------------------------------------------------------------
376 // overridden base class methods
377 // ----------------------------------------------------------------------------
379 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
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();
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.
396 wxWindowList
wxDatePickerCtrlGeneric::GetCompositeWindowParts() const
399 parts
.push_back(m_combo
);
400 parts
.push_back(m_popup
);
404 // ----------------------------------------------------------------------------
405 // wxDatePickerCtrlGeneric API
406 // ----------------------------------------------------------------------------
409 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
410 const wxDateTime
& upperdate
)
412 return m_popup
->SetDateRange(lowerdate
, upperdate
);
416 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
418 if ( HasFlag(wxDP_ALLOWNONE
) && m_popup
->IsTextEmpty() )
419 return wxInvalidDateTime
;
420 return m_popup
->GetDate();
424 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
426 m_popup
->SetDateValue(date
);
430 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
432 return m_popup
->GetDateRange(dt1
, dt2
);
437 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
439 m_popup
->SetDateRange(dt1
, dt2
);
442 wxCalendarCtrl
*wxDatePickerCtrlGeneric::GetCalendar() const
447 // ----------------------------------------------------------------------------
449 // ----------------------------------------------------------------------------
452 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
455 m_combo
->SetSize(GetClientSize());
461 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
463 ev
.SetEventObject(this);
465 GetParent()->GetEventHandler()->ProcessEvent(ev
);
467 // We'll create an additional event if the date is valid.
468 // If the date isn't valid, the user's probably in the middle of typing
470 if ( !m_popup
|| !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
473 m_popup
->SendDateEvent(dt
);
477 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent
& WXUNUSED(event
))
483 #endif // wxUSE_DATEPICKCTRL