generate the correct events in the native MSW version of wxCalendarCtrl
[wxWidgets.git] / src / msw / calctrl.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/calctrl.cpp
3 // Purpose: wxCalendarCtrl implementation
4 // Author: Vadim Zeitlin
5 // Created: 2008-04-04
6 // RCS-ID: $Id$
7 // Copyright: (C) 2008 Vadim Zeitlin <vadim@wxwidgets.org>
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 #include "wx/wxprec.h"
20
21 #ifdef __BORLANDC__
22 #pragma hdrstop
23 #endif
24
25 #if wxUSE_CALENDARCTRL
26
27 #ifndef WX_PRECOMP
28 #include "wx/msw/wrapwin.h"
29 #include "wx/msw/wrapcctl.h" // include <commctrl.h> "properly"
30 #include "wx/msw/private.h"
31 #endif
32
33 #include "wx/calctrl.h"
34
35 #include "wx/msw/private/datecontrols.h"
36
37 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
38
39 // ============================================================================
40 // implementation
41 // ============================================================================
42
43 // ----------------------------------------------------------------------------
44 // wxCalendarCtrl creation
45 // ----------------------------------------------------------------------------
46
47 bool
48 wxCalendarCtrl::Create(wxWindow *parent,
49 wxWindowID id,
50 const wxDateTime& dt,
51 const wxPoint& pos,
52 const wxSize& size,
53 long style,
54 const wxString& name)
55 {
56 if ( !wxMSWDateControls::CheckInitialization() )
57 return false;
58
59 // initialize the base class
60 if ( !CreateControl(parent, id, pos, size, style, wxDefaultValidator, name) )
61 return false;
62
63 // create the native control
64 if ( !MSWCreateControl(MONTHCAL_CLASS, wxEmptyString, pos, size) )
65 return false;
66
67 SetDate(dt.IsValid() ? dt : wxDateTime::Today());
68
69 return true;
70 }
71
72 WXDWORD wxCalendarCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
73 {
74 WXDWORD styleMSW = wxCalendarCtrlBase::MSWGetStyle(style, exstyle);
75
76 // right now we don't support any native styles but we should add wx styles
77 // corresponding to MCS_NOTODAY, MCS_NOTODAYCIRCLE and MCS_WEEKNUMBERS
78 // probably (TODO)
79
80 // for compatibility with the other versions, just turn off today display
81 // unconditionally for now
82 styleMSW |= MCS_NOTODAY;
83
84 return styleMSW;
85 }
86
87 // TODO: handle WM_WININICHANGE
88
89 // ----------------------------------------------------------------------------
90 // wxCalendarCtrl geometry
91 // ----------------------------------------------------------------------------
92
93 wxSize wxCalendarCtrl::DoGetBestSize() const
94 {
95 RECT rc;
96 if ( !GetHwnd() || !MonthCal_GetMinReqRect(GetHwnd(), &rc) )
97 {
98 return wxCalendarCtrlBase::DoGetBestSize();
99 }
100
101 const wxSize best = wxRectFromRECT(rc).GetSize() + GetWindowBorderSize();
102 CacheBestSize(best);
103 return best;
104 }
105
106 wxCalendarHitTestResult
107 wxCalendarCtrl::HitTest(const wxPoint& pos,
108 wxDateTime *date,
109 wxDateTime::WeekDay *wd)
110 {
111 WinStruct<MCHITTESTINFO> hti;
112 hti.pt.x = pos.x;
113 hti.pt.y = pos.y;
114 switch ( MonthCal_HitTest(GetHwnd(), &hti) )
115 {
116 default:
117 case MCHT_CALENDARWEEKNUM:
118 wxFAIL_MSG( "unexpected" );
119 // fall through
120
121 case MCHT_NOWHERE:
122 case MCHT_CALENDARBK:
123 case MCHT_TITLEBK:
124 case MCHT_TITLEMONTH:
125 case MCHT_TITLEYEAR:
126 return wxCAL_HITTEST_NOWHERE;
127
128 case MCHT_CALENDARDATE:
129 if ( date )
130 wxMSWDateControls::FromSystemTime(date, hti.st);
131 return wxCAL_HITTEST_DAY;
132
133 case MCHT_CALENDARDAY:
134 if ( wd )
135 {
136 *wd = wx_static_cast(wxDateTime::WeekDay, hti.st.wDayOfWeek);
137 }
138 return wxCAL_HITTEST_HEADER;
139
140 case MCHT_TITLEBTNNEXT:
141 return wxCAL_HITTEST_INCMONTH;
142
143 case MCHT_TITLEBTNPREV:
144 return wxCAL_HITTEST_DECMONTH;
145
146 case MCHT_CALENDARDATENEXT:
147 case MCHT_CALENDARDATEPREV:
148 return wxCAL_HITTEST_SURROUNDING_WEEK;
149 }
150 }
151
152 // ----------------------------------------------------------------------------
153 // wxCalendarCtrl operations
154 // ----------------------------------------------------------------------------
155
156 bool wxCalendarCtrl::SetDate(const wxDateTime& dt)
157 {
158 wxCHECK_MSG( dt.IsValid(), false, "invalid date" );
159
160 SYSTEMTIME st;
161 wxMSWDateControls::ToSystemTime(&st, dt);
162 if ( !MonthCal_SetCurSel(GetHwnd(), &st) )
163 {
164 wxLogDebug(_T("DateTime_SetSystemtime() failed"));
165
166 return false;
167 }
168
169 m_date = dt;
170
171 return true;
172 }
173
174 wxDateTime wxCalendarCtrl::GetDate() const
175 {
176 #ifdef __WXDEBUG__
177 SYSTEMTIME st;
178 if ( !MonthCal_GetCurSel(GetHwnd(), &st) )
179 {
180 wxASSERT_MSG( !m_date.IsValid(), "mismatch between data and control" );
181
182 return wxDefaultDateTime;
183 }
184
185 wxDateTime dt;
186 wxMSWDateControls::FromSystemTime(&dt, st);
187
188 wxASSERT_MSG( dt == m_date, "mismatch between data and control" );
189 #endif // __WXDEBUG__
190
191 return m_date;
192 }
193
194 bool wxCalendarCtrl::SetDateRange(const wxDateTime& dt1, const wxDateTime& dt2)
195 {
196 SYSTEMTIME st[2];
197
198 DWORD flags = 0;
199 if ( dt1.IsValid() )
200 {
201 wxMSWDateControls::ToSystemTime(&st[0], dt1);
202 flags |= GDTR_MIN;
203 }
204
205 if ( dt2.IsValid() )
206 {
207 wxMSWDateControls::ToSystemTime(&st[1], dt2);
208 flags |= GDTR_MAX;
209 }
210
211 if ( !MonthCal_SetRange(GetHwnd(), flags, st) )
212 {
213 wxLogDebug(_T("MonthCal_SetRange() failed"));
214 }
215
216 return flags != 0;
217 }
218
219 bool wxCalendarCtrl::GetDateRange(wxDateTime *dt1, wxDateTime *dt2) const
220 {
221 SYSTEMTIME st[2];
222
223 DWORD flags = MonthCal_GetRange(GetHwnd(), st);
224 if ( dt1 )
225 {
226 if ( flags & GDTR_MIN )
227 wxMSWDateControls::FromSystemTime(dt1, st[0]);
228 else
229 *dt1 = wxDefaultDateTime;
230 }
231
232 if ( dt2 )
233 {
234 if ( flags & GDTR_MAX )
235 wxMSWDateControls::FromSystemTime(dt2, st[1]);
236 else
237 *dt2 = wxDefaultDateTime;
238 }
239
240 return flags != 0;
241 }
242
243 // ----------------------------------------------------------------------------
244 // other wxCalendarCtrl operations
245 // ----------------------------------------------------------------------------
246
247 bool wxCalendarCtrl::EnableMonthChange(bool enable)
248 {
249 if ( !wxCalendarCtrlBase::EnableMonthChange(enable) )
250 return false;
251
252 wxDateTime dtStart, dtEnd;
253 if ( !enable )
254 {
255 dtStart = GetDate();
256 dtStart.SetDay(1);
257
258 dtEnd = dtStart.GetLastMonthDay();
259 }
260 //else: leave them invalid to remove the restriction
261
262 SetDateRange(dtStart, dtEnd);
263
264 return true;
265 }
266
267 void wxCalendarCtrl::Mark(size_t day, bool mark)
268 {
269 wxFAIL_MSG( "not implemented" );
270 }
271
272 // ----------------------------------------------------------------------------
273 // wxCalendarCtrl events
274 // ----------------------------------------------------------------------------
275
276 bool wxCalendarCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
277 {
278 NMHDR* hdr = (NMHDR *)lParam;
279 switch ( hdr->code )
280 {
281 case MCN_SELCHANGE:
282 // we need to update m_date first, before calling the user code
283 // which expects GetDate() to return the new date
284 const wxDateTime dateOld = m_date;
285 const NMSELCHANGE * const sch = (NMSELCHANGE *)lParam;
286 wxMSWDateControls::FromSystemTime(&m_date, sch->stSelStart);
287
288 // changing the year or the month results in a second dummy
289 // MCN_SELCHANGE event on this system which doesn't really change
290 // anything -- filter it out
291 if ( m_date != dateOld )
292 {
293 GenerateAllChangeEvents(dateOld);
294
295 *result = 0;
296 return true;
297 }
298 }
299
300 return wxCalendarCtrlBase::MSWOnNotify(idCtrl, lParam, result);
301 }
302
303 #endif // wxUSE_CALENDARCTRL