]> git.saurik.com Git - wxWidgets.git/blame - src/msw/datectrl.cpp
Move column organizing code to ports, away from common code
[wxWidgets.git] / src / msw / datectrl.cpp
CommitLineData
feb72429 1/////////////////////////////////////////////////////////////////////////////
02e05e7e 2// Name: src/msw/datectrl.cpp
feb72429
VZ
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
02e05e7e
WS
26#if wxUSE_DATEPICKCTRL
27
feb72429 28#ifndef WX_PRECOMP
57bd4c60
WS
29 #include "wx/msw/wrapwin.h"
30 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
02e05e7e
WS
31 #include "wx/app.h"
32 #include "wx/intl.h"
33 #include "wx/dcclient.h"
02e05e7e 34 #include "wx/msw/private.h"
feb72429
VZ
35#endif
36
37#include "wx/datectrl.h"
0aa7cb54 38#include "wx/dynlib.h"
feb72429
VZ
39
40#define _WX_DEFINE_DATE_EVENTS_
41#include "wx/dateevt.h"
42
3200f37d
VZ
43// apparently some versions of mingw define these macros erroneously
44#ifndef DateTime_GetSystemtime
45 #define DateTime_GetSystemtime DateTime_GetSystemTime
46#endif
47
48#ifndef DateTime_SetSystemtime
49 #define DateTime_SetSystemtime DateTime_SetSystemTime
65ab1002
JS
50#endif
51
a69e2a0a
VZ
52IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
53
feb72429
VZ
54// ============================================================================
55// implementation
56// ============================================================================
57
58// ----------------------------------------------------------------------------
59// helpers for wxDateTime <-> SYSTEMTIME conversion
60// ----------------------------------------------------------------------------
61
62static inline void wxFromSystemTime(wxDateTime *dt, const SYSTEMTIME& st)
63{
64 dt->Set(st.wDay,
65 wx_static_cast(wxDateTime::Month, wxDateTime::Jan + st.wMonth - 1),
66 st.wYear,
67 0, 0, 0);
68}
69
70static inline void wxToSystemTime(SYSTEMTIME *st, const wxDateTime& dt)
71{
72 const wxDateTime::Tm tm(dt.GetTm());
73
3a0c6181
WS
74 st->wYear = (WXWORD)tm.year;
75 st->wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
feb72429
VZ
76 st->wDay = tm.mday;
77
78 st->wDayOfWeek =
79 st->wHour =
80 st->wMinute =
81 st->wSecond =
82 st->wMilliseconds = 0;
83}
84
85// ----------------------------------------------------------------------------
86// wxDatePickerCtrl creation
87// ----------------------------------------------------------------------------
88
89bool
90wxDatePickerCtrl::Create(wxWindow *parent,
91 wxWindowID id,
92 const wxDateTime& dt,
93 const wxPoint& pos,
94 const wxSize& size,
95 long style,
96 const wxValidator& validator,
97 const wxString& name)
98{
0aa7cb54
VZ
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
3103e8a9 103 // it explicitly
0aa7cb54
VZ
104 static bool s_initDone = false; // MT-ok: used from GUI thread only
105 if ( !s_initDone )
106 {
6e0f4664 107#ifndef __WXWINCE__
2a1f999f 108 if ( wxApp::GetComCtl32Version() < 470 )
0aa7cb54
VZ
109 {
110 wxLogError(_("This system doesn't support date picker control, please upgrade your version of comctl32.dll"));
111
112 return false;
113 }
6e0f4664 114#endif
0aa7cb54 115
64c288fa 116#if wxUSE_DYNLIB_CLASS
0aa7cb54
VZ
117 INITCOMMONCONTROLSEX icex;
118 icex.dwSize = sizeof(icex);
119 icex.dwICC = ICC_DATE_CLASSES;
120
6e0f4664
WS
121 wxDynamicLibrary dllComCtl32(
122#ifdef __WXWINCE__
123 _T("commctrl.dll")
124#else
125 _T("comctl32.dll")
126#endif
127 , wxDL_VERBATIM);
0aa7cb54 128
6e0f4664 129 if ( dllComCtl32.IsLoaded() )
0aa7cb54 130 {
6e0f4664
WS
131 typedef BOOL (WINAPI *ICCEx_t)(INITCOMMONCONTROLSEX *);
132 wxDYNLIB_FUNCTION( ICCEx_t, InitCommonControlsEx, dllComCtl32 );
133
134 if ( pfnInitCommonControlsEx )
135 {
136 (*pfnInitCommonControlsEx)(&icex);
137 }
0aa7cb54 138
6e0f4664
WS
139 s_initDone = true;
140 }
02e05e7e 141#endif
0aa7cb54
VZ
142 }
143
144
5385747e
VZ
145 // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
146 if ( !(style & wxDP_DROPDOWN) )
147 style |= wxDP_SPIN;
148
feb72429
VZ
149 // initialize the base class
150 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
151 return false;
152
153 // create the native control
f31a4098 154 if ( !MSWCreateControl(DATETIMEPICK_CLASS, wxEmptyString, pos, size) )
feb72429
VZ
155 return false;
156
bab3f3ea 157 if ( dt.IsValid() || (style & wxDP_ALLOWNONE) )
feb72429 158 SetValue(dt);
4d536421
RD
159 else
160 SetValue(wxDateTime::Today());
feb72429
VZ
161
162 return true;
163}
164
165WXDWORD wxDatePickerCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
166{
167 WXDWORD styleMSW = wxDatePickerCtrlBase::MSWGetStyle(style, exstyle);
168
0aa7cb54
VZ
169 // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
170 // comctl32.dll 4.72
2a1f999f 171 if ( wxApp::GetComCtl32Version() > 472 && (style & wxDP_SPIN) )
29c86948
VZ
172 styleMSW |= DTS_UPDOWN;
173 //else: drop down by default
174
2cfbeac8
VZ
175#ifdef DTS_SHORTDATECENTURYFORMAT
176 if ( style & wxDP_SHOWCENTURY )
177 styleMSW |= DTS_SHORTDATECENTURYFORMAT;
178 else
179#endif // DTS_SHORTDATECENTURYFORMAT
180 styleMSW |= DTS_SHORTDATEFORMAT;
feb72429 181
3200f37d
VZ
182 if ( style & wxDP_ALLOWNONE )
183 styleMSW |= DTS_SHOWNONE;
184
feb72429
VZ
185 return styleMSW;
186}
187
188// TODO: handle WM_WININICHANGE
189
190// ----------------------------------------------------------------------------
191// wxDatePickerCtrl geometry
192// ----------------------------------------------------------------------------
193
194wxSize wxDatePickerCtrl::DoGetBestSize() const
195{
c01359d9
VZ
196 wxClientDC dc(wx_const_cast(wxDatePickerCtrl *, this));
197 dc.SetFont(GetFont());
feb72429 198
c01359d9
VZ
199 // we can't use FormatDate() here as the CRT doesn't always use the same
200 // format as the date picker control
201 wxString s;
202 for ( int len = 100; ; len *= 2 )
203 {
204 if ( ::GetDateFormat
205 (
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
212 ) )
213 {
214 // success
215 break;
216 }
217
218 const DWORD rc = ::GetLastError();
219 if ( rc != ERROR_INSUFFICIENT_BUFFER )
220 {
221 wxLogApiError(_T("GetDateFormat"), rc);
222
223 // fall back on wxDateTime, what else to do?
224 s = wxDateTime::Today().FormatDate();
225 break;
226 }
227 }
228
229 // the control adds a lot of extra space around separators
230 s.Replace(_T(","), _T(" , "));
231
232 int x, y;
233 dc.GetTextExtent(s, &x, &y);
234
235 wxSize best(x + 40 /* margin + arrows */, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y));
31582e4e
RD
236 CacheBestSize(best);
237 return best;
feb72429
VZ
238}
239
240// ----------------------------------------------------------------------------
241// wxDatePickerCtrl operations
242// ----------------------------------------------------------------------------
243
244void wxDatePickerCtrl::SetValue(const wxDateTime& dt)
245{
3200f37d
VZ
246 wxCHECK_RET( dt.IsValid() || HasFlag(wxDP_ALLOWNONE),
247 _T("this control requires a valid date") );
feb72429
VZ
248
249 SYSTEMTIME st;
3200f37d
VZ
250 if ( dt.IsValid() )
251 wxToSystemTime(&st, dt);
252 if ( !DateTime_SetSystemtime(GetHwnd(),
253 dt.IsValid() ? GDT_VALID : GDT_NONE,
254 &st) )
feb72429
VZ
255 {
256 wxLogDebug(_T("DateTime_SetSystemtime() failed"));
257 }
e4164aa9 258
5541976c
VZ
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)
28039907 261 m_date = dt;
0bdd8074
VZ
262 if ( m_date.IsValid() )
263 m_date.ResetTime();
feb72429
VZ
264}
265
a236aa20
VZ
266#include <iostream>
267
feb72429
VZ
268wxDateTime wxDatePickerCtrl::GetValue() const
269{
e4164aa9 270#ifdef __WXDEBUG__
feb72429
VZ
271 wxDateTime dt;
272 SYSTEMTIME st;
273 if ( DateTime_GetSystemtime(GetHwnd(), &st) == GDT_VALID )
274 {
275 wxFromSystemTime(&dt, st);
276 }
277
e4164aa9
VZ
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__
282
283 return m_date;
feb72429
VZ
284}
285
286void wxDatePickerCtrl::SetRange(const wxDateTime& dt1, const wxDateTime& dt2)
287{
288 SYSTEMTIME st[2];
289
290 DWORD flags = 0;
291 if ( dt1.IsValid() )
292 {
293 wxToSystemTime(&st[0], dt1);
294 flags |= GDTR_MIN;
295 }
296
297 if ( dt2.IsValid() )
298 {
299 wxToSystemTime(&st[1], dt2);
300 flags |= GDTR_MAX;
301 }
302
303 if ( !DateTime_SetRange(GetHwnd(), flags, st) )
304 {
305 wxLogDebug(_T("DateTime_SetRange() failed"));
306 }
307}
308
309bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
310{
311 SYSTEMTIME st[2];
312
313 DWORD flags = DateTime_GetRange(GetHwnd(), st);
314 if ( dt1 )
315 {
316 if ( flags & GDTR_MIN )
317 wxFromSystemTime(dt1, st[0]);
318 else
319 *dt1 = wxDefaultDateTime;
320 }
321
322 if ( dt2 )
323 {
324 if ( flags & GDTR_MAX )
325 wxFromSystemTime(dt2, st[1]);
326 else
327 *dt2 = wxDefaultDateTime;
328 }
329
330 return flags != 0;
331}
332
333// ----------------------------------------------------------------------------
334// wxDatePickerCtrl events
335// ----------------------------------------------------------------------------
336
337bool
338wxDatePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
339{
340 NMHDR* hdr = (NMHDR *)lParam;
341 switch ( hdr->code )
342 {
343 case DTN_DATETIMECHANGE:
b4446c24 344 {
feb72429
VZ
345 NMDATETIMECHANGE *dtch = (NMDATETIMECHANGE *)hdr;
346 wxDateTime dt;
347 if ( dtch->dwFlags == GDT_VALID )
348 wxFromSystemTime(&dt, dtch->st);
349
e4164aa9
VZ
350 // filter out duplicate DTN_DATETIMECHANGE events which the native
351 // control sends us when using wxDP_DROPDOWN style
0c8433d0
VZ
352 if ( (m_date.IsValid() != dt.IsValid()) ||
353 (m_date.IsValid() && dt != m_date) )
feb72429 354 {
e4164aa9
VZ
355 m_date = dt;
356 wxDateEvent event(this, dt, wxEVT_DATE_CHANGED);
357 if ( GetEventHandler()->ProcessEvent(event) )
358 {
359 *result = 0;
360 return true;
361 }
feb72429 362 }
0c8433d0 363 //else: both the old and new values are invalid, nothing changed
b4446c24 364 }
feb72429
VZ
365 }
366
367 return wxDatePickerCtrlBase::MSWOnNotify(idCtrl, lParam, result);
368}
369
9b877d18 370#endif // wxUSE_DATEPICKCTRL