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 // ----------------------------------------------------------------------------
73 // this should have been a flag in wxDatePickerCtrlGeneric itself but adding it
74 // there now would break backwards compatibility, so put it here as a global:
75 // this shouldn't be a big problem as only one (GUI) thread normally can call
76 // wxDatePickerCtrlGeneric::SetValue() and so it can be only ever used for one
79 // if the value is not NULL, it points to the control which is inside SetValue()
80 static wxDatePickerCtrlGeneric
*gs_inSetValue
= NULL
;
82 // ----------------------------------------------------------------------------
84 // ----------------------------------------------------------------------------
86 class wxCalendarComboPopup
: public wxCalendarCtrl
,
91 wxCalendarComboPopup() : wxCalendarCtrl(),
100 // NB: Don't create lazily since it didn't work that way before
101 // wxComboCtrl was used, and changing behaviour would almost
102 // certainly introduce new bugs.
103 virtual bool Create(wxWindow
* parent
)
105 if ( !wxCalendarCtrl::Create(parent
, wxID_ANY
, wxDefaultDateTime
,
106 wxPoint(0, 0), wxDefaultSize
,
107 wxCAL_SHOW_HOLIDAYS
| wxBORDER_SUNKEN
) )
110 wxWindow
*yearControl
= wxCalendarCtrl::GetYearControl();
112 wxClientDC
dc(yearControl
);
113 dc
.SetFont(yearControl
->GetFont());
114 wxCoord width
, dummy
;
115 dc
.GetTextExtent(wxT("2000"), &width
, &dummy
);
116 width
+= ConvertDialogToPixels(wxSize(20, 0)).x
;
118 wxSize calSize
= wxCalendarCtrl::GetBestSize();
119 wxSize yearSize
= yearControl
->GetSize();
122 wxPoint yearPosition
= yearControl
->GetPosition();
124 SetFormat(wxT("%x"));
126 width
= yearPosition
.x
+ yearSize
.x
+2+CALBORDER
/2;
127 if (width
< calSize
.x
-4)
130 int calPos
= (width
-calSize
.x
)/2;
136 wxCalendarCtrl::SetSize(calPos
, 0, calSize
.x
, calSize
.y
);
137 yearControl
->SetSize(width
-yearSize
.x
-CALBORDER
/2, yearPosition
.y
,
138 yearSize
.x
, yearSize
.y
);
139 wxCalendarCtrl::GetMonthControl()->Move(0, 0);
141 m_useSize
.x
= width
+CALBORDER
/2;
142 m_useSize
.y
= calSize
.y
-2+CALBORDER
;
144 wxWindow
* tx
= m_combo
->GetTextCtrl();
148 tx
->Connect(wxEVT_KILL_FOCUS
,
149 wxFocusEventHandler(wxCalendarComboPopup::OnKillTextFocus
),
155 virtual wxSize
GetAdjustedSize(int WXUNUSED(minWidth
),
156 int WXUNUSED(prefHeight
),
157 int WXUNUSED(maxHeight
))
162 virtual wxWindow
*GetControl() { return this; }
164 void SetDateValue(const wxDateTime
& date
)
166 if ( date
.IsValid() )
168 m_combo
->SetText(date
.Format(m_format
));
172 wxASSERT_MSG( HasDPFlag(wxDP_ALLOWNONE
),
173 _T("this control must have a valid date") );
175 m_combo
->SetText(wxEmptyString
);
178 m_currentDate
= date
;
181 const wxDateTime
& GetDateValue() const
183 return m_currentDate
;
186 bool ParseDateTime(const wxString
& s
, wxDateTime
* pDt
)
192 pDt
->ParseFormat(s
, m_format
);
193 if ( !pDt
->IsValid() )
200 void SendDateEvent(const wxDateTime
& dt
)
203 // Sends both wxCalendarEvent and wxDateEvent
204 wxWindow
* datePicker
= m_combo
->GetParent();
206 wxCalendarEvent
cev((wxCalendarCtrl
*) this, wxEVT_CALENDAR_SEL_CHANGED
);
207 cev
.SetEventObject(datePicker
);
208 cev
.SetId(datePicker
->GetId());
210 GetParent()->ProcessEvent(cev
);
212 wxDateEvent
event(datePicker
, dt
, wxEVT_DATE_CHANGED
);
213 datePicker
->GetParent()->ProcessEvent(event
);
218 void OnCalKey(wxKeyEvent
& ev
)
220 if (ev
.GetKeyCode() == WXK_ESCAPE
&& !ev
.HasModifiers())
226 void OnSelChange(wxCalendarEvent
&ev
)
228 m_currentDate
= wxCalendarCtrl::GetDate();
229 m_combo
->SetText(m_currentDate
.Format(m_format
));
231 if ( ev
.GetEventType() == wxEVT_CALENDAR_DOUBLECLICKED
)
236 SendDateEvent(m_currentDate
);
239 void OnKillTextFocus(wxFocusEvent
&ev
)
244 wxString value
= m_combo
->GetValue();
245 if ( !ParseDateTime(value
, &dt
) )
247 if ( !HasDPFlag(wxDP_ALLOWNONE
) )
252 m_combo
->SetText(dt
.Format(m_format
));
254 m_combo
->SetText(wxEmptyString
);
256 // notify that we had to change the date after validation
257 if ( (dt
.IsValid() && (!m_currentDate
.IsValid() || m_currentDate
!= dt
)) ||
258 (!dt
.IsValid() && m_currentDate
.IsValid()) )
265 bool HasDPFlag(int flag
)
267 return m_combo
->GetParent()->HasFlag(flag
);
270 bool SetFormat(const wxChar
*fmt
)
275 dt
.ParseFormat(wxT("2003-10-13"), wxT("%Y-%m-%d"));
276 wxString
str(dt
.Format(fmt
));
278 const wxChar
*p
= str
.c_str();
282 if (n
== dt
.GetDay())
284 m_format
.Append(wxT("%d"));
287 else if (n
== (int)dt
.GetMonth()+1)
289 m_format
.Append(wxT("%m"));
292 else if (n
== dt
.GetYear())
294 m_format
.Append(wxT("%Y"));
297 else if (n
== (dt
.GetYear() % 100))
299 if ( HasDPFlag(wxDP_SHOWCENTURY
) )
300 m_format
.Append(wxT("%Y"));
302 m_format
.Append(wxT("%y"));
306 m_format
.Append(*p
++);
311 wxArrayString allowedChars
;
312 for ( wxChar c
= _T('0'); c
<= _T('9'); c
++ )
313 allowedChars
.Add(wxString(c
, 1));
315 const wxChar
*p2
= m_format
.c_str();
321 allowedChars
.Add(wxString(*p2
++, 1));
325 wxTextValidator
tv(wxFILTER_INCLUDE_CHAR_LIST
);
326 tv
.SetIncludes(allowedChars
);
327 m_combo
->SetValidator(tv
);
330 if (m_currentDate
.IsValid())
331 m_combo
->SetText(m_currentDate
.Format(m_format
));
337 virtual void SetStringValue(const wxString
& s
)
340 if ( ParseDateTime(s
, &dt
) )
342 else if ( HasDPFlag(wxDP_ALLOWNONE
) )
346 virtual wxString
GetStringValue() const
348 if ( !m_currentDate
.IsValid() )
349 return wxEmptyString
;
351 return m_currentDate
.Format(m_format
);
358 wxDateTime m_currentDate
;
360 DECLARE_EVENT_TABLE()
364 BEGIN_EVENT_TABLE(wxCalendarComboPopup
, wxCalendarCtrl
)
365 EVT_KEY_DOWN(wxCalendarComboPopup::OnCalKey
)
366 EVT_CALENDAR_SEL_CHANGED(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
367 EVT_CALENDAR_DAY(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
368 EVT_CALENDAR_MONTH(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
369 EVT_CALENDAR_YEAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
370 EVT_CALENDAR(wxID_ANY
, wxCalendarComboPopup::OnSelChange
)
374 // ============================================================================
375 // wxDatePickerCtrlGeneric implementation
376 // ============================================================================
378 BEGIN_EVENT_TABLE(wxDatePickerCtrlGeneric
, wxDatePickerCtrlBase
)
379 EVT_TEXT(wxID_ANY
, wxDatePickerCtrlGeneric::OnText
)
380 EVT_SIZE(wxDatePickerCtrlGeneric::OnSize
)
383 #ifndef wxHAS_NATIVE_DATEPICKCTRL
384 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
387 // ----------------------------------------------------------------------------
389 // ----------------------------------------------------------------------------
391 bool wxDatePickerCtrlGeneric::Create(wxWindow
*parent
,
393 const wxDateTime
& date
,
397 const wxValidator
& validator
,
398 const wxString
& name
)
400 wxASSERT_MSG( !(style
& wxDP_SPIN
),
401 _T("wxDP_SPIN style not supported, use wxDP_DEFAULT") );
403 if ( !wxControl::Create(parent
, id
, pos
, size
,
404 style
| wxCLIP_CHILDREN
| wxWANTS_CHARS
| wxBORDER_NONE
,
412 m_combo
= new wxComboCtrl(this, -1, wxEmptyString
,
413 wxDefaultPosition
, wxDefaultSize
);
415 m_popup
= new wxCalendarComboPopup();
417 m_combo
->SetPopupControl(m_popup
);
421 m_popup
->SetDateValue(date
.IsValid() ? date
: wxDateTime::Today());
423 SetBestFittingSize(size
);
429 void wxDatePickerCtrlGeneric::Init()
436 wxDatePickerCtrlGeneric::~wxDatePickerCtrlGeneric()
440 bool wxDatePickerCtrlGeneric::Destroy()
449 return wxControl::Destroy();
452 // ----------------------------------------------------------------------------
453 // overridden base class methods
454 // ----------------------------------------------------------------------------
456 wxSize
wxDatePickerCtrlGeneric::DoGetBestSize() const
458 return m_combo
->GetBestSize();
461 // ----------------------------------------------------------------------------
462 // wxDatePickerCtrlGeneric API
463 // ----------------------------------------------------------------------------
466 wxDatePickerCtrlGeneric::SetDateRange(const wxDateTime
& lowerdate
,
467 const wxDateTime
& upperdate
)
469 return m_cal
->SetDateRange(lowerdate
, upperdate
);
473 wxDateTime
wxDatePickerCtrlGeneric::GetValue() const
475 return m_popup
->GetDateValue();
479 void wxDatePickerCtrlGeneric::SetValue(const wxDateTime
& date
)
481 m_popup
->SetDateValue(date
);
485 bool wxDatePickerCtrlGeneric::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
488 *dt1
= m_cal
->GetLowerDateLimit();
490 *dt2
= m_cal
->GetUpperDateLimit();
496 wxDatePickerCtrlGeneric::SetRange(const wxDateTime
&dt1
, const wxDateTime
&dt2
)
498 m_cal
->SetDateRange(dt1
, dt2
);
501 // ----------------------------------------------------------------------------
503 // ----------------------------------------------------------------------------
506 void wxDatePickerCtrlGeneric::OnSize(wxSizeEvent
& event
)
509 m_combo
->SetSize(GetClientSize());
515 void wxDatePickerCtrlGeneric::OnText(wxCommandEvent
&ev
)
517 ev
.SetEventObject(this);
519 GetParent()->ProcessEvent(ev
);
521 // We'll create an additional event if the date is valid.
522 // If the date isn't valid, the user's probably in the middle of typing
524 if ( !m_popup
->ParseDateTime(m_combo
->GetValue(), &dt
) )
527 m_popup
->SendDateEvent(dt
);
531 #endif // wxUSE_DATEPICKCTRL_GENERIC
533 #endif // wxUSE_DATEPICKCTRL