1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/datectrl.cpp
3 // Purpose: wxDatePickerCtrl implementation
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 #include "wx/wxprec.h"
26 #if wxUSE_DATEPICKCTRL
29 #include "wx/msw/wrapwin.h"
30 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/msw/private.h"
38 #include "wx/datectrl.h"
40 #include "wx/msw/private/datecontrols.h"
42 #include "wx/dateevt.h"
44 // apparently some versions of mingw define these macros erroneously
45 #ifndef DateTime_GetSystemtime
46 #define DateTime_GetSystemtime DateTime_GetSystemTime
49 #ifndef DateTime_SetSystemtime
50 #define DateTime_SetSystemtime DateTime_SetSystemTime
53 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
55 // ============================================================================
57 // ============================================================================
59 // ----------------------------------------------------------------------------
60 // wxDatePickerCtrl creation
61 // ----------------------------------------------------------------------------
64 wxDatePickerCtrl::Create(wxWindow
*parent
,
70 const wxValidator
& validator
,
73 if ( !wxMSWDateControls::CheckInitialization() )
76 // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
77 if ( !(style
& wxDP_DROPDOWN
) )
80 // initialize the base class
81 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
84 // create the native control
85 if ( !MSWCreateControl(DATETIMEPICK_CLASS
, wxEmptyString
, pos
, size
) )
88 if ( dt
.IsValid() || (style
& wxDP_ALLOWNONE
) )
91 SetValue(wxDateTime::Today());
96 WXDWORD
wxDatePickerCtrl::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
98 WXDWORD styleMSW
= wxDatePickerCtrlBase::MSWGetStyle(style
, exstyle
);
100 // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
102 if ( wxApp::GetComCtl32Version() > 472 && (style
& wxDP_SPIN
) )
103 styleMSW
|= DTS_UPDOWN
;
104 //else: drop down by default
106 #ifdef DTS_SHORTDATECENTURYFORMAT
107 if ( style
& wxDP_SHOWCENTURY
)
108 styleMSW
|= DTS_SHORTDATECENTURYFORMAT
;
110 #endif // DTS_SHORTDATECENTURYFORMAT
111 styleMSW
|= DTS_SHORTDATEFORMAT
;
113 if ( style
& wxDP_ALLOWNONE
)
114 styleMSW
|= DTS_SHOWNONE
;
119 // TODO: handle WM_WININICHANGE
121 // ----------------------------------------------------------------------------
122 // wxDatePickerCtrl geometry
123 // ----------------------------------------------------------------------------
125 wxSize
wxDatePickerCtrl::DoGetBestSize() const
127 wxClientDC
dc(const_cast<wxDatePickerCtrl
*>(this));
129 // we can't use FormatDate() here as the CRT doesn't always use the same
130 // format as the date picker control
132 for ( int len
= 100; ; len
*= 2 )
136 LOCALE_USER_DEFAULT
, // the control should use the same
137 DATE_SHORTDATE
, // the format used by the control
138 NULL
, // use current date (we don't care)
139 NULL
, // no custom format
140 wxStringBuffer(s
, len
), // output buffer
141 len
// and its length
148 const DWORD rc
= ::GetLastError();
149 if ( rc
!= ERROR_INSUFFICIENT_BUFFER
)
151 wxLogApiError(wxT("GetDateFormat"), rc
);
153 // fall back on wxDateTime, what else to do?
154 s
= wxDateTime::Today().FormatDate();
159 // the best size for the control is bigger than just the string
160 // representation of todays date because the control must accommodate any
161 // date and while the widths of all digits are usually about the same, the
162 // width of the month string varies a lot, so try to account for it
166 dc
.GetTextExtent(s
, &x
, &y
);
168 // account for the drop-down arrow or spin arrows
169 x
+= wxSystemSettings::GetMetric(wxSYS_HSCROLL_ARROW_X
);
171 // and for the checkbox if we have it
172 if ( HasFlag(wxDP_ALLOWNONE
) )
173 x
+= 3*GetCharWidth();
175 wxSize
best(x
, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
));
180 // ----------------------------------------------------------------------------
181 // wxDatePickerCtrl operations
182 // ----------------------------------------------------------------------------
184 void wxDatePickerCtrl::SetValue(const wxDateTime
& dt
)
186 wxCHECK_RET( dt
.IsValid() || HasFlag(wxDP_ALLOWNONE
),
187 wxT("this control requires a valid date") );
192 // Don't try setting the date if it's out of range: calendar control
193 // under XP (and presumably all the other pre-Vista Windows versions)
194 // doesn't return false from DateTime_SetSystemtime() in this case but
195 // doesn't actually change the date, so we can't update our m_date
196 // unconditionally and would need to check whether it was changed
197 // before doing it. It looks simpler to just check whether it's in
198 // range here instead.
200 // If we ever drop support for XP we could rely on the return value of
201 // DateTime_SetSystemtime() but this probably won't happen in near
203 wxDateTime dtStart
, dtEnd
;
204 GetRange(&dtStart
, &dtEnd
);
205 if ( (dtStart
.IsValid() && dt
< dtStart
) ||
206 (dtEnd
.IsValid() && dt
> dtEnd
) )
208 // Fail silently, some existing code relies on SetValue() with an
209 // out of range value simply doing nothing -- so don't.
213 dt
.GetAsMSWSysTime(&st
);
216 if ( !DateTime_SetSystemtime(GetHwnd(),
217 dt
.IsValid() ? GDT_VALID
: GDT_NONE
,
220 // The only expected failure is when the date is out of range but we
221 // already checked for this above.
222 wxFAIL_MSG( wxT("Setting the calendar date unexpectedly failed.") );
224 // In any case, skip updating m_date below.
228 // we need to keep only the date part, times don't make sense for this
229 // control (in particular, comparisons with other dates would fail)
231 if ( m_date
.IsValid() )
235 wxDateTime
wxDatePickerCtrl::GetValue() const
240 if ( DateTime_GetSystemtime(GetHwnd(), &st
) == GDT_VALID
)
242 dt
.SetFromMSWSysDate(st
);
245 wxASSERT_MSG( m_date
.IsValid() == dt
.IsValid() &&
246 (!dt
.IsValid() || dt
== m_date
),
247 wxT("bug in wxDatePickerCtrl: m_date not in sync") );
248 #endif // wxDEBUG_LEVEL
253 void wxDatePickerCtrl::SetRange(const wxDateTime
& dt1
, const wxDateTime
& dt2
)
260 dt1
.GetAsMSWSysTime(st
+ 0);
266 dt2
.GetAsMSWSysTime(st
+ 1);
270 if ( !DateTime_SetRange(GetHwnd(), flags
, st
) )
272 wxLogDebug(wxT("DateTime_SetRange() failed"));
276 bool wxDatePickerCtrl::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
280 DWORD flags
= DateTime_GetRange(GetHwnd(), st
);
283 if ( flags
& GDTR_MIN
)
284 dt1
->SetFromMSWSysDate(st
[0]);
286 *dt1
= wxDefaultDateTime
;
291 if ( flags
& GDTR_MAX
)
292 dt2
->SetFromMSWSysDate(st
[1]);
294 *dt2
= wxDefaultDateTime
;
300 // ----------------------------------------------------------------------------
301 // wxDatePickerCtrl events
302 // ----------------------------------------------------------------------------
305 wxDatePickerCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
307 NMHDR
* hdr
= (NMHDR
*)lParam
;
310 case DTN_DATETIMECHANGE
:
312 NMDATETIMECHANGE
*dtch
= (NMDATETIMECHANGE
*)hdr
;
314 if ( dtch
->dwFlags
== GDT_VALID
)
315 dt
.SetFromMSWSysDate(dtch
->st
);
317 // filter out duplicate DTN_DATETIMECHANGE events which the native
318 // control sends us when using wxDP_DROPDOWN style
319 if ( (m_date
.IsValid() != dt
.IsValid()) ||
320 (m_date
.IsValid() && dt
!= m_date
) )
323 wxDateEvent
event(this, dt
, wxEVT_DATE_CHANGED
);
324 if ( HandleWindowEvent(event
) )
330 //else: both the old and new values are invalid, nothing changed
334 return wxDatePickerCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
);
337 #endif // wxUSE_DATEPICKCTRL