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/msw/private.h"
37 #include "wx/datectrl.h"
38 #include "wx/dynlib.h"
40 #define _WX_DEFINE_DATE_EVENTS_
41 #include "wx/dateevt.h"
43 // apparently some versions of mingw define these macros erroneously
44 #ifndef DateTime_GetSystemtime
45 #define DateTime_GetSystemtime DateTime_GetSystemTime
48 #ifndef DateTime_SetSystemtime
49 #define DateTime_SetSystemtime DateTime_SetSystemTime
52 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl
, wxControl
)
54 // ============================================================================
56 // ============================================================================
58 // ----------------------------------------------------------------------------
59 // helpers for wxDateTime <-> SYSTEMTIME conversion
60 // ----------------------------------------------------------------------------
62 static inline void wxFromSystemTime(wxDateTime
*dt
, const SYSTEMTIME
& st
)
65 wx_static_cast(wxDateTime::Month
, wxDateTime::Jan
+ st
.wMonth
- 1),
70 static inline void wxToSystemTime(SYSTEMTIME
*st
, const wxDateTime
& dt
)
72 const wxDateTime::Tm
tm(dt
.GetTm());
74 st
->wYear
= (WXWORD
)tm
.year
;
75 st
->wMonth
= (WXWORD
)(tm
.mon
- wxDateTime::Jan
+ 1);
82 st
->wMilliseconds
= 0;
85 // ----------------------------------------------------------------------------
86 // wxDatePickerCtrl creation
87 // ----------------------------------------------------------------------------
90 wxDatePickerCtrl::Create(wxWindow
*parent
,
96 const wxValidator
& validator
,
99 // although we already call InitCommonControls() in app.cpp which is
100 // supposed to initialize all common controls, in comctl32.dll 4.72 (and
101 // presumably earlier versions 4.70 and 4.71, date time picker not being
102 // supported in < 4.70 anyhow) it does not do it and we have to initialize
104 static bool s_initDone
= false; // MT-ok: used from GUI thread only
108 if ( wxApp::GetComCtl32Version() < 470 )
110 wxLogError(_("This system doesn't support date picker control, please upgrade your version of comctl32.dll"));
116 #if wxUSE_DYNLIB_CLASS
117 INITCOMMONCONTROLSEX icex
;
118 icex
.dwSize
= sizeof(icex
);
119 icex
.dwICC
= ICC_DATE_CLASSES
;
121 wxDynamicLibrary
dllComCtl32(
129 if ( dllComCtl32
.IsLoaded() )
131 typedef BOOL (WINAPI
*ICCEx_t
)(INITCOMMONCONTROLSEX
*);
132 wxDYNLIB_FUNCTION( ICCEx_t
, InitCommonControlsEx
, dllComCtl32
);
134 if ( pfnInitCommonControlsEx
)
136 (*pfnInitCommonControlsEx
)(&icex
);
145 // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
146 if ( !(style
& wxDP_DROPDOWN
) )
149 // initialize the base class
150 if ( !CreateControl(parent
, id
, pos
, size
, style
, validator
, name
) )
153 // create the native control
154 if ( !MSWCreateControl(DATETIMEPICK_CLASS
, wxEmptyString
, pos
, size
) )
157 if ( dt
.IsValid() || (style
& wxDP_ALLOWNONE
) )
160 SetValue(wxDateTime::Today());
165 WXDWORD
wxDatePickerCtrl::MSWGetStyle(long style
, WXDWORD
*exstyle
) const
167 WXDWORD styleMSW
= wxDatePickerCtrlBase::MSWGetStyle(style
, exstyle
);
169 // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
171 if ( wxApp::GetComCtl32Version() > 472 && (style
& wxDP_SPIN
) )
172 styleMSW
|= DTS_UPDOWN
;
173 //else: drop down by default
175 #ifdef DTS_SHORTDATECENTURYFORMAT
176 if ( style
& wxDP_SHOWCENTURY
)
177 styleMSW
|= DTS_SHORTDATECENTURYFORMAT
;
179 #endif // DTS_SHORTDATECENTURYFORMAT
180 styleMSW
|= DTS_SHORTDATEFORMAT
;
182 if ( style
& wxDP_ALLOWNONE
)
183 styleMSW
|= DTS_SHOWNONE
;
188 // TODO: handle WM_WININICHANGE
190 // ----------------------------------------------------------------------------
191 // wxDatePickerCtrl geometry
192 // ----------------------------------------------------------------------------
194 wxSize
wxDatePickerCtrl::DoGetBestSize() const
196 wxClientDC
dc(wx_const_cast(wxDatePickerCtrl
*, this));
197 dc
.SetFont(GetFont());
199 // we can't use FormatDate() here as the CRT doesn't always use the same
200 // format as the date picker control
202 for ( int len
= 100; ; len
*= 2 )
206 LOCALE_USER_DEFAULT
, // the control should use the same
207 DATE_SHORTDATE
, // the format used by the control
208 NULL
, // use current date (we don't care)
209 NULL
, // no custom format
210 wxStringBuffer(s
, len
), // output buffer
211 len
// and its length
218 const DWORD rc
= ::GetLastError();
219 if ( rc
!= ERROR_INSUFFICIENT_BUFFER
)
221 wxLogApiError(_T("GetDateFormat"), rc
);
223 // fall back on wxDateTime, what else to do?
224 s
= wxDateTime::Today().FormatDate();
229 // the control adds a lot of extra space around separators
230 s
.Replace(_T(","), _T(" , "));
233 dc
.GetTextExtent(s
, &x
, &y
);
235 wxSize
best(x
+ 40 /* margin + arrows */, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y
));
240 // ----------------------------------------------------------------------------
241 // wxDatePickerCtrl operations
242 // ----------------------------------------------------------------------------
244 void wxDatePickerCtrl::SetValue(const wxDateTime
& dt
)
246 wxCHECK_RET( dt
.IsValid() || HasFlag(wxDP_ALLOWNONE
),
247 _T("this control requires a valid date") );
251 wxToSystemTime(&st
, dt
);
252 if ( !DateTime_SetSystemtime(GetHwnd(),
253 dt
.IsValid() ? GDT_VALID
: GDT_NONE
,
256 wxLogDebug(_T("DateTime_SetSystemtime() failed"));
259 // we need to keep only the date part, times don't make sense for this
260 // control (in particular, comparisons with other dates would fail)
262 if ( m_date
.IsValid() )
268 wxDateTime
wxDatePickerCtrl::GetValue() const
273 if ( DateTime_GetSystemtime(GetHwnd(), &st
) == GDT_VALID
)
275 wxFromSystemTime(&dt
, st
);
278 wxASSERT_MSG( m_date
.IsValid() == dt
.IsValid() &&
279 (!dt
.IsValid() || dt
== m_date
),
280 _T("bug in wxDatePickerCtrl: m_date not in sync") );
281 #endif // __WXDEBUG__
286 void wxDatePickerCtrl::SetRange(const wxDateTime
& dt1
, const wxDateTime
& dt2
)
293 wxToSystemTime(&st
[0], dt1
);
299 wxToSystemTime(&st
[1], dt2
);
303 if ( !DateTime_SetRange(GetHwnd(), flags
, st
) )
305 wxLogDebug(_T("DateTime_SetRange() failed"));
309 bool wxDatePickerCtrl::GetRange(wxDateTime
*dt1
, wxDateTime
*dt2
) const
313 DWORD flags
= DateTime_GetRange(GetHwnd(), st
);
316 if ( flags
& GDTR_MIN
)
317 wxFromSystemTime(dt1
, st
[0]);
319 *dt1
= wxDefaultDateTime
;
324 if ( flags
& GDTR_MAX
)
325 wxFromSystemTime(dt2
, st
[1]);
327 *dt2
= wxDefaultDateTime
;
333 // ----------------------------------------------------------------------------
334 // wxDatePickerCtrl events
335 // ----------------------------------------------------------------------------
338 wxDatePickerCtrl::MSWOnNotify(int idCtrl
, WXLPARAM lParam
, WXLPARAM
*result
)
340 NMHDR
* hdr
= (NMHDR
*)lParam
;
343 case DTN_DATETIMECHANGE
:
345 NMDATETIMECHANGE
*dtch
= (NMDATETIMECHANGE
*)hdr
;
347 if ( dtch
->dwFlags
== GDT_VALID
)
348 wxFromSystemTime(&dt
, dtch
->st
);
350 // filter out duplicate DTN_DATETIMECHANGE events which the native
351 // control sends us when using wxDP_DROPDOWN style
352 if ( (m_date
.IsValid() != dt
.IsValid()) ||
353 (m_date
.IsValid() && dt
!= m_date
) )
356 wxDateEvent
event(this, dt
, wxEVT_DATE_CHANGED
);
357 if ( GetEventHandler()->ProcessEvent(event
) )
363 //else: both the old and new values are invalid, nothing changed
367 return wxDatePickerCtrlBase::MSWOnNotify(idCtrl
, lParam
, result
);
370 #endif // wxUSE_DATEPICKCTRL