]>
Commit | Line | Data |
---|---|---|
2ef31e80 VZ |
1 | /////////////////////////////////////////////////////////////////////////////// |
2 | // Name: generic/calctrl.cpp | |
3 | // Purpose: implementation fo the generic wxCalendarCtrl | |
4 | // Author: Vadim Zeitlin | |
5 | // Modified by: | |
6 | // Created: 29.12.99 | |
7 | // RCS-ID: $Id$ | |
8 | // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> | |
9 | // Licence: wxWindows license | |
10 | /////////////////////////////////////////////////////////////////////////////// | |
11 | ||
12 | // ============================================================================ | |
13 | // declarations | |
14 | // ============================================================================ | |
15 | ||
16 | // ---------------------------------------------------------------------------- | |
17 | // headers | |
18 | // ---------------------------------------------------------------------------- | |
19 | ||
20 | #ifdef __GNUG__ | |
21 | #pragma implementation "calctrl.h" | |
22 | #endif | |
23 | ||
24 | // For compilers that support precompilation, includes "wx.h". | |
25 | #include "wx/wxprec.h" | |
26 | ||
27 | #ifdef __BORLANDC__ | |
28 | #pragma hdrstop | |
29 | #endif | |
30 | ||
31 | #ifndef WX_PRECOMP | |
9d9b7755 VZ |
32 | #include "wx/dcclient.h" |
33 | #include "wx/settings.h" | |
34 | #include "wx/brush.h" | |
2ef31e80 VZ |
35 | #endif //WX_PRECOMP |
36 | ||
9d9b7755 | 37 | #include "wx/calctrl.h" |
2ef31e80 | 38 | |
882a8f40 VZ |
39 | #define DEBUG_PAINT 0 |
40 | ||
41 | // ---------------------------------------------------------------------------- | |
42 | // private classes | |
43 | // ---------------------------------------------------------------------------- | |
44 | ||
45 | class wxMonthComboBox : public wxComboBox | |
46 | { | |
47 | public: | |
48 | wxMonthComboBox(wxCalendarCtrl *cal); | |
49 | ||
50 | void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); } | |
51 | ||
52 | private: | |
53 | wxCalendarCtrl *m_cal; | |
54 | ||
55 | DECLARE_EVENT_TABLE() | |
56 | }; | |
57 | ||
58 | class wxYearSpinCtrl : public wxSpinCtrl | |
59 | { | |
60 | public: | |
61 | wxYearSpinCtrl(wxCalendarCtrl *cal); | |
62 | ||
63 | void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); } | |
64 | ||
65 | private: | |
66 | wxCalendarCtrl *m_cal; | |
67 | ||
68 | DECLARE_EVENT_TABLE() | |
69 | }; | |
70 | ||
2ef31e80 VZ |
71 | // ---------------------------------------------------------------------------- |
72 | // wxWin macros | |
73 | // ---------------------------------------------------------------------------- | |
74 | ||
75 | BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl) | |
76 | EVT_PAINT(wxCalendarCtrl::OnPaint) | |
77 | ||
9d9b7755 VZ |
78 | EVT_CHAR(wxCalendarCtrl::OnChar) |
79 | ||
2ef31e80 | 80 | EVT_LEFT_DOWN(wxCalendarCtrl::OnClick) |
0185cd09 | 81 | EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick) |
882a8f40 VZ |
82 | END_EVENT_TABLE() |
83 | ||
84 | BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox) | |
85 | EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange) | |
86 | END_EVENT_TABLE() | |
9d9b7755 | 87 | |
882a8f40 VZ |
88 | BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl) |
89 | EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange) | |
2ef31e80 VZ |
90 | END_EVENT_TABLE() |
91 | ||
92 | IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl) | |
93 | ||
94 | // ============================================================================ | |
95 | // implementation | |
96 | // ============================================================================ | |
97 | ||
882a8f40 VZ |
98 | // ---------------------------------------------------------------------------- |
99 | // wxMonthComboBox and wxYearSpinCtrl | |
100 | // ---------------------------------------------------------------------------- | |
101 | ||
102 | wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal) | |
103 | : wxComboBox(cal->GetParent(), -1, | |
104 | wxEmptyString, | |
105 | wxDefaultPosition, | |
106 | wxDefaultSize, | |
107 | 0, NULL, | |
108 | wxCB_READONLY) | |
109 | { | |
110 | m_cal = cal; | |
111 | ||
112 | wxDateTime::Month m; | |
113 | for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) ) | |
114 | { | |
115 | Append(wxDateTime::GetMonthName(m)); | |
116 | } | |
117 | ||
118 | SetSelection(m_cal->GetDate().GetMonth()); | |
119 | } | |
120 | ||
121 | wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal) | |
122 | : wxSpinCtrl(cal->GetParent(), -1, | |
123 | cal->GetDate().Format(_T("%Y")), | |
124 | wxDefaultPosition, | |
125 | wxDefaultSize, | |
126 | wxSP_ARROW_KEYS, | |
127 | -4300, 10000, cal->GetDate().GetYear()) | |
128 | { | |
129 | m_cal = cal; | |
130 | } | |
131 | ||
2ef31e80 VZ |
132 | // ---------------------------------------------------------------------------- |
133 | // wxCalendarCtrl | |
134 | // ---------------------------------------------------------------------------- | |
135 | ||
136 | void wxCalendarCtrl::Init() | |
137 | { | |
138 | m_comboMonth = NULL; | |
139 | m_spinYear = NULL; | |
140 | ||
141 | m_widthCol = | |
142 | m_heightRow = 0; | |
9d9b7755 VZ |
143 | |
144 | wxDateTime::WeekDay wd; | |
145 | for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) ) | |
146 | { | |
147 | m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr); | |
148 | } | |
2ef31e80 VZ |
149 | } |
150 | ||
13111b2a VZ |
151 | bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent), |
152 | wxWindowID WXUNUSED(id), | |
2ef31e80 | 153 | const wxDateTime& date, |
13111b2a | 154 | const wxPoint& WXUNUSED(pos), |
2ef31e80 VZ |
155 | const wxSize& size, |
156 | long style, | |
13111b2a | 157 | const wxString& WXUNUSED(name)) |
2ef31e80 | 158 | { |
13111b2a | 159 | SetWindowStyle(style | (wxRAISED_BORDER | wxWANTS_CHARS)); |
2ef31e80 | 160 | |
882a8f40 | 161 | m_date = date.IsValid() ? date : wxDateTime::Today(); |
9d9b7755 | 162 | |
882a8f40 VZ |
163 | m_comboMonth = new wxMonthComboBox(this); |
164 | m_spinYear = new wxYearSpinCtrl(this); | |
9d9b7755 | 165 | |
2ef31e80 VZ |
166 | wxSize sizeReal; |
167 | if ( size.x == -1 || size.y == -1 ) | |
168 | { | |
169 | sizeReal = DoGetBestSize(); | |
170 | if ( size.x != -1 ) | |
171 | sizeReal.x = size.x; | |
172 | if ( size.y != -1 ) | |
173 | sizeReal.y = size.y; | |
174 | } | |
175 | else | |
176 | { | |
177 | sizeReal = size; | |
178 | } | |
179 | ||
180 | SetSize(sizeReal); | |
181 | ||
182 | SetBackgroundColour(*wxWHITE); | |
9d9b7755 | 183 | SetFont(*wxSWISS_FONT); |
2ef31e80 VZ |
184 | |
185 | return TRUE; | |
186 | } | |
187 | ||
882a8f40 VZ |
188 | wxCalendarCtrl::~wxCalendarCtrl() |
189 | { | |
190 | #if 0 | |
191 | m_comboMonth->PopEventHandler(); | |
192 | m_spinYear->PopEventHandler(); | |
193 | #endif // 0 | |
194 | } | |
195 | ||
196 | // ---------------------------------------------------------------------------- | |
197 | // forward wxWin functions to subcontrols | |
198 | // ---------------------------------------------------------------------------- | |
199 | ||
200 | bool wxCalendarCtrl::Show(bool show) | |
201 | { | |
202 | if ( !wxControl::Show(show) ) | |
203 | { | |
204 | return FALSE; | |
205 | } | |
206 | ||
207 | m_comboMonth->Show(show); | |
208 | m_spinYear->Show(show); | |
209 | ||
210 | return TRUE; | |
211 | } | |
212 | ||
213 | bool wxCalendarCtrl::Enable(bool enable) | |
214 | { | |
215 | if ( !wxControl::Enable(enable) ) | |
216 | { | |
217 | return FALSE; | |
218 | } | |
219 | ||
220 | m_comboMonth->Enable(enable); | |
221 | m_spinYear->Enable(enable); | |
222 | ||
223 | return TRUE; | |
224 | } | |
225 | ||
9d9b7755 VZ |
226 | // ---------------------------------------------------------------------------- |
227 | // changing date | |
228 | // ---------------------------------------------------------------------------- | |
229 | ||
230 | void wxCalendarCtrl::SetDate(const wxDateTime& date) | |
231 | { | |
232 | if ( m_date.GetMonth() == date.GetMonth() && | |
233 | m_date.GetYear() == date.GetYear() ) | |
234 | { | |
235 | // just change the day | |
236 | ChangeDay(date); | |
237 | } | |
238 | else | |
239 | { | |
240 | // change everything | |
241 | m_date = date; | |
242 | ||
243 | // update the controls | |
244 | m_comboMonth->SetSelection(m_date.GetMonth()); | |
245 | m_spinYear->SetValue(m_date.Format(_T("%Y"))); | |
246 | ||
247 | // update the calendar | |
248 | Refresh(); | |
249 | } | |
250 | } | |
251 | ||
252 | void wxCalendarCtrl::ChangeDay(const wxDateTime& date) | |
253 | { | |
254 | if ( m_date != date ) | |
255 | { | |
256 | // we need to refresh the row containing the old date and the one | |
257 | // containing the new one | |
258 | wxDateTime dateOld = m_date; | |
259 | m_date = date; | |
260 | ||
261 | RefreshDate(dateOld); | |
262 | ||
263 | // if the date is in the same row, it was already drawn correctly | |
264 | if ( GetWeek(m_date) != GetWeek(dateOld) ) | |
265 | { | |
266 | RefreshDate(m_date); | |
267 | } | |
268 | } | |
269 | } | |
270 | ||
271 | void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date) | |
272 | { | |
273 | wxDateTime::Tm tm1 = m_date.GetTm(), | |
274 | tm2 = date.GetTm(); | |
275 | ||
276 | wxEventType type; | |
277 | if ( tm1.year != tm2.year ) | |
278 | type = wxEVT_CALENDAR_YEAR_CHANGED; | |
279 | else if ( tm1.mon != tm2.mon ) | |
280 | type = wxEVT_CALENDAR_MONTH_CHANGED; | |
281 | else if ( tm1.mday != tm2.mday ) | |
282 | type = wxEVT_CALENDAR_DAY_CHANGED; | |
283 | else | |
284 | return; | |
285 | ||
286 | SetDate(date); | |
287 | ||
288 | GenerateEvent(type); | |
289 | } | |
290 | ||
2ef31e80 VZ |
291 | // ---------------------------------------------------------------------------- |
292 | // date helpers | |
293 | // ---------------------------------------------------------------------------- | |
294 | ||
295 | wxDateTime wxCalendarCtrl::GetStartDate() const | |
296 | { | |
297 | wxDateTime::Tm tm = m_date.GetTm(); | |
298 | ||
299 | wxDateTime date = wxDateTime(1, tm.mon, tm.year); | |
9d9b7755 | 300 | |
1a8557b1 VZ |
301 | // rewind back |
302 | date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST | |
303 | ? wxDateTime::Mon : wxDateTime::Sun); | |
304 | ||
305 | // be sure to do it or it might gain 1 hour if DST changed in between | |
306 | date.ResetTime(); | |
2ef31e80 VZ |
307 | |
308 | return date; | |
309 | } | |
310 | ||
311 | bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const | |
312 | { | |
313 | return date.GetMonth() == m_date.GetMonth(); | |
314 | } | |
315 | ||
9d9b7755 VZ |
316 | size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const |
317 | { | |
1a8557b1 VZ |
318 | return date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST |
319 | ? wxDateTime::Monday_First | |
320 | : wxDateTime::Sunday_First); | |
9d9b7755 VZ |
321 | } |
322 | ||
2ef31e80 VZ |
323 | // ---------------------------------------------------------------------------- |
324 | // size management | |
325 | // ---------------------------------------------------------------------------- | |
326 | ||
9d9b7755 VZ |
327 | // this is a composite control and it must arrange its parts each time its |
328 | // size or position changes: the combobox and spinctrl are along the top of | |
329 | // the available area and the calendar takes up therest of the space | |
330 | ||
331 | // the constants used for the layout | |
332 | #define VERT_MARGIN 5 // distance between combo and calendar | |
333 | #define HORZ_MARGIN 15 // spin | |
334 | ||
2ef31e80 VZ |
335 | wxSize wxCalendarCtrl::DoGetBestSize() const |
336 | { | |
9d9b7755 VZ |
337 | // calc the size of the calendar |
338 | ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast | |
339 | ||
340 | wxCoord width = 7*m_widthCol, | |
341 | height = 7*m_heightRow; | |
342 | ||
343 | wxSize sizeCombo = m_comboMonth->GetBestSize(), | |
344 | sizeSpin = m_spinYear->GetBestSize(); | |
345 | ||
346 | height += VERT_MARGIN + wxMax(sizeCombo.y, sizeSpin.y); | |
347 | ||
348 | return wxSize(width, height); | |
2ef31e80 VZ |
349 | } |
350 | ||
351 | void wxCalendarCtrl::DoSetSize(int x, int y, | |
352 | int width, int height, | |
353 | int sizeFlags) | |
354 | { | |
355 | wxControl::DoSetSize(x, y, width, height, sizeFlags); | |
356 | } | |
357 | ||
358 | void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height) | |
359 | { | |
9d9b7755 VZ |
360 | wxSize sizeCombo = m_comboMonth->GetSize(); |
361 | m_comboMonth->Move(x, y); | |
2ef31e80 | 362 | |
9d9b7755 | 363 | int xDiff = sizeCombo.x + HORZ_MARGIN; |
882a8f40 | 364 | m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y); |
2ef31e80 | 365 | |
9d9b7755 VZ |
366 | wxSize sizeSpin = m_spinYear->GetSize(); |
367 | int yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN; | |
368 | ||
369 | wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff); | |
370 | } | |
371 | ||
882a8f40 VZ |
372 | void wxCalendarCtrl::DoGetPosition(int *x, int *y) const |
373 | { | |
374 | wxControl::DoGetPosition(x, y); | |
375 | ||
376 | // our real top corner is not in this position | |
377 | if ( y ) | |
378 | { | |
379 | *y -= m_comboMonth->GetSize().y + VERT_MARGIN; | |
380 | } | |
381 | } | |
382 | ||
383 | void wxCalendarCtrl::DoGetSize(int *width, int *height) const | |
384 | { | |
385 | wxControl::DoGetSize(width, height); | |
386 | ||
387 | // our real height is bigger | |
388 | if ( height ) | |
389 | { | |
390 | *height += m_comboMonth->GetSize().y + VERT_MARGIN; | |
391 | } | |
392 | } | |
393 | ||
9d9b7755 | 394 | void wxCalendarCtrl::RecalcGeometry() |
2ef31e80 | 395 | { |
9d9b7755 VZ |
396 | if ( m_widthCol != 0 ) |
397 | return; | |
2ef31e80 | 398 | |
9d9b7755 | 399 | wxClientDC dc(this); |
2ef31e80 | 400 | |
9d9b7755 | 401 | dc.SetFont(m_font); |
2ef31e80 VZ |
402 | |
403 | // determine the column width (we assume that the weekday names are always | |
404 | // wider (in any language) than the numbers) | |
405 | m_widthCol = 0; | |
9d9b7755 | 406 | wxDateTime::WeekDay wd; |
2ef31e80 VZ |
407 | for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) ) |
408 | { | |
2ef31e80 | 409 | wxCoord width; |
9d9b7755 | 410 | dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow); |
2ef31e80 VZ |
411 | if ( width > m_widthCol ) |
412 | { | |
413 | m_widthCol = width; | |
414 | } | |
415 | } | |
416 | ||
417 | // leave some margins | |
418 | m_widthCol += 2; | |
419 | m_heightRow += 2; | |
9d9b7755 VZ |
420 | } |
421 | ||
422 | // ---------------------------------------------------------------------------- | |
423 | // drawing | |
424 | // ---------------------------------------------------------------------------- | |
425 | ||
13111b2a | 426 | void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) |
9d9b7755 VZ |
427 | { |
428 | wxPaintDC dc(this); | |
429 | ||
9d9b7755 VZ |
430 | dc.SetFont(m_font); |
431 | ||
432 | RecalcGeometry(); | |
433 | ||
882a8f40 | 434 | #if DEBUG_PAINT |
9d9b7755 VZ |
435 | printf("--- starting to paint, selection: %s, week %u\n", |
436 | m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(), | |
437 | GetWeek(m_date)); | |
882a8f40 | 438 | #endif |
2ef31e80 VZ |
439 | |
440 | // first draw the week days | |
9d9b7755 | 441 | if ( IsExposed(0, 0, 7*m_widthCol, m_heightRow) ) |
2ef31e80 | 442 | { |
882a8f40 | 443 | #if DEBUG_PAINT |
9d9b7755 | 444 | puts("painting the header"); |
882a8f40 | 445 | #endif |
9d9b7755 VZ |
446 | |
447 | dc.SetTextForeground(*wxBLUE); | |
448 | dc.SetBrush(wxBrush(*wxLIGHT_GREY, wxSOLID)); | |
449 | dc.SetBackgroundMode(wxTRANSPARENT); | |
450 | dc.SetPen(*wxLIGHT_GREY_PEN); | |
451 | dc.DrawRectangle(0, 0, 7*m_widthCol, m_heightRow); | |
1a8557b1 VZ |
452 | |
453 | bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0; | |
454 | for ( size_t wd = 0; wd < 7; wd++ ) | |
9d9b7755 | 455 | { |
1a8557b1 VZ |
456 | size_t n; |
457 | if ( startOnMonday ) | |
458 | n = wd == 6 ? 0 : wd + 1; | |
459 | else | |
460 | n = wd; | |
461 | ||
462 | dc.DrawText(m_weekdays[n], wd*m_widthCol + 1, 0); | |
9d9b7755 | 463 | } |
2ef31e80 VZ |
464 | } |
465 | ||
466 | // then the calendar itself | |
467 | dc.SetTextForeground(*wxBLACK); | |
468 | //dc.SetFont(*wxNORMAL_FONT); | |
469 | ||
470 | wxCoord y = m_heightRow; | |
471 | ||
472 | wxDateTime date = GetStartDate(); | |
882a8f40 | 473 | #if DEBUG_PAINT |
9d9b7755 VZ |
474 | printf("starting calendar from %s\n", |
475 | date.Format("%a %d-%m-%Y %H:%M:%S").c_str()); | |
882a8f40 | 476 | #endif |
9d9b7755 | 477 | |
2ef31e80 | 478 | dc.SetBackgroundMode(wxSOLID); |
9d9b7755 | 479 | for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow ) |
2ef31e80 | 480 | { |
9d9b7755 | 481 | // if the update region doesn't intersect this row, don't paint it |
15807266 | 482 | if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) ) |
9d9b7755 VZ |
483 | { |
484 | date += wxDateSpan::Week(); | |
485 | ||
486 | continue; | |
487 | } | |
882a8f40 | 488 | |
1a8557b1 | 489 | #if DEBUG_PAINT |
9d9b7755 | 490 | printf("painting week %d at y = %d\n", nWeek, y); |
882a8f40 | 491 | #endif |
9d9b7755 | 492 | |
1a8557b1 | 493 | for ( size_t wd = 0; wd < 7; wd++ ) |
2ef31e80 VZ |
494 | { |
495 | if ( IsDateShown(date) ) | |
496 | { | |
882a8f40 | 497 | // don't use wxDate::Format() which prepends 0s |
2ef31e80 VZ |
498 | wxString day = wxString::Format(_T("%u"), date.GetDay()); |
499 | wxCoord width; | |
500 | dc.GetTextExtent(day, &width, (wxCoord *)NULL); | |
501 | ||
502 | bool isSel = m_date == date; | |
503 | if ( isSel ) | |
504 | { | |
505 | dc.SetTextForeground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT)); | |
506 | dc.SetTextBackground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT)); | |
507 | } | |
508 | ||
509 | dc.DrawText(day, wd*m_widthCol + (m_widthCol - width) / 2, y); | |
510 | ||
511 | if ( isSel ) | |
512 | { | |
9d9b7755 | 513 | dc.SetTextForeground(m_foregroundColour); |
2ef31e80 VZ |
514 | dc.SetTextBackground(m_backgroundColour); |
515 | } | |
516 | } | |
517 | //else: just don't draw it | |
518 | ||
519 | date += wxDateSpan::Day(); | |
520 | } | |
2ef31e80 | 521 | } |
882a8f40 | 522 | #if DEBUG_PAINT |
9d9b7755 | 523 | puts("+++ finished painting"); |
882a8f40 | 524 | #endif |
9d9b7755 VZ |
525 | } |
526 | ||
527 | void wxCalendarCtrl::RefreshDate(const wxDateTime& date) | |
528 | { | |
529 | RecalcGeometry(); | |
530 | ||
531 | wxRect rect; | |
532 | ||
533 | // always refresh the whole row at once because our OnPaint() will draw | |
534 | // the whole row anyhow - and this allows the small optimisation in | |
535 | // OnClick() below to work | |
536 | rect.x = 0; | |
537 | rect.y = m_heightRow * GetWeek(date); | |
538 | rect.width = 7*m_widthCol; | |
539 | rect.height = m_heightRow; | |
540 | ||
882a8f40 | 541 | #if DEBUG_PAINT |
9d9b7755 VZ |
542 | printf("*** refreshing week %d at (%d, %d)-(%d, %d)\n", |
543 | GetWeek(date), | |
544 | rect.x, rect.y, | |
545 | rect.x + rect.width, rect.y + rect.height); | |
882a8f40 | 546 | #endif |
9d9b7755 VZ |
547 | |
548 | Refresh(TRUE, &rect); | |
2ef31e80 VZ |
549 | } |
550 | ||
551 | // ---------------------------------------------------------------------------- | |
552 | // mouse handling | |
553 | // ---------------------------------------------------------------------------- | |
554 | ||
0185cd09 | 555 | void wxCalendarCtrl::OnDClick(wxMouseEvent& event) |
2ef31e80 | 556 | { |
0185cd09 | 557 | if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY ) |
2ef31e80 VZ |
558 | { |
559 | event.Skip(); | |
560 | } | |
561 | else | |
562 | { | |
0185cd09 VZ |
563 | GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED, FALSE); |
564 | } | |
565 | } | |
566 | ||
567 | void wxCalendarCtrl::OnClick(wxMouseEvent& event) | |
568 | { | |
569 | wxDateTime date; | |
570 | wxDateTime::WeekDay wday; | |
571 | switch ( HitTest(event.GetPosition(), &date, &wday) ) | |
572 | { | |
573 | case wxCAL_HITTEST_DAY: | |
574 | ChangeDay(date); | |
2ef31e80 | 575 | |
0185cd09 VZ |
576 | GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED); |
577 | break; | |
578 | ||
579 | case wxCAL_HITTEST_HEADER: | |
580 | { | |
581 | wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED); | |
582 | event.m_wday = wday; | |
583 | (void)GetEventHandler()->ProcessEvent(event); | |
584 | } | |
585 | break; | |
586 | ||
587 | default: | |
588 | wxFAIL_MSG(_T("unknown hittest code")); | |
589 | // fall through | |
590 | ||
591 | case wxCAL_HITTEST_NOWHERE: | |
592 | event.Skip(); | |
593 | break; | |
2ef31e80 VZ |
594 | } |
595 | } | |
596 | ||
0185cd09 VZ |
597 | wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos, |
598 | wxDateTime *date, | |
599 | wxDateTime::WeekDay *wd) | |
2ef31e80 | 600 | { |
9d9b7755 VZ |
601 | RecalcGeometry(); |
602 | ||
0185cd09 VZ |
603 | int wday = pos.x / m_widthCol; |
604 | ||
2ef31e80 VZ |
605 | wxCoord y = pos.y; |
606 | if ( y < m_heightRow ) | |
0185cd09 VZ |
607 | { |
608 | if ( wd ) | |
609 | { | |
610 | if ( GetWindowStyle() & wxCAL_MONDAY_FIRST ) | |
611 | { | |
612 | wday = wday == 6 ? 0 : wday + 1; | |
613 | } | |
614 | ||
615 | *wd = (wxDateTime::WeekDay)wday; | |
616 | } | |
2ef31e80 | 617 | |
0185cd09 VZ |
618 | return wxCAL_HITTEST_HEADER; |
619 | } | |
2ef31e80 | 620 | |
0185cd09 | 621 | int week = (y - m_heightRow) / m_heightRow; |
2ef31e80 | 622 | if ( week >= 6 || wday >= 7 ) |
0185cd09 VZ |
623 | { |
624 | return wxCAL_HITTEST_NOWHERE; | |
625 | } | |
2ef31e80 | 626 | |
0185cd09 | 627 | wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday); |
2ef31e80 | 628 | |
0185cd09 VZ |
629 | if ( IsDateShown(dt) ) |
630 | { | |
631 | if ( date ) | |
632 | *date = dt; | |
9d9b7755 | 633 | |
0185cd09 VZ |
634 | return wxCAL_HITTEST_DAY; |
635 | } | |
636 | else | |
637 | { | |
638 | return wxCAL_HITTEST_NOWHERE; | |
639 | } | |
2ef31e80 | 640 | } |
9d9b7755 VZ |
641 | |
642 | // ---------------------------------------------------------------------------- | |
643 | // subcontrols events handling | |
644 | // ---------------------------------------------------------------------------- | |
645 | ||
646 | void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event) | |
647 | { | |
648 | wxDateTime::Tm tm = m_date.GetTm(); | |
649 | ||
650 | wxDateTime::Month mon = (wxDateTime::Month)event.GetInt(); | |
651 | if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) ) | |
652 | { | |
653 | tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year); | |
654 | } | |
655 | ||
656 | SetDate(wxDateTime(tm.mday, mon, tm.year)); | |
657 | ||
658 | GenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED); | |
659 | } | |
660 | ||
661 | void wxCalendarCtrl::OnYearChange(wxSpinEvent& event) | |
662 | { | |
663 | wxDateTime::Tm tm = m_date.GetTm(); | |
664 | ||
13111b2a | 665 | int year = (int)event.GetInt(); |
9d9b7755 VZ |
666 | if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) ) |
667 | { | |
668 | tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year); | |
669 | } | |
670 | ||
671 | SetDate(wxDateTime(tm.mday, tm.mon, year)); | |
672 | ||
673 | GenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED); | |
674 | } | |
675 | ||
676 | // ---------------------------------------------------------------------------- | |
677 | // keyboard interface | |
678 | // ---------------------------------------------------------------------------- | |
679 | ||
680 | void wxCalendarCtrl::OnChar(wxKeyEvent& event) | |
681 | { | |
682 | switch ( event.KeyCode() ) | |
683 | { | |
684 | case _T('+'): | |
685 | case WXK_ADD: | |
686 | SetDateAndNotify(m_date + wxDateSpan::Year()); | |
687 | break; | |
688 | ||
689 | case _T('-'): | |
690 | case WXK_SUBTRACT: | |
691 | SetDateAndNotify(m_date - wxDateSpan::Year()); | |
692 | break; | |
693 | ||
882a8f40 VZ |
694 | case WXK_PRIOR: |
695 | SetDateAndNotify(m_date - wxDateSpan::Month()); | |
9d9b7755 VZ |
696 | break; |
697 | ||
882a8f40 VZ |
698 | case WXK_NEXT: |
699 | SetDateAndNotify(m_date + wxDateSpan::Month()); | |
9d9b7755 VZ |
700 | break; |
701 | ||
702 | case WXK_RIGHT: | |
1a8557b1 VZ |
703 | if ( event.ControlDown() ) |
704 | SetDateAndNotify(wxDateTime(m_date).SetToNextWeekDay( | |
705 | GetWindowStyle() & wxCAL_MONDAY_FIRST | |
706 | ? wxDateTime::Sun : wxDateTime::Sat)); | |
707 | else | |
708 | SetDateAndNotify(m_date + wxDateSpan::Day()); | |
9d9b7755 VZ |
709 | break; |
710 | ||
711 | case WXK_LEFT: | |
1a8557b1 VZ |
712 | if ( event.ControlDown() ) |
713 | SetDateAndNotify(wxDateTime(m_date).SetToPrevWeekDay( | |
714 | GetWindowStyle() & wxCAL_MONDAY_FIRST | |
715 | ? wxDateTime::Mon : wxDateTime::Sun)); | |
716 | else | |
717 | SetDateAndNotify(m_date - wxDateSpan::Day()); | |
9d9b7755 VZ |
718 | break; |
719 | ||
720 | case WXK_UP: | |
721 | SetDateAndNotify(m_date - wxDateSpan::Week()); | |
722 | break; | |
723 | ||
724 | case WXK_DOWN: | |
725 | SetDateAndNotify(m_date + wxDateSpan::Week()); | |
726 | break; | |
727 | ||
728 | case WXK_HOME: | |
1a8557b1 VZ |
729 | if ( event.ControlDown() ) |
730 | SetDateAndNotify(wxDateTime::Today()); | |
731 | else | |
732 | SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear())); | |
733 | break; | |
734 | ||
735 | case WXK_END: | |
736 | SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay()); | |
9d9b7755 VZ |
737 | break; |
738 | ||
739 | default: | |
740 | event.Skip(); | |
741 | } | |
742 | } | |
743 | ||
744 | // ---------------------------------------------------------------------------- | |
745 | // wxCalendarEvent | |
746 | // ---------------------------------------------------------------------------- | |
747 | ||
0185cd09 | 748 | void wxCalendarCtrl::GenerateEvent(wxEventType type, bool selChanged) |
9d9b7755 VZ |
749 | { |
750 | // we're called for a change in some particular date field but we always | |
751 | // also generate a generic "changed" event | |
752 | wxCalendarEvent event(this, type); | |
9d9b7755 | 753 | (void)GetEventHandler()->ProcessEvent(event); |
0185cd09 VZ |
754 | |
755 | if ( selChanged ) | |
756 | { | |
757 | wxCalendarEvent event2(this, wxEVT_CALENDAR_SEL_CHANGED); | |
758 | ||
759 | (void)GetEventHandler()->ProcessEvent(event2); | |
760 | } | |
761 | } | |
762 | ||
763 | void wxCalendarEvent::Init() | |
764 | { | |
765 | m_wday = wxDateTime::Inv_WeekDay; | |
9d9b7755 VZ |
766 | } |
767 | ||
768 | wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type) | |
769 | : wxCommandEvent(type, cal->GetId()) | |
770 | { | |
771 | m_date = cal->GetDate(); | |
772 | } |