added support for wxDP_ALLOWNONE in wxMSW; documented it; added test for it in the...
[wxWidgets.git] / src / msw / datectrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: msw/datectrl.cpp
3 // Purpose: wxDatePickerCtrl implementation
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 2005-01-09
7 // RCS-ID: $Id$
8 // Copyright: (c) 2005 Vadim Zeitlin <vadim@wxwindows.org>
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #include "wx/wxprec.h"
21
22 #ifdef __BORLANDC__
23 #pragma hdrstop
24 #endif
25
26 #ifndef WX_PRECOMP
27 #endif
28
29 #if wxUSE_DATEPICKCTRL
30
31 #include "wx/datectrl.h"
32 #include "wx/app.h"
33 #include "wx/intl.h"
34 #include "wx/dynlib.h"
35
36 #define _WX_DEFINE_DATE_EVENTS_
37 #include "wx/dateevt.h"
38
39 #include "wx/msw/wrapwin.h"
40 #include "wx/msw/wrapcctl.h"
41 #include "wx/msw/private.h"
42
43 #if defined(__GNUWIN32__) && !wxCHECK_W32API_VERSION( 2, 4 )
44 typedef struct tagNMDATETIMECHANGE
45 {
46 NMHDR nmhdr;
47 DWORD dwFlags;
48 SYSTEMTIME st;
49 } NMDATETIMECHANGE;
50 #endif // old gcc headers
51
52 // apparently some versions of mingw define these macros erroneously
53 #ifndef DateTime_GetSystemtime
54 #define DateTime_GetSystemtime DateTime_GetSystemTime
55 #endif
56
57 #ifndef DateTime_SetSystemtime
58 #define DateTime_SetSystemtime DateTime_SetSystemTime
59 #endif
60
61 // ============================================================================
62 // implementation
63 // ============================================================================
64
65 // ----------------------------------------------------------------------------
66 // helpers for wxDateTime <-> SYSTEMTIME conversion
67 // ----------------------------------------------------------------------------
68
69 static inline void wxFromSystemTime(wxDateTime *dt, const SYSTEMTIME& st)
70 {
71 dt->Set(st.wDay,
72 wx_static_cast(wxDateTime::Month, wxDateTime::Jan + st.wMonth - 1),
73 st.wYear,
74 0, 0, 0);
75 }
76
77 static inline void wxToSystemTime(SYSTEMTIME *st, const wxDateTime& dt)
78 {
79 const wxDateTime::Tm tm(dt.GetTm());
80
81 st->wYear = (WXWORD)tm.year;
82 st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
83 st->wDay = tm.mday;
84
85 st->wDayOfWeek =
86 st->wHour =
87 st->wMinute =
88 st->wSecond =
89 st->wMilliseconds = 0;
90 }
91
92 // ----------------------------------------------------------------------------
93 // wxDatePickerCtrl creation
94 // ----------------------------------------------------------------------------
95
96 bool
97 wxDatePickerCtrl::Create(wxWindow *parent,
98 wxWindowID id,
99 const wxDateTime& dt,
100 const wxPoint& pos,
101 const wxSize& size,
102 long style,
103 const wxValidator& validator,
104 const wxString& name)
105 {
106 // although we already call InitCommonControls() in app.cpp which is
107 // supposed to initialize all common controls, in comctl32.dll 4.72 (and
108 // presumably earlier versions 4.70 and 4.71, date time picker not being
109 // supported in < 4.70 anyhow) it does not do it and we have to initialize
110 // it explicitely
111 static bool s_initDone = false; // MT-ok: used from GUI thread only
112 if ( !s_initDone )
113 {
114 if ( wxTheApp->GetComCtl32Version() < 470 )
115 {
116 wxLogError(_("This system doesn't support date picker control, please upgrade your version of comctl32.dll"));
117
118 return false;
119 }
120
121 INITCOMMONCONTROLSEX icex;
122 icex.dwSize = sizeof(icex);
123 icex.dwICC = ICC_DATE_CLASSES;
124
125 wxDynamicLibrary dllComCtl32(_T("comctl32.dll"), wxDL_VERBATIM);
126
127 typedef BOOL (WINAPI *ICCEx_t)(INITCOMMONCONTROLSEX *);
128 wxDYNLIB_FUNCTION( ICCEx_t, InitCommonControlsEx, dllComCtl32 );
129
130 if ( pfnInitCommonControlsEx )
131 {
132 (*pfnInitCommonControlsEx)(&icex);
133 }
134
135 s_initDone = true;
136 }
137
138
139 // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
140 if ( !(style & wxDP_DROPDOWN) )
141 style |= wxDP_SPIN;
142
143 // initialize the base class
144 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
145 return false;
146
147 // create the native control
148 if ( !MSWCreateControl(DATETIMEPICK_CLASS, wxEmptyString, pos, size) )
149 return false;
150
151 if ( dt.IsValid() )
152 SetValue(dt);
153
154 return true;
155 }
156
157 WXDWORD wxDatePickerCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
158 {
159 WXDWORD styleMSW = wxDatePickerCtrlBase::MSWGetStyle(style, exstyle);
160
161 // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
162 // comctl32.dll 4.72
163 if ( wxTheApp->GetComCtl32Version() > 472 && (style & wxDP_SPIN) )
164 styleMSW |= DTS_UPDOWN;
165 //else: drop down by default
166
167 #ifdef DTS_SHORTDATECENTURYFORMAT
168 if ( style & wxDP_SHOWCENTURY )
169 styleMSW |= DTS_SHORTDATECENTURYFORMAT;
170 else
171 #endif // DTS_SHORTDATECENTURYFORMAT
172 styleMSW |= DTS_SHORTDATEFORMAT;
173
174 if ( style & wxDP_ALLOWNONE )
175 styleMSW |= DTS_SHOWNONE;
176
177 return styleMSW;
178 }
179
180 // TODO: handle WM_WININICHANGE
181
182 // ----------------------------------------------------------------------------
183 // wxDatePickerCtrl geometry
184 // ----------------------------------------------------------------------------
185
186 wxSize wxDatePickerCtrl::DoGetBestSize() const
187 {
188 const int y = GetCharHeight();
189
190 return wxSize(DEFAULT_ITEM_WIDTH, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y));
191 }
192
193 // ----------------------------------------------------------------------------
194 // wxDatePickerCtrl operations
195 // ----------------------------------------------------------------------------
196
197 void wxDatePickerCtrl::SetValue(const wxDateTime& dt)
198 {
199 wxCHECK_RET( dt.IsValid() || HasFlag(wxDP_ALLOWNONE),
200 _T("this control requires a valid date") );
201
202 SYSTEMTIME st;
203 if ( dt.IsValid() )
204 wxToSystemTime(&st, dt);
205 if ( !DateTime_SetSystemtime(GetHwnd(),
206 dt.IsValid() ? GDT_VALID : GDT_NONE,
207 &st) )
208 {
209 wxLogDebug(_T("DateTime_SetSystemtime() failed"));
210 }
211 }
212
213 wxDateTime wxDatePickerCtrl::GetValue() const
214 {
215 wxDateTime dt;
216 SYSTEMTIME st;
217 if ( DateTime_GetSystemtime(GetHwnd(), &st) == GDT_VALID )
218 {
219 wxFromSystemTime(&dt, st);
220 }
221
222 return dt;
223 }
224
225 void wxDatePickerCtrl::SetRange(const wxDateTime& dt1, const wxDateTime& dt2)
226 {
227 SYSTEMTIME st[2];
228
229 DWORD flags = 0;
230 if ( dt1.IsValid() )
231 {
232 wxToSystemTime(&st[0], dt1);
233 flags |= GDTR_MIN;
234 }
235
236 if ( dt2.IsValid() )
237 {
238 wxToSystemTime(&st[1], dt2);
239 flags |= GDTR_MAX;
240 }
241
242 if ( !DateTime_SetRange(GetHwnd(), flags, st) )
243 {
244 wxLogDebug(_T("DateTime_SetRange() failed"));
245 }
246 }
247
248 bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
249 {
250 SYSTEMTIME st[2];
251
252 DWORD flags = DateTime_GetRange(GetHwnd(), st);
253 if ( dt1 )
254 {
255 if ( flags & GDTR_MIN )
256 wxFromSystemTime(dt1, st[0]);
257 else
258 *dt1 = wxDefaultDateTime;
259 }
260
261 if ( dt2 )
262 {
263 if ( flags & GDTR_MAX )
264 wxFromSystemTime(dt2, st[1]);
265 else
266 *dt2 = wxDefaultDateTime;
267 }
268
269 return flags != 0;
270 }
271
272 // ----------------------------------------------------------------------------
273 // wxDatePickerCtrl events
274 // ----------------------------------------------------------------------------
275
276 bool
277 wxDatePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
278 {
279 NMHDR* hdr = (NMHDR *)lParam;
280 switch ( hdr->code )
281 {
282 case DTN_DATETIMECHANGE:
283 NMDATETIMECHANGE *dtch = (NMDATETIMECHANGE *)hdr;
284 wxDateTime dt;
285 if ( dtch->dwFlags == GDT_VALID )
286 wxFromSystemTime(&dt, dtch->st);
287
288 wxDateEvent event(this, dt, wxEVT_DATE_CHANGED);
289 if ( GetEventHandler()->ProcessEvent(event) )
290 {
291 *result = 0;
292 return true;
293 }
294 }
295
296 return wxDatePickerCtrlBase::MSWOnNotify(idCtrl, lParam, result);
297 }
298
299 #endif // wxUSE_DATEPICKCTRL
300