generate double click 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: this is a bit tricky as we want to receive
64 // double click events but the MONTHCAL_CLASS doesn't use CS_DBLCLKS style
65 // and so we create our own copy of it which does
66 static ClassRegistrar s_clsMonthCal;
67 if ( !s_clsMonthCal.IsInitialized() )
68 {
69 // get a copy of standard class and modify it
70 WNDCLASS wc;
71 if ( ::GetClassInfo(NULL, MONTHCAL_CLASS, &wc) )
72 {
73 wc.lpszClassName = wxT("_wx_SysMonthCtl32");
74 wc.style |= CS_DBLCLKS;
75 s_clsMonthCal.Register(wc);
76 }
77 else
78 {
79 wxLogLastError(_T("GetClassInfoEx(SysMonthCal32)"));
80 }
81 }
82
83 const wxChar * const clsname = s_clsMonthCal.IsRegistered()
84 ? s_clsMonthCal.GetName().wx_str()
85 : MONTHCAL_CLASS;
86
87 if ( !MSWCreateControl(clsname, wxEmptyString, pos, size) )
88 return false;
89
90 SetDate(dt.IsValid() ? dt : wxDateTime::Today());
91
92 Connect(wxEVT_LEFT_DOWN,
93 wxMouseEventHandler(wxCalendarCtrl::MSWOnDoubleClick));
94
95 return true;
96 }
97
98 WXDWORD wxCalendarCtrl::MSWGetStyle(long style, WXDWORD *exstyle) const
99 {
100 WXDWORD styleMSW = wxCalendarCtrlBase::MSWGetStyle(style, exstyle);
101
102 // right now we don't support any native styles but we should add wx styles
103 // corresponding to MCS_NOTODAY, MCS_NOTODAYCIRCLE and MCS_WEEKNUMBERS
104 // probably (TODO)
105
106 // for compatibility with the other versions, just turn off today display
107 // unconditionally for now
108 styleMSW |= MCS_NOTODAY;
109
110 return styleMSW;
111 }
112
113 // TODO: handle WM_WININICHANGE
114
115 // ----------------------------------------------------------------------------
116 // wxCalendarCtrl geometry
117 // ----------------------------------------------------------------------------
118
119 wxSize wxCalendarCtrl::DoGetBestSize() const
120 {
121 RECT rc;
122 if ( !GetHwnd() || !MonthCal_GetMinReqRect(GetHwnd(), &rc) )
123 {
124 return wxCalendarCtrlBase::DoGetBestSize();
125 }
126
127 const wxSize best = wxRectFromRECT(rc).GetSize() + GetWindowBorderSize();
128 CacheBestSize(best);
129 return best;
130 }
131
132 wxCalendarHitTestResult
133 wxCalendarCtrl::HitTest(const wxPoint& pos,
134 wxDateTime *date,
135 wxDateTime::WeekDay *wd)
136 {
137 WinStruct<MCHITTESTINFO> hti;
138 hti.pt.x = pos.x;
139 hti.pt.y = pos.y;
140 switch ( MonthCal_HitTest(GetHwnd(), &hti) )
141 {
142 default:
143 case MCHT_CALENDARWEEKNUM:
144 wxFAIL_MSG( "unexpected" );
145 // fall through
146
147 case MCHT_NOWHERE:
148 case MCHT_CALENDARBK:
149 case MCHT_TITLEBK:
150 case MCHT_TITLEMONTH:
151 case MCHT_TITLEYEAR:
152 return wxCAL_HITTEST_NOWHERE;
153
154 case MCHT_CALENDARDATE:
155 if ( date )
156 wxMSWDateControls::FromSystemTime(date, hti.st);
157 return wxCAL_HITTEST_DAY;
158
159 case MCHT_CALENDARDAY:
160 if ( wd )
161 {
162 *wd = wx_static_cast(wxDateTime::WeekDay, hti.st.wDayOfWeek);
163 }
164 return wxCAL_HITTEST_HEADER;
165
166 case MCHT_TITLEBTNNEXT:
167 return wxCAL_HITTEST_INCMONTH;
168
169 case MCHT_TITLEBTNPREV:
170 return wxCAL_HITTEST_DECMONTH;
171
172 case MCHT_CALENDARDATENEXT:
173 case MCHT_CALENDARDATEPREV:
174 return wxCAL_HITTEST_SURROUNDING_WEEK;
175 }
176 }
177
178 // ----------------------------------------------------------------------------
179 // wxCalendarCtrl operations
180 // ----------------------------------------------------------------------------
181
182 bool wxCalendarCtrl::SetDate(const wxDateTime& dt)
183 {
184 wxCHECK_MSG( dt.IsValid(), false, "invalid date" );
185
186 SYSTEMTIME st;
187 wxMSWDateControls::ToSystemTime(&st, dt);
188 if ( !MonthCal_SetCurSel(GetHwnd(), &st) )
189 {
190 wxLogDebug(_T("DateTime_SetSystemtime() failed"));
191
192 return false;
193 }
194
195 m_date = dt;
196
197 return true;
198 }
199
200 wxDateTime wxCalendarCtrl::GetDate() const
201 {
202 #ifdef __WXDEBUG__
203 SYSTEMTIME st;
204 if ( !MonthCal_GetCurSel(GetHwnd(), &st) )
205 {
206 wxASSERT_MSG( !m_date.IsValid(), "mismatch between data and control" );
207
208 return wxDefaultDateTime;
209 }
210
211 wxDateTime dt;
212 wxMSWDateControls::FromSystemTime(&dt, st);
213
214 wxASSERT_MSG( dt == m_date, "mismatch between data and control" );
215 #endif // __WXDEBUG__
216
217 return m_date;
218 }
219
220 bool wxCalendarCtrl::SetDateRange(const wxDateTime& dt1, const wxDateTime& dt2)
221 {
222 SYSTEMTIME st[2];
223
224 DWORD flags = 0;
225 if ( dt1.IsValid() )
226 {
227 wxMSWDateControls::ToSystemTime(&st[0], dt1);
228 flags |= GDTR_MIN;
229 }
230
231 if ( dt2.IsValid() )
232 {
233 wxMSWDateControls::ToSystemTime(&st[1], dt2);
234 flags |= GDTR_MAX;
235 }
236
237 if ( !MonthCal_SetRange(GetHwnd(), flags, st) )
238 {
239 wxLogDebug(_T("MonthCal_SetRange() failed"));
240 }
241
242 return flags != 0;
243 }
244
245 bool wxCalendarCtrl::GetDateRange(wxDateTime *dt1, wxDateTime *dt2) const
246 {
247 SYSTEMTIME st[2];
248
249 DWORD flags = MonthCal_GetRange(GetHwnd(), st);
250 if ( dt1 )
251 {
252 if ( flags & GDTR_MIN )
253 wxMSWDateControls::FromSystemTime(dt1, st[0]);
254 else
255 *dt1 = wxDefaultDateTime;
256 }
257
258 if ( dt2 )
259 {
260 if ( flags & GDTR_MAX )
261 wxMSWDateControls::FromSystemTime(dt2, st[1]);
262 else
263 *dt2 = wxDefaultDateTime;
264 }
265
266 return flags != 0;
267 }
268
269 // ----------------------------------------------------------------------------
270 // other wxCalendarCtrl operations
271 // ----------------------------------------------------------------------------
272
273 bool wxCalendarCtrl::EnableMonthChange(bool enable)
274 {
275 if ( !wxCalendarCtrlBase::EnableMonthChange(enable) )
276 return false;
277
278 wxDateTime dtStart, dtEnd;
279 if ( !enable )
280 {
281 dtStart = GetDate();
282 dtStart.SetDay(1);
283
284 dtEnd = dtStart.GetLastMonthDay();
285 }
286 //else: leave them invalid to remove the restriction
287
288 SetDateRange(dtStart, dtEnd);
289
290 return true;
291 }
292
293 void wxCalendarCtrl::Mark(size_t day, bool mark)
294 {
295 wxFAIL_MSG( "not implemented" );
296 }
297
298 // ----------------------------------------------------------------------------
299 // wxCalendarCtrl events
300 // ----------------------------------------------------------------------------
301
302 bool wxCalendarCtrl::MSWOnNotify(int idCtrl, WXLPARAM lParam, WXLPARAM *result)
303 {
304 NMHDR* hdr = (NMHDR *)lParam;
305 switch ( hdr->code )
306 {
307 case MCN_SELCHANGE:
308 // we need to update m_date first, before calling the user code
309 // which expects GetDate() to return the new date
310 const wxDateTime dateOld = m_date;
311 const NMSELCHANGE * const sch = (NMSELCHANGE *)lParam;
312 wxMSWDateControls::FromSystemTime(&m_date, sch->stSelStart);
313
314 // changing the year or the month results in a second dummy
315 // MCN_SELCHANGE event on this system which doesn't really change
316 // anything -- filter it out
317 if ( m_date != dateOld )
318 {
319 GenerateAllChangeEvents(dateOld);
320
321 *result = 0;
322 return true;
323 }
324 }
325
326 return wxCalendarCtrlBase::MSWOnNotify(idCtrl, lParam, result);
327 }
328
329 void wxCalendarCtrl::MSWOnDoubleClick(wxMouseEvent& event)
330 {
331 if ( HitTest(event.GetPosition()) == wxCAL_HITTEST_DAY )
332 {
333 if ( GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED) )
334 return; // skip event.Skip() below
335 }
336
337 event.Skip();
338 }
339
340 #endif // wxUSE_CALENDARCTRL