]> git.saurik.com Git - wxWidgets.git/blob - src/msw/datectrl.cpp
Check for invalid items in generic wxDataViewCtrl::GetSelections().
[wxWidgets.git] / src / msw / datectrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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 #if wxUSE_DATEPICKCTRL
27
28 #ifndef WX_PRECOMP
29 #include "wx/msw/wrapwin.h"
30 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
31 #include "wx/app.h"
32 #include "wx/intl.h"
33 #include "wx/dcclient.h"
34 #include "wx/settings.h"
35 #include "wx/msw/private.h"
36 #endif
37
38 #include "wx/datectrl.h"
39
40 #include "wx/msw/private/datecontrols.h"
41
42 #include "wx/dateevt.h"
43
44 // apparently some versions of mingw define these macros erroneously
45 #ifndef DateTime_GetSystemtime
46 #define DateTime_GetSystemtime DateTime_GetSystemTime
47 #endif
48
49 #ifndef DateTime_SetSystemtime
50 #define DateTime_SetSystemtime DateTime_SetSystemTime
51 #endif
52
53 IMPLEMENT_DYNAMIC_CLASS(wxDatePickerCtrl, wxControl)
54
55 // ============================================================================
56 // implementation
57 // ============================================================================
58
59 // ----------------------------------------------------------------------------
60 // wxDatePickerCtrl creation
61 // ----------------------------------------------------------------------------
62
63 bool
64 wxDatePickerCtrl::Create(wxWindow *parent,
65 wxWindowID id,
66 const wxDateTime& dt,
67 const wxPoint& pos,
68 const wxSize& size,
69 long style,
70 const wxValidator& validator,
71 const wxString& name)
72 {
73 if ( !wxMSWDateControls::CheckInitialization() )
74 return false;
75
76 // use wxDP_SPIN if wxDP_DEFAULT (0) was given as style
77 if ( !(style & wxDP_DROPDOWN) )
78 style |= wxDP_SPIN;
79
80 // initialize the base class
81 if ( !CreateControl(parent, id, pos, size, style, validator, name) )
82 return false;
83
84 // create the native control
85 if ( !MSWCreateControl(DATETIMEPICK_CLASS, wxEmptyString, pos, size) )
86 return false;
87
88 if ( dt.IsValid() || (style & wxDP_ALLOWNONE) )
89 SetValue(dt);
90 else
91 SetValue(wxDateTime::Today());
92
93 return true;
94 }
95
96 WXDWORD wxDatePickerCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
97 {
98 WXDWORD styleMSW = wxDatePickerCtrlBase::MSWGetStyle(style, exstyle);
99
100 // although MSDN doesn't mention it, DTS_UPDOWN doesn't work with
101 // comctl32.dll 4.72
102 if ( wxApp::GetComCtl32Version() > 472 && (style & wxDP_SPIN) )
103 styleMSW |= DTS_UPDOWN;
104 //else: drop down by default
105
106 #ifdef DTS_SHORTDATECENTURYFORMAT
107 if ( style & wxDP_SHOWCENTURY )
108 styleMSW |= DTS_SHORTDATECENTURYFORMAT;
109 else
110 #endif // DTS_SHORTDATECENTURYFORMAT
111 styleMSW |= DTS_SHORTDATEFORMAT;
112
113 if ( style & wxDP_ALLOWNONE )
114 styleMSW |= DTS_SHOWNONE;
115
116 return styleMSW;
117 }
118
119 // TODO: handle WM_WININICHANGE
120
121 // ----------------------------------------------------------------------------
122 // wxDatePickerCtrl geometry
123 // ----------------------------------------------------------------------------
124
125 wxSize wxDatePickerCtrl::DoGetBestSize() const
126 {
127 wxClientDC dc(const_cast<wxDatePickerCtrl *>(this));
128
129 // we can't use FormatDate() here as the CRT doesn't always use the same
130 // format as the date picker control
131 wxString s;
132 for ( int len = 100; ; len *= 2 )
133 {
134 if ( ::GetDateFormat
135 (
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
142 ) )
143 {
144 // success
145 break;
146 }
147
148 const DWORD rc = ::GetLastError();
149 if ( rc != ERROR_INSUFFICIENT_BUFFER )
150 {
151 wxLogApiError(wxT("GetDateFormat"), rc);
152
153 // fall back on wxDateTime, what else to do?
154 s = wxDateTime::Today().FormatDate();
155 break;
156 }
157 }
158
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
163 s += wxT("WW");
164
165 int x, y;
166 dc.GetTextExtent(s, &x, &y);
167
168 // account for the drop-down arrow or spin arrows
169 x += wxSystemSettings::GetMetric(wxSYS_HSCROLL_ARROW_X);
170
171 // and for the checkbox if we have it
172 if ( HasFlag(wxDP_ALLOWNONE) )
173 x += 3*GetCharWidth();
174
175 wxSize best(x, EDIT_HEIGHT_FROM_CHAR_HEIGHT(y));
176 CacheBestSize(best);
177 return best;
178 }
179
180 // ----------------------------------------------------------------------------
181 // wxDatePickerCtrl operations
182 // ----------------------------------------------------------------------------
183
184 void wxDatePickerCtrl::SetValue(const wxDateTime& dt)
185 {
186 wxCHECK_RET( dt.IsValid() || HasFlag(wxDP_ALLOWNONE),
187 wxT("this control requires a valid date") );
188
189 SYSTEMTIME st;
190 if ( dt.IsValid() )
191 {
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.
199 //
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
202 // future.
203 wxDateTime dtStart, dtEnd;
204 GetRange(&dtStart, &dtEnd);
205 if ( (dtStart.IsValid() && dt < dtStart) ||
206 (dtEnd.IsValid() && dt > dtEnd) )
207 {
208 // Fail silently, some existing code relies on SetValue() with an
209 // out of range value simply doing nothing -- so don't.
210 return;
211 }
212
213 dt.GetAsMSWSysTime(&st);
214 }
215
216 if ( !DateTime_SetSystemtime(GetHwnd(),
217 dt.IsValid() ? GDT_VALID : GDT_NONE,
218 &st) )
219 {
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.") );
223
224 // In any case, skip updating m_date below.
225 return;
226 }
227
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)
230 m_date = dt;
231 if ( m_date.IsValid() )
232 m_date.ResetTime();
233 }
234
235 wxDateTime wxDatePickerCtrl::GetValue() const
236 {
237 #if wxDEBUG_LEVEL
238 wxDateTime dt;
239 SYSTEMTIME st;
240 if ( DateTime_GetSystemtime(GetHwnd(), &st) == GDT_VALID )
241 {
242 dt.SetFromMSWSysDate(st);
243 }
244
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
249
250 return m_date;
251 }
252
253 void wxDatePickerCtrl::SetRange(const wxDateTime& dt1, const wxDateTime& dt2)
254 {
255 SYSTEMTIME st[2];
256
257 DWORD flags = 0;
258 if ( dt1.IsValid() )
259 {
260 dt1.GetAsMSWSysTime(st + 0);
261 flags |= GDTR_MIN;
262 }
263
264 if ( dt2.IsValid() )
265 {
266 dt2.GetAsMSWSysTime(st + 1);
267 flags |= GDTR_MAX;
268 }
269
270 if ( !DateTime_SetRange(GetHwnd(), flags, st) )
271 {
272 wxLogDebug(wxT("DateTime_SetRange() failed"));
273 }
274 }
275
276 bool wxDatePickerCtrl::GetRange(wxDateTime *dt1, wxDateTime *dt2) const
277 {
278 SYSTEMTIME st[2];
279
280 DWORD flags = DateTime_GetRange(GetHwnd(), st);
281 if ( dt1 )
282 {
283 if ( flags & GDTR_MIN )
284 dt1->SetFromMSWSysDate(st[0]);
285 else
286 *dt1 = wxDefaultDateTime;
287 }
288
289 if ( dt2 )
290 {
291 if ( flags & GDTR_MAX )
292 dt2->SetFromMSWSysDate(st[1]);
293 else
294 *dt2 = wxDefaultDateTime;
295 }
296
297 return flags != 0;
298 }
299
300 // ----------------------------------------------------------------------------
301 // wxDatePickerCtrl events
302 // ----------------------------------------------------------------------------
303
304 bool
305 wxDatePickerCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
306 {
307 NMHDR* hdr = (NMHDR *)lParam;
308 switch ( hdr->code )
309 {
310 case DTN_DATETIMECHANGE:
311 {
312 NMDATETIMECHANGE *dtch = (NMDATETIMECHANGE *)hdr;
313 wxDateTime dt;
314 if ( dtch->dwFlags == GDT_VALID )
315 dt.SetFromMSWSysDate(dtch->st);
316
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) )
321 {
322 m_date = dt;
323 wxDateEvent event(this, dt, wxEVT_DATE_CHANGED);
324 if ( HandleWindowEvent(event) )
325 {
326 *result = 0;
327 return true;
328 }
329 }
330 //else: both the old and new values are invalid, nothing changed
331 }
332 }
333
334 return wxDatePickerCtrlBase::MSWOnNotify(idCtrl, lParam, result);
335 }
336
337 #endif // wxUSE_DATEPICKCTRL