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"
49 #include "wx/dateevt.h"
51 #include "wx/calctrl.h"
52 #include "wx/generic/calctrlg.h"
55 // ----------------------------------------------------------------------------
57 // ----------------------------------------------------------------------------
59 #if defined(__WXMSW__)
65 // ----------------------------------------------------------------------------
67 // ----------------------------------------------------------------------------
70 // ----------------------------------------------------------------------------
72 // ----------------------------------------------------------------------------
74 class wxCalendarComboPopup
: public wxGenericCalendarCtrl
,
79 wxCalendarComboPopup() : wxGenericCalendarCtrl(),
88 // NB: Don't create lazily since it didn't work that way before
89 // wxComboCtrl was used, and changing behaviour would almost
90 // certainly introduce new bugs.
91 virtual bool Create(wxWindow
* parent
)
93 if ( !wxGenericCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
94 wxPoint(0, 0), wxDefaultSize
,
95 wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
98 wxWindow
*yearControl
= wxGenericCalendarCtrl::GetYearControl();
100 wxClientDC
dc(yearControl
);
101 dc
.SetFont(yearControl
->GetFont());
102 wxCoord width
, dummy
;
103 dc
.GetTextExtent(wxT("2000"), &width
, &dummy
);
104 width
+= ConvertDialogToPixels(wxSize(20, 0)).x
;
106 wxSize calSize
= wxGenericCalendarCtrl::GetBestSize();
107 wxSize yearSize
= yearControl
->GetSize();
110 wxPoint yearPosition
= yearControl
->GetPosition();
114 width
= yearPosition
.x
+ yearSize
.x
+2+CALBORDER
/2;
115 if (width
< calSize
.x
-4)
118 int calPos
= (width
-calSize
.x
)/2;
124 wxGenericCalendarCtrl::SetSize(calPos
, 0, calSize
.x
, calSize
.y
);
125 yearControl
->SetSize(width
-yearSize
.x
-CALBORDER
/2, yearPosition
.y
,
126 yearSize
.x
, yearSize
.y
);
127 wxGenericCalendarCtrl::GetMonthControl()->Move(0, 0);
129 m_useSize
.x
= width
+CALBORDER
/2;
130 m_useSize
.y
= calSize
.y
-2+CALBORDER
;
132 wxWindow
* tx
= m_combo
->GetTextCtrl();
136 tx
->Connect(wxEVT_KILL_FOCUS
,
137 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
143 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
144 int WXUNUSED(prefHeight
),
145 int WXUNUSED(maxHeight
))
150 virtual wxWindow
*GetControl() { return this; }
152 void SetDateValue(const wxDateTime
& date
)
154 if ( date
.IsValid() )
156 m_combo
->SetText(date
.Format(m_format
));
160 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
161 _T("this control must have a valid date") );
163 m_combo
->SetText(wxEmptyString
);
169 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
175 pDt
->ParseFormat(s
.c_str(), m_format
);
176 if ( !pDt
->IsValid() )
183 void SendDateEvent(const wxDateTime
& dt
)
185 // Sends both wxCalendarEvent and wxDateEvent
186 wxWindow
* datePicker
= m_combo
->GetParent();
188 wxCalendarEvent
cev(datePicker
, dt
, wxEVT_CALENDAR_SEL_CHANGED
);
189 datePicker
->GetEventHandler()->ProcessEvent(cev
);
191 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
192 datePicker
->GetEventHandler()->ProcessEvent(event
);
197 void OnCalKey(wxKeyEvent
& ev
)
199 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
205 void OnSelChange(wxCalendarEvent
&ev
)
207 m_combo
->SetText(GetDate().Format(m_format
));
209 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
214 SendDateEvent(GetDate());
217 void OnKillTextFocus(wxFocusEvent
&ev
)
221 const wxDateTime
& dtOld
= GetDate();
224 wxString value
= m_combo
->GetValue();
225 if ( !ParseDateTime(value
, &dt
) )
227 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
231 m_combo
->SetText(GetStringValueFor(dt
));
233 // notify that we had to change the date after validation
234 if ( (dt
.IsValid() && (!dtOld
.IsValid() || dt
!= dtOld
)) ||
235 (!dt
.IsValid() && dtOld
.IsValid()) )
242 bool HasDPFlag(int flag
)
244 return m_combo
->GetParent()->HasFlag(flag
);
247 bool SetFormat(const wxString
& fmt
)
252 dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
253 wxString
str(dt
.Format(fmt
));
255 const wxChar
*p
= str
.c_str();
259 if (n
== dt
.GetDay())
261 m_format
.Append(wxT("%d"));
264 else if (n
== (int)dt
.GetMonth()+1)
266 m_format
.Append(wxT("%m"));
269 else if (n
== dt
.GetYear())
271 m_format
.Append(wxT("%Y"));
274 else if (n
== (dt
.GetYear() % 100))
276 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
277 m_format
.Append(wxT("%Y"));
279 m_format
.Append(wxT("%y"));
283 m_format
.Append(*p
++);
288 wxArrayString allowedChars
;
289 for ( wxChar c
= _T('0'); c
<= _T('9'); c
++ )
290 allowedChars
.Add(wxString(c
, 1));
292 const wxChar
*p2
= m_format
.c_str();
298 allowedChars
.Add(wxString(*p2
++, 1));
302 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
303 tv
.SetIncludes(allowedChars
);
304 m_combo
->SetValidator(tv
);
307 if ( GetDate().IsValid() )
308 m_combo
->SetText(GetDate().Format(m_format
));
314 virtual void SetStringValue(const wxString
& s
)
317 if ( ParseDateTime(s
, &dt
) )
319 else if ( HasDPFlag(wxDP_ALLOWNONE
) )
320 SetDate(wxInvalidDateTime
);
321 //else: !wxDP_ALLOWNONE, keep the old value
324 virtual wxString
GetStringValue() const
326 return GetStringValueFor(GetDate());
330 // returns either the given date representation using the current format or
331 // an empty string if it's invalid
332 wxString
GetStringValueFor(const wxDateTime
& dt
) const
336 val
= dt
.Format(m_format
);
344 DECLARE_EVENT_TABLE()
348 BEGIN_EVENT_TABLE(wxCalendarComboPopup
, wxGenericCalendarCtrl
)
349 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey
)
350 EVT_CALENDAR_SEL_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
351 EVT_CALENDAR_PAGE_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
352 EVT_CALENDAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
356 // ============================================================================
357 // wxDatePickerCtrlGeneric implementation
358 // ============================================================================
360 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
361 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
362 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
363 EVT_SET_FOCUS(wxDatePickerCtrlGeneric::OnFocus
)
366 #ifndef wxHAS_NATIVE_DATEPICKCTRL
367 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
370 // ----------------------------------------------------------------------------
372 // ----------------------------------------------------------------------------
374 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
376 const wxDateTime
& date
,
380 const wxValidator
& validator
,
381 const wxString
& name
)
383 wxASSERT_MSG( !(style
& wxDP_SPIN
),
384 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
386 if ( !wxControl::Create(parent
, id
, pos
, size
,
387 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
395 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
396 wxDefaultPosition
, wxDefaultSize
);
398 m_combo
->SetCtrlMainWnd(this);
400 m_popup
= new wxCalendarComboPopup();
402 #if defined(__WXMSW__)
403 // without this keyboard navigation in month control doesn't work
404 m_combo
->UseAltPopupWindow();
406 m_combo
->SetPopupControl(m_popup
);
408 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
410 SetInitialSize(size
);
416 void wxDatePickerCtrlGeneric::Init()
422 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
426 bool wxDatePickerCtrlGeneric::Destroy()
434 return wxControl::Destroy();
437 // ----------------------------------------------------------------------------
438 // overridden base class methods
439 // ----------------------------------------------------------------------------
441 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
443 return m_combo
->GetBestSize();
446 // ----------------------------------------------------------------------------
447 // wxDatePickerCtrlGeneric API
448 // ----------------------------------------------------------------------------
451 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
452 const wxDateTime
& upperdate
)
454 return m_popup
->SetDateRange(lowerdate
, upperdate
);
458 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
460 return m_popup
->GetDate();
464 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
466 m_popup
->SetDateValue(date
);
470 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
473 *dt1
= m_popup
->GetLowerDateLimit();
475 *dt2
= m_popup
->GetUpperDateLimit();
481 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
483 m_popup
->SetDateRange(dt1
, dt2
);
486 wxGenericCalendarCtrl
*wxDatePickerCtrlGeneric::GetCalendar() const
491 // ----------------------------------------------------------------------------
493 // ----------------------------------------------------------------------------
496 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
499 m_combo
->SetSize(GetClientSize());
505 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
507 ev
.SetEventObject(this);
509 GetParent()->GetEventHandler()->ProcessEvent(ev
);
511 // We'll create an additional event if the date is valid.
512 // If the date isn't valid, the user's probably in the middle of typing
514 if ( !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
517 m_popup
->SendDateEvent(dt
);
521 void wxDatePickerCtrlGeneric::OnFocus(wxFocusEvent
& WXUNUSED(event
))
527 #endif // wxUSE_DATEPICKCTRL_GENERIC
529 #endif // wxUSE_DATEPICKCTRL