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"
32 #include "wx/textctrl.h"
33 #include "wx/valtext.h"
36 #include "wx/datectrl.h"
37 #include "wx/generic/datectrl.h"
40 // ----------------------------------------------------------------------------
42 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
47 // ----------------------------------------------------------------------------
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
54 class wxCalendarComboPopup
: public wxCalendarCtrl
,
59 wxCalendarComboPopup() : wxCalendarCtrl(),
68 // NB: Don't create lazily since it didn't work that way before
69 // wxComboCtrl was used, and changing behaviour would almost
70 // certainly introduce new bugs.
71 virtual bool Create(wxWindow
* parent
)
73 if ( !wxCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
74 wxPoint(0, 0), wxDefaultSize
,
75 wxCAL_SEQUENTIAL_MONTH_SELECTION
76 | wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
79 SetFormat(GetLocaleDateFormat());
81 m_useSize
= wxCalendarCtrl::GetBestSize();
83 wxWindow
* tx
= m_combo
->GetTextCtrl();
87 tx
->Connect(wxEVT_KILL_FOCUS
,
88 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
94 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
95 int WXUNUSED(prefHeight
),
96 int WXUNUSED(maxHeight
))
101 virtual wxWindow
*GetControl() { return this; }
103 void SetDateValue(const wxDateTime
& date
)
105 if ( date
.IsValid() )
107 m_combo
->SetText(date
.Format(m_format
));
112 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
113 wxT("this control must have a valid date") );
115 m_combo
->SetText(wxEmptyString
);
119 bool IsTextEmpty() const
121 return m_combo
->GetTextCtrl()->IsEmpty();
124 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
130 pDt
->ParseFormat(s
.c_str(), m_format
);
131 if ( !pDt
->IsValid() )
138 void SendDateEvent(const wxDateTime
& dt
)
140 // Sends both wxCalendarEvent and wxDateEvent
141 wxWindow
* datePicker
= m_combo
->GetParent();
143 wxCalendarEvent
cev(datePicker
, dt
, wxEVT_CALENDAR_SEL_CHANGED
);
144 datePicker
->GetEventHandler()->ProcessEvent(cev
);
146 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
147 datePicker
->GetEventHandler()->ProcessEvent(event
);
152 void OnCalKey(wxKeyEvent
& ev
)
154 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
160 void OnSelChange(wxCalendarEvent
&ev
)
162 m_combo
->SetText(GetDate().Format(m_format
));
164 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
169 SendDateEvent(GetDate());
172 void OnKillTextFocus(wxFocusEvent
&ev
)
176 const wxDateTime
& dtOld
= GetDate();
179 wxString value
= m_combo
->GetValue();
180 if ( !ParseDateTime(value
, &dt
) )
182 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
186 m_combo
->SetText(GetStringValueFor(dt
));
188 if ( !dt
.IsValid() && HasDPFlag(wxDP_ALLOWNONE
) )
191 // notify that we had to change the date after validation
192 if ( (dt
.IsValid() && (!dtOld
.IsValid() || dt
!= dtOld
)) ||
193 (!dt
.IsValid() && dtOld
.IsValid()) )
200 bool HasDPFlag(int flag
) const
202 return m_combo
->GetParent()->HasFlag(flag
);
205 // it expands "%x" format and changes %y to %Y if wxDP_SHOWCENTURY flag
206 // is given. If the locale format can't be easily analyzed (e.g. when
207 // the month is given as a name, not number), "%x" is returned
208 wxString
GetLocaleDateFormat() const
210 wxString
x_format(wxT("%x"));
212 int year_cnt
= 0, month_cnt
= 0, day_cnt
= 0;
215 dt
.ParseFormat(wxT("2003-10-17"), wxT("%Y-%m-%d"));
216 wxString
str(dt
.Format(x_format
));
218 const wxChar
*p
= str
.c_str();
224 if (n
== dt
.GetDay())
226 fmt
.Append(wxT("%d"));
230 else if (n
== (int)dt
.GetMonth()+1)
232 fmt
.Append(wxT("%m"));
236 else if (n
== dt
.GetYear())
238 fmt
.Append(wxT("%Y"));
242 else if (n
== (dt
.GetYear() % 100))
244 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
245 fmt
.Append(wxT("%Y"));
247 fmt
.Append(wxT("%y"));
252 // this shouldn't happen
261 if (year_cnt
== 1 && month_cnt
== 1 && day_cnt
== 1)
267 bool SetFormat(const wxString
& fmt
)
273 wxArrayString allowedChars
;
274 for ( wxChar c
= wxT('0'); c
<= wxT('9'); c
++ )
275 allowedChars
.Add(wxString(c
, 1));
277 const wxChar
*p2
= m_format
.c_str();
283 allowedChars
.Add(wxString(*p2
++, 1));
287 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
288 tv
.SetIncludes(allowedChars
);
289 m_combo
->SetValidator(tv
);
292 if ( GetDate().IsValid() )
293 m_combo
->SetText(GetDate().Format(m_format
));
299 virtual void SetStringValue(const wxString
& s
)
302 if ( !s
.empty() && ParseDateTime(s
, &dt
) )
304 //else: keep the old value
307 virtual wxString
GetStringValue() const
309 return GetStringValueFor(GetDate());
313 // returns either the given date representation using the current format or
314 // an empty string if it's invalid
315 wxString
GetStringValueFor(const wxDateTime
& dt
) const
319 val
= dt
.Format(m_format
);
327 DECLARE_EVENT_TABLE()
331 BEGIN_EVENT_TABLE(wxCalendarComboPopup
, wxCalendarCtrl
)
332 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey
)
333 EVT_CALENDAR_SEL_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
334 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
335 EVT_CALENDAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
339 // ============================================================================
340 // wxDatePickerCtrlGeneric implementation
341 // ============================================================================
343 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
344 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
345 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
346 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus
)
349 #ifndef wxHAS_NATIVE_DATEPICKCTRL
350 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
353 // ----------------------------------------------------------------------------
355 // ----------------------------------------------------------------------------
357 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
359 const wxDateTime
& date
,
363 const wxValidator
& validator
,
364 const wxString
& name
)
366 wxASSERT_MSG( !(style
& wxDP_SPIN
),
367 wxT("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
369 if ( !wxControl::Create(parent
, id
, pos
, size
,
370 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
378 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
379 wxDefaultPosition
, wxDefaultSize
);
381 m_combo
->SetCtrlMainWnd(this);
383 m_popup
= new wxCalendarComboPopup();
385 #if defined(__WXMSW__)
386 // without this keyboard navigation in month control doesn't work
387 m_combo
->UseAltPopupWindow();
389 m_combo
->SetPopupControl(m_popup
);
391 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
393 SetInitialSize(size
);
399 void wxDatePickerCtrlGeneric::Init()
405 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
409 bool wxDatePickerCtrlGeneric::Destroy()
417 return wxControl::Destroy();
420 // ----------------------------------------------------------------------------
421 // overridden base class methods
422 // ----------------------------------------------------------------------------
424 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
426 return m_combo
->GetBestSize();
429 // ----------------------------------------------------------------------------
430 // wxDatePickerCtrlGeneric API
431 // ----------------------------------------------------------------------------
434 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
435 const wxDateTime
& upperdate
)
437 return m_popup
->SetDateRange(lowerdate
, upperdate
);
441 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
443 if ( HasFlag(wxDP_ALLOWNONE
) && m_popup
->IsTextEmpty() )
444 return wxInvalidDateTime
;
445 return m_popup
->GetDate();
449 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
451 m_popup
->SetDateValue(date
);
455 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
457 return m_popup
->GetDateRange(dt1
, dt2
);
462 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
464 m_popup
->SetDateRange(dt1
, dt2
);
467 wxCalendarCtrl
*wxDatePickerCtrlGeneric::GetCalendar() const
472 // ----------------------------------------------------------------------------
474 // ----------------------------------------------------------------------------
477 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
480 m_combo
->SetSize(GetClientSize());
486 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
488 ev
.SetEventObject(this);
490 GetParent()->GetEventHandler()->ProcessEvent(ev
);
492 // We'll create an additional event if the date is valid.
493 // If the date isn't valid, the user's probably in the middle of typing
495 if ( !m_popup
|| !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
498 m_popup
->SendDateEvent(dt
);
502 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent
& WXUNUSED(event
))
508 #endif // wxUSE_DATEPICKCTRL