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
29 #include "wx/dialog.h"
30 #include "wx/dcmemory.h"
33 #include "wx/textctrl.h"
34 #include "wx/valtext.h"
37 #include "wx/calctrl.h"
40 #include "wx/datectrl.h"
41 #include "wx/generic/datectrl.h"
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
48 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 // ----------------------------------------------------------------------------
57 class wxCalendarComboPopup
: public wxCalendarCtrl
,
62 wxCalendarComboPopup() : wxCalendarCtrl(),
71 // NB: Don't create lazily since it didn't work that way before
72 // wxComboCtrl was used, and changing behaviour would almost
73 // certainly introduce new bugs.
74 virtual bool Create(wxWindow
* parent
)
76 if ( !wxCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
77 wxPoint(0, 0), wxDefaultSize
,
78 wxCAL_SEQUENTIAL_MONTH_SELECTION
79 | wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
82 SetFormat(GetLocaleDateFormat());
84 m_useSize
= wxCalendarCtrl::GetBestSize();
86 wxWindow
* tx
= m_combo
->GetTextCtrl();
90 tx
->Connect(wxEVT_KILL_FOCUS
,
91 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
97 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
98 int WXUNUSED(prefHeight
),
99 int WXUNUSED(maxHeight
))
104 virtual wxWindow
*GetControl() { return this; }
106 void SetDateValue(const wxDateTime
& date
)
108 if ( date
.IsValid() )
110 m_combo
->SetText(date
.Format(m_format
));
115 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
116 wxT("this control must have a valid date") );
118 m_combo
->SetText(wxEmptyString
);
122 bool IsTextEmpty() const
124 return m_combo
->GetTextCtrl()->IsEmpty();
127 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
133 pDt
->ParseFormat(s
, m_format
);
134 if ( !pDt
->IsValid() )
141 void SendDateEvent(const wxDateTime
& dt
)
143 // Sends both wxCalendarEvent and wxDateEvent
144 wxWindow
* datePicker
= m_combo
->GetParent();
146 wxCalendarEvent
cev(datePicker
, dt
, wxEVT_CALENDAR_SEL_CHANGED
);
147 datePicker
->GetEventHandler()->ProcessEvent(cev
);
149 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
150 datePicker
->GetEventHandler()->ProcessEvent(event
);
155 void OnCalKey(wxKeyEvent
& ev
)
157 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
163 void OnSelChange(wxCalendarEvent
&ev
)
165 m_combo
->SetText(GetDate().Format(m_format
));
167 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
172 SendDateEvent(GetDate());
175 void OnKillTextFocus(wxFocusEvent
&ev
)
179 const wxDateTime
& dtOld
= GetDate();
182 wxString value
= m_combo
->GetValue();
183 if ( !ParseDateTime(value
, &dt
) )
185 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
189 m_combo
->SetText(GetStringValueFor(dt
));
191 if ( !dt
.IsValid() && HasDPFlag(wxDP_ALLOWNONE
) )
194 // notify that we had to change the date after validation
195 if ( (dt
.IsValid() && (!dtOld
.IsValid() || dt
!= dtOld
)) ||
196 (!dt
.IsValid() && dtOld
.IsValid()) )
203 bool HasDPFlag(int flag
) const
205 return m_combo
->GetParent()->HasFlag(flag
);
208 // Return the format to be used for the dates shown by the control. This
209 // functions honours wxDP_SHOWCENTURY flag.
210 wxString
GetLocaleDateFormat() const
213 wxString fmt
= wxLocale::GetInfo(wxLOCALE_SHORT_DATE_FMT
);
214 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
215 fmt
.Replace("%y", "%Y");
220 #endif // wxUSE_INTL/!wxUSE_INTL
223 bool SetFormat(const wxString
& fmt
)
229 wxArrayString allowedChars
;
230 for ( wxChar c
= wxT('0'); c
<= wxT('9'); c
++ )
231 allowedChars
.Add(wxString(c
, 1));
233 const wxChar
*p2
= m_format
.c_str();
239 allowedChars
.Add(wxString(*p2
++, 1));
243 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
244 tv
.SetIncludes(allowedChars
);
245 m_combo
->SetValidator(tv
);
248 if ( GetDate().IsValid() )
249 m_combo
->SetText(GetDate().Format(m_format
));
255 virtual void SetStringValue(const wxString
& s
)
258 if ( !s
.empty() && ParseDateTime(s
, &dt
) )
260 //else: keep the old value
263 virtual wxString
GetStringValue() const
265 return GetStringValueFor(GetDate());
269 // returns either the given date representation using the current format or
270 // an empty string if it's invalid
271 wxString
GetStringValueFor(const wxDateTime
& dt
) const
275 val
= dt
.Format(m_format
);
283 DECLARE_EVENT_TABLE()
287 BEGIN_EVENT_TABLE(wxCalendarComboPopup
, wxCalendarCtrl
)
288 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey
)
289 EVT_CALENDAR_SEL_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
290 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
291 EVT_CALENDAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
295 // ============================================================================
296 // wxDatePickerCtrlGeneric implementation
297 // ============================================================================
299 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
300 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
301 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
302 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus
)
305 #ifndef wxHAS_NATIVE_DATEPICKCTRL
306 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
309 // ----------------------------------------------------------------------------
311 // ----------------------------------------------------------------------------
313 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
315 const wxDateTime
& date
,
319 const wxValidator
& validator
,
320 const wxString
& name
)
322 wxASSERT_MSG( !(style
& wxDP_SPIN
),
323 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
325 if ( !wxControl::Create(parent
, id
, pos
, size
,
326 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
334 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
335 wxDefaultPosition
, wxDefaultSize
);
337 m_combo
->SetCtrlMainWnd(this);
339 m_popup
= new wxCalendarComboPopup();
341 #if defined(__WXMSW__)
342 // without this keyboard navigation in month control doesn't work
343 m_combo
->UseAltPopupWindow();
345 m_combo
->SetPopupControl(m_popup
);
347 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
349 SetInitialSize(size
);
355 void wxDatePickerCtrlGeneric::Init()
361 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
365 bool wxDatePickerCtrlGeneric::Destroy()
373 return wxControl::Destroy();
376 // ----------------------------------------------------------------------------
377 // overridden base class methods
378 // ----------------------------------------------------------------------------
380 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
382 // A better solution would be to use a custom text control that would have
383 // the best size determined by the current date format and let m_combo take
384 // care of the best size computation, but this isn't easily possible with
385 // wxComboCtrl currently, so we compute our own best size here instead even
386 // if this means adding some extra margins to account for text control
387 // borders, space between it and the button and so on.
388 wxSize size
= m_combo
->GetButtonSize();
390 wxTextCtrl
* const text
= m_combo
->GetTextCtrl();
391 size
.x
+= text
->GetTextExtent(text
->GetValue()).x
;
392 size
.x
+= 2*text
->GetCharWidth(); // This is the margin mentioned above.
397 wxWindowList
wxDatePickerCtrlGeneric::GetCompositeWindowParts() const
400 parts
.push_back(m_combo
);
401 parts
.push_back(m_popup
);
405 // ----------------------------------------------------------------------------
406 // wxDatePickerCtrlGeneric API
407 // ----------------------------------------------------------------------------
410 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
411 const wxDateTime
& upperdate
)
413 return m_popup
->SetDateRange(lowerdate
, upperdate
);
417 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
419 if ( HasFlag(wxDP_ALLOWNONE
) && m_popup
->IsTextEmpty() )
420 return wxInvalidDateTime
;
421 return m_popup
->GetDate();
425 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
427 m_popup
->SetDateValue(date
);
431 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
433 return m_popup
->GetDateRange(dt1
, dt2
);
438 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
440 m_popup
->SetDateRange(dt1
, dt2
);
443 wxCalendarCtrl
*wxDatePickerCtrlGeneric::GetCalendar() const
448 // ----------------------------------------------------------------------------
450 // ----------------------------------------------------------------------------
453 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
456 m_combo
->SetSize(GetClientSize());
462 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
464 ev
.SetEventObject(this);
466 GetParent()->GetEventHandler()->ProcessEvent(ev
);
468 // We'll create an additional event if the date is valid.
469 // If the date isn't valid, the user's probably in the middle of typing
471 if ( !m_popup
|| !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
474 m_popup
->SendDateEvent(dt
);
478 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent
& WXUNUSED(event
))
484 #endif // wxUSE_DATEPICKCTRL