]> git.saurik.com Git - wxWidgets.git/blob - src/generic/calctrl.cpp
1. wxDateTimeHolidayAuthority class for calculating the holidays added
[wxWidgets.git] / src / generic / calctrl.cpp
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
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/brush.h"
35 #include "wx/combobox.h"
36 #endif //WX_PRECOMP
37
38 #include "wx/calctrl.h"
39
40 #define DEBUG_PAINT 0
41
42 // ----------------------------------------------------------------------------
43 // private classes
44 // ----------------------------------------------------------------------------
45
46 class wxMonthComboBox : public wxComboBox
47 {
48 public:
49 wxMonthComboBox(wxCalendarCtrl *cal);
50
51 void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); }
52
53 private:
54 wxCalendarCtrl *m_cal;
55
56 DECLARE_EVENT_TABLE()
57 };
58
59 class wxYearSpinCtrl : public wxSpinCtrl
60 {
61 public:
62 wxYearSpinCtrl(wxCalendarCtrl *cal);
63
64 void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); }
65
66 private:
67 wxCalendarCtrl *m_cal;
68
69 DECLARE_EVENT_TABLE()
70 };
71
72 // ----------------------------------------------------------------------------
73 // wxWin macros
74 // ----------------------------------------------------------------------------
75
76 BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl)
77 EVT_PAINT(wxCalendarCtrl::OnPaint)
78
79 EVT_CHAR(wxCalendarCtrl::OnChar)
80
81 EVT_LEFT_DOWN(wxCalendarCtrl::OnClick)
82 EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick)
83
84 EVT_CALENDAR_MONTH(-1, wxCalendarCtrl::OnCalMonthChange)
85 EVT_CALENDAR_YEAR(-1, wxCalendarCtrl::OnCalMonthChange)
86 END_EVENT_TABLE()
87
88 BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
89 EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange)
90 END_EVENT_TABLE()
91
92 BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
93 EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange)
94 END_EVENT_TABLE()
95
96 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
97
98 // ============================================================================
99 // implementation
100 // ============================================================================
101
102 // ----------------------------------------------------------------------------
103 // wxMonthComboBox and wxYearSpinCtrl
104 // ----------------------------------------------------------------------------
105
106 wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
107 : wxComboBox(cal->GetParent(), -1,
108 wxEmptyString,
109 wxDefaultPosition,
110 wxDefaultSize,
111 0, NULL,
112 wxCB_READONLY)
113 {
114 m_cal = cal;
115
116 wxDateTime::Month m;
117 for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
118 {
119 Append(wxDateTime::GetMonthName(m));
120 }
121
122 SetSelection(m_cal->GetDate().GetMonth());
123 }
124
125 wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
126 : wxSpinCtrl(cal->GetParent(), -1,
127 cal->GetDate().Format(_T("%Y")),
128 wxDefaultPosition,
129 wxDefaultSize,
130 wxSP_ARROW_KEYS,
131 -4300, 10000, cal->GetDate().GetYear())
132 {
133 m_cal = cal;
134 }
135
136 // ----------------------------------------------------------------------------
137 // wxCalendarCtrl
138 // ----------------------------------------------------------------------------
139
140 void wxCalendarCtrl::Init()
141 {
142 m_comboMonth = NULL;
143 m_spinYear = NULL;
144
145 m_widthCol =
146 m_heightRow = 0;
147
148 wxDateTime::WeekDay wd;
149 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
150 {
151 m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
152 }
153
154 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
155 {
156 m_attrs[n] = NULL;
157 }
158
159 wxSystemSettings ss;
160 m_colHighlightFg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
161 m_colHighlightBg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
162
163 m_colHolidayFg = *wxRED;
164 // don't set m_colHolidayBg - by default, same as our bg colour
165
166 m_colHeaderFg = *wxBLUE;
167 m_colHeaderBg = *wxLIGHT_GREY;
168 }
169
170 bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent),
171 wxWindowID WXUNUSED(id),
172 const wxDateTime& date,
173 const wxPoint& WXUNUSED(pos),
174 const wxSize& size,
175 long style,
176 const wxString& WXUNUSED(name))
177 {
178 SetWindowStyle(style | (wxRAISED_BORDER | wxWANTS_CHARS));
179
180 m_date = date.IsValid() ? date : wxDateTime::Today();
181
182 m_comboMonth = new wxMonthComboBox(this);
183 m_spinYear = new wxYearSpinCtrl(this);
184
185 wxSize sizeReal;
186 if ( size.x == -1 || size.y == -1 )
187 {
188 sizeReal = DoGetBestSize();
189 if ( size.x != -1 )
190 sizeReal.x = size.x;
191 if ( size.y != -1 )
192 sizeReal.y = size.y;
193 }
194 else
195 {
196 sizeReal = size;
197 }
198
199 SetSize(sizeReal);
200
201 SetBackgroundColour(*wxWHITE);
202 SetFont(*wxSWISS_FONT);
203
204 SetHolidayAttrs();
205
206 return TRUE;
207 }
208
209 wxCalendarCtrl::~wxCalendarCtrl()
210 {
211 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
212 {
213 delete m_attrs[n];
214 }
215 }
216
217 // ----------------------------------------------------------------------------
218 // forward wxWin functions to subcontrols
219 // ----------------------------------------------------------------------------
220
221 bool wxCalendarCtrl::Show(bool show)
222 {
223 if ( !wxControl::Show(show) )
224 {
225 return FALSE;
226 }
227
228 m_comboMonth->Show(show);
229 m_spinYear->Show(show);
230
231 return TRUE;
232 }
233
234 bool wxCalendarCtrl::Enable(bool enable)
235 {
236 if ( !wxControl::Enable(enable) )
237 {
238 return FALSE;
239 }
240
241 m_comboMonth->Enable(enable);
242 m_spinYear->Enable(enable);
243
244 return TRUE;
245 }
246
247 // ----------------------------------------------------------------------------
248 // changing date
249 // ----------------------------------------------------------------------------
250
251 void wxCalendarCtrl::SetDate(const wxDateTime& date)
252 {
253 if ( m_date.GetMonth() == date.GetMonth() &&
254 m_date.GetYear() == date.GetYear() )
255 {
256 // just change the day
257 ChangeDay(date);
258 }
259 else
260 {
261 // change everything
262 m_date = date;
263
264 // update the controls
265 m_comboMonth->SetSelection(m_date.GetMonth());
266 m_spinYear->SetValue(m_date.Format(_T("%Y")));
267
268 // update the calendar
269 Refresh();
270 }
271 }
272
273 void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
274 {
275 if ( m_date != date )
276 {
277 // we need to refresh the row containing the old date and the one
278 // containing the new one
279 wxDateTime dateOld = m_date;
280 m_date = date;
281
282 RefreshDate(dateOld);
283
284 // if the date is in the same row, it was already drawn correctly
285 if ( GetWeek(m_date) != GetWeek(dateOld) )
286 {
287 RefreshDate(m_date);
288 }
289 }
290 }
291
292 void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
293 {
294 wxDateTime::Tm tm1 = m_date.GetTm(),
295 tm2 = date.GetTm();
296
297 wxEventType type;
298 if ( tm1.year != tm2.year )
299 type = wxEVT_CALENDAR_YEAR_CHANGED;
300 else if ( tm1.mon != tm2.mon )
301 type = wxEVT_CALENDAR_MONTH_CHANGED;
302 else if ( tm1.mday != tm2.mday )
303 type = wxEVT_CALENDAR_DAY_CHANGED;
304 else
305 return;
306
307 SetDate(date);
308
309 GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
310 }
311
312 // ----------------------------------------------------------------------------
313 // date helpers
314 // ----------------------------------------------------------------------------
315
316 wxDateTime wxCalendarCtrl::GetStartDate() const
317 {
318 wxDateTime::Tm tm = m_date.GetTm();
319
320 wxDateTime date = wxDateTime(1, tm.mon, tm.year);
321
322 // rewind back
323 date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
324 ? wxDateTime::Mon : wxDateTime::Sun);
325
326 return date;
327 }
328
329 bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
330 {
331 return date.GetMonth() == m_date.GetMonth();
332 }
333
334 size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
335 {
336 return date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
337 ? wxDateTime::Monday_First
338 : wxDateTime::Sunday_First);
339 }
340
341 // ----------------------------------------------------------------------------
342 // size management
343 // ----------------------------------------------------------------------------
344
345 // this is a composite control and it must arrange its parts each time its
346 // size or position changes: the combobox and spinctrl are along the top of
347 // the available area and the calendar takes up therest of the space
348
349 // the constants used for the layout
350 #define VERT_MARGIN 5 // distance between combo and calendar
351 #define HORZ_MARGIN 15 // spin
352
353 wxSize wxCalendarCtrl::DoGetBestSize() const
354 {
355 // calc the size of the calendar
356 ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast
357
358 wxCoord width = 7*m_widthCol,
359 height = 7*m_heightRow;
360
361 wxSize sizeCombo = m_comboMonth->GetBestSize(),
362 sizeSpin = m_spinYear->GetBestSize();
363
364 height += VERT_MARGIN + wxMax(sizeCombo.y, sizeSpin.y);
365
366 return wxSize(width, height);
367 }
368
369 void wxCalendarCtrl::DoSetSize(int x, int y,
370 int width, int height,
371 int sizeFlags)
372 {
373 wxControl::DoSetSize(x, y, width, height, sizeFlags);
374 }
375
376 void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
377 {
378 wxSize sizeCombo = m_comboMonth->GetSize();
379 m_comboMonth->Move(x, y);
380
381 int xDiff = sizeCombo.x + HORZ_MARGIN;
382 m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
383
384 wxSize sizeSpin = m_spinYear->GetSize();
385 int yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
386
387 wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
388 }
389
390 void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
391 {
392 wxControl::DoGetPosition(x, y);
393
394 // our real top corner is not in this position
395 if ( y )
396 {
397 *y -= m_comboMonth->GetSize().y + VERT_MARGIN;
398 }
399 }
400
401 void wxCalendarCtrl::DoGetSize(int *width, int *height) const
402 {
403 wxControl::DoGetSize(width, height);
404
405 // our real height is bigger
406 if ( height )
407 {
408 *height += m_comboMonth->GetSize().y + VERT_MARGIN;
409 }
410 }
411
412 void wxCalendarCtrl::RecalcGeometry()
413 {
414 if ( m_widthCol != 0 )
415 return;
416
417 wxClientDC dc(this);
418
419 dc.SetFont(m_font);
420
421 // determine the column width (we assume that the weekday names are always
422 // wider (in any language) than the numbers)
423 m_widthCol = 0;
424 wxDateTime::WeekDay wd;
425 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
426 {
427 wxCoord width;
428 dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
429 if ( width > m_widthCol )
430 {
431 m_widthCol = width;
432 }
433 }
434
435 // leave some margins
436 m_widthCol += 2;
437 m_heightRow += 2;
438 }
439
440 // ----------------------------------------------------------------------------
441 // drawing
442 // ----------------------------------------------------------------------------
443
444 void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
445 {
446 wxPaintDC dc(this);
447
448 dc.SetFont(m_font);
449
450 RecalcGeometry();
451
452 #if DEBUG_PAINT
453 printf("--- starting to paint, selection: %s, week %u\n",
454 m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
455 GetWeek(m_date));
456 #endif
457
458 // first draw the week days
459 if ( IsExposed(0, 0, 7*m_widthCol, m_heightRow) )
460 {
461 #if DEBUG_PAINT
462 puts("painting the header");
463 #endif
464
465 dc.SetBackgroundMode(wxTRANSPARENT);
466 dc.SetTextForeground(m_colHeaderFg);
467 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
468 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
469 dc.DrawRectangle(0, 0, 7*m_widthCol, m_heightRow);
470
471 bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0;
472 for ( size_t wd = 0; wd < 7; wd++ )
473 {
474 size_t n;
475 if ( startOnMonday )
476 n = wd == 6 ? 0 : wd + 1;
477 else
478 n = wd;
479
480 dc.DrawText(m_weekdays[n], wd*m_widthCol + 1, 0);
481 }
482 }
483
484 // then the calendar itself
485 dc.SetTextForeground(*wxBLACK);
486 //dc.SetFont(*wxNORMAL_FONT);
487
488 wxCoord y = m_heightRow;
489
490 wxDateTime date = GetStartDate();
491 #if DEBUG_PAINT
492 printf("starting calendar from %s\n",
493 date.Format("%a %d-%m-%Y %H:%M:%S").c_str());
494 #endif
495
496 dc.SetBackgroundMode(wxSOLID);
497 for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow )
498 {
499 // if the update region doesn't intersect this row, don't paint it
500 if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) )
501 {
502 date += wxDateSpan::Week();
503
504 continue;
505 }
506
507 #if DEBUG_PAINT
508 printf("painting week %d at y = %d\n", nWeek, y);
509 #endif
510
511 for ( size_t wd = 0; wd < 7; wd++ )
512 {
513 if ( IsDateShown(date) )
514 {
515 // don't use wxDate::Format() which prepends 0s
516 unsigned int day = date.GetDay();
517 wxString dayStr = wxString::Format(_T("%u"), day);
518 wxCoord width;
519 dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL);
520
521 bool changedColours = FALSE,
522 changedFont = FALSE;
523
524 wxCalendarDateAttr *attr = m_attrs[day - 1];
525
526 bool isSel = m_date == date;
527 if ( isSel )
528 {
529 dc.SetTextForeground(m_colHighlightFg);
530 dc.SetTextBackground(m_colHighlightBg);
531
532 changedColours = TRUE;
533 }
534 else if ( attr )
535 {
536 wxColour colFg, colBg;
537
538 if ( attr->IsHoliday() )
539 {
540 colFg = m_colHolidayFg;
541 colBg = m_colHolidayBg;
542 }
543 else
544 {
545 colFg = attr->GetTextColour();
546 colBg = attr->GetBackgroundColour();
547 }
548
549 if ( colFg.Ok() )
550 {
551 dc.SetTextForeground(colFg);
552 changedColours = TRUE;
553 }
554
555 if ( colBg.Ok() )
556 {
557 dc.SetTextBackground(colBg);
558 changedColours = TRUE;
559 }
560
561 if ( attr->HasFont() )
562 {
563 dc.SetFont(attr->GetFont());
564 changedFont = TRUE;
565 }
566 }
567
568 wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2;
569 dc.DrawText(dayStr, x, y + 1);
570
571 if ( !isSel && attr && attr->HasBorder() )
572 {
573 wxColour colBorder;
574 if ( attr->HasBorderColour() )
575 {
576 colBorder = attr->GetBorderColour();
577 }
578 else
579 {
580 colBorder = m_foregroundColour;
581 }
582
583 wxPen pen(colBorder, 1, wxSOLID);
584 dc.SetPen(pen);
585 dc.SetBrush(*wxTRANSPARENT_BRUSH);
586
587 switch ( attr->GetBorder() )
588 {
589 case wxCAL_BORDER_SQUARE:
590 dc.DrawRectangle(x - 2, y,
591 width + 4, m_heightRow);
592 break;
593
594 case wxCAL_BORDER_ROUND:
595 dc.DrawEllipse(x - 2, y,
596 width + 4, m_heightRow);
597 break;
598
599 default:
600 wxFAIL_MSG(_T("unknown border type"));
601 }
602 }
603
604 if ( changedColours )
605 {
606 dc.SetTextForeground(m_foregroundColour);
607 dc.SetTextBackground(m_backgroundColour);
608 }
609
610 if ( changedFont )
611 {
612 dc.SetFont(m_font);
613 }
614 }
615 //else: just don't draw it
616
617 date += wxDateSpan::Day();
618 }
619 }
620 #if DEBUG_PAINT
621 puts("+++ finished painting");
622 #endif
623 }
624
625 void wxCalendarCtrl::RefreshDate(const wxDateTime& date)
626 {
627 RecalcGeometry();
628
629 wxRect rect;
630
631 // always refresh the whole row at once because our OnPaint() will draw
632 // the whole row anyhow - and this allows the small optimisation in
633 // OnClick() below to work
634 rect.x = 0;
635 rect.y = m_heightRow * GetWeek(date);
636 rect.width = 7*m_widthCol;
637 rect.height = m_heightRow;
638
639 #if DEBUG_PAINT
640 printf("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
641 GetWeek(date),
642 rect.x, rect.y,
643 rect.x + rect.width, rect.y + rect.height);
644 #endif
645
646 Refresh(TRUE, &rect);
647 }
648
649 // ----------------------------------------------------------------------------
650 // mouse handling
651 // ----------------------------------------------------------------------------
652
653 void wxCalendarCtrl::OnDClick(wxMouseEvent& event)
654 {
655 if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY )
656 {
657 event.Skip();
658 }
659 else
660 {
661 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
662 }
663 }
664
665 void wxCalendarCtrl::OnClick(wxMouseEvent& event)
666 {
667 wxDateTime date;
668 wxDateTime::WeekDay wday;
669 switch ( HitTest(event.GetPosition(), &date, &wday) )
670 {
671 case wxCAL_HITTEST_DAY:
672 ChangeDay(date);
673
674 GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED,
675 wxEVT_CALENDAR_SEL_CHANGED);
676 break;
677
678 case wxCAL_HITTEST_HEADER:
679 {
680 wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED);
681 event.m_wday = wday;
682 (void)GetEventHandler()->ProcessEvent(event);
683 }
684 break;
685
686 default:
687 wxFAIL_MSG(_T("unknown hittest code"));
688 // fall through
689
690 case wxCAL_HITTEST_NOWHERE:
691 event.Skip();
692 break;
693 }
694 }
695
696 wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos,
697 wxDateTime *date,
698 wxDateTime::WeekDay *wd)
699 {
700 RecalcGeometry();
701
702 int wday = pos.x / m_widthCol;
703
704 wxCoord y = pos.y;
705 if ( y < m_heightRow )
706 {
707 if ( wd )
708 {
709 if ( GetWindowStyle() & wxCAL_MONDAY_FIRST )
710 {
711 wday = wday == 6 ? 0 : wday + 1;
712 }
713
714 *wd = (wxDateTime::WeekDay)wday;
715 }
716
717 return wxCAL_HITTEST_HEADER;
718 }
719
720 int week = (y - m_heightRow) / m_heightRow;
721 if ( week >= 6 || wday >= 7 )
722 {
723 return wxCAL_HITTEST_NOWHERE;
724 }
725
726 wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday);
727
728 if ( IsDateShown(dt) )
729 {
730 if ( date )
731 *date = dt;
732
733 return wxCAL_HITTEST_DAY;
734 }
735 else
736 {
737 return wxCAL_HITTEST_NOWHERE;
738 }
739 }
740
741 // ----------------------------------------------------------------------------
742 // subcontrols events handling
743 // ----------------------------------------------------------------------------
744
745 void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event)
746 {
747 wxDateTime::Tm tm = m_date.GetTm();
748
749 wxDateTime::Month mon = (wxDateTime::Month)event.GetInt();
750 if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) )
751 {
752 tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year);
753 }
754
755 SetDate(wxDateTime(tm.mday, mon, tm.year));
756
757 GenerateEvents(wxEVT_CALENDAR_MONTH_CHANGED, wxEVT_CALENDAR_SEL_CHANGED);
758 }
759
760 void wxCalendarCtrl::OnYearChange(wxSpinEvent& event)
761 {
762 wxDateTime::Tm tm = m_date.GetTm();
763
764 int year = (int)event.GetInt();
765 if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
766 {
767 tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
768 }
769
770 SetDate(wxDateTime(tm.mday, tm.mon, year));
771
772 GenerateEvents(wxEVT_CALENDAR_YEAR_CHANGED, wxEVT_CALENDAR_SEL_CHANGED);
773 }
774
775 // ----------------------------------------------------------------------------
776 // keyboard interface
777 // ----------------------------------------------------------------------------
778
779 void wxCalendarCtrl::OnChar(wxKeyEvent& event)
780 {
781 switch ( event.KeyCode() )
782 {
783 case _T('+'):
784 case WXK_ADD:
785 SetDateAndNotify(m_date + wxDateSpan::Year());
786 break;
787
788 case _T('-'):
789 case WXK_SUBTRACT:
790 SetDateAndNotify(m_date - wxDateSpan::Year());
791 break;
792
793 case WXK_PRIOR:
794 SetDateAndNotify(m_date - wxDateSpan::Month());
795 break;
796
797 case WXK_NEXT:
798 SetDateAndNotify(m_date + wxDateSpan::Month());
799 break;
800
801 case WXK_RIGHT:
802 if ( event.ControlDown() )
803 SetDateAndNotify(wxDateTime(m_date).SetToNextWeekDay(
804 GetWindowStyle() & wxCAL_MONDAY_FIRST
805 ? wxDateTime::Sun : wxDateTime::Sat));
806 else
807 SetDateAndNotify(m_date + wxDateSpan::Day());
808 break;
809
810 case WXK_LEFT:
811 if ( event.ControlDown() )
812 SetDateAndNotify(wxDateTime(m_date).SetToPrevWeekDay(
813 GetWindowStyle() & wxCAL_MONDAY_FIRST
814 ? wxDateTime::Mon : wxDateTime::Sun));
815 else
816 SetDateAndNotify(m_date - wxDateSpan::Day());
817 break;
818
819 case WXK_UP:
820 SetDateAndNotify(m_date - wxDateSpan::Week());
821 break;
822
823 case WXK_DOWN:
824 SetDateAndNotify(m_date + wxDateSpan::Week());
825 break;
826
827 case WXK_HOME:
828 if ( event.ControlDown() )
829 SetDateAndNotify(wxDateTime::Today());
830 else
831 SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
832 break;
833
834 case WXK_END:
835 SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
836 break;
837
838 case WXK_RETURN:
839 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
840 break;
841
842 default:
843 event.Skip();
844 }
845 }
846
847 // ----------------------------------------------------------------------------
848 // holidays handling
849 // ----------------------------------------------------------------------------
850
851 void wxCalendarCtrl::OnCalMonthChange(wxCalendarEvent& event)
852 {
853 SetHolidayAttrs();
854
855 event.Skip();
856 }
857
858 void wxCalendarCtrl::EnableHolidayDisplay(bool display)
859 {
860 long style = GetWindowStyle();
861 if ( display )
862 style |= wxCAL_SHOW_HOLIDAYS;
863 else
864 style &= ~wxCAL_SHOW_HOLIDAYS;
865
866 SetWindowStyle(style);
867
868 if ( display )
869 SetHolidayAttrs();
870 else
871 ResetHolidayAttrs();
872
873 Refresh();
874 }
875
876 void wxCalendarCtrl::SetHolidayAttrs()
877 {
878 if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
879 {
880 ResetHolidayAttrs();
881
882 wxDateTime::Tm tm = m_date.GetTm();
883 wxDateTime dtStart(1, tm.mon, tm.year),
884 dtEnd = dtStart.GetLastMonthDay();
885
886 wxDateTimeArray hol;
887 wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
888
889 size_t count = hol.GetCount();
890 for ( size_t n = 0; n < count; n++ )
891 {
892 SetHoliday(hol[n].GetDay());
893 }
894 }
895 }
896
897 void wxCalendarCtrl::SetHoliday(size_t day)
898 {
899 wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
900
901 wxCalendarDateAttr *attr = GetAttr(day);
902 if ( !attr )
903 {
904 attr = new wxCalendarDateAttr;
905 }
906
907 attr->SetHoliday(TRUE);
908
909 // can't use SetAttr() because it would delete this pointer
910 m_attrs[day - 1] = attr;
911 }
912
913 void wxCalendarCtrl::ResetHolidayAttrs()
914 {
915 for ( size_t day = 0; day < 31; day++ )
916 {
917 if ( m_attrs[day] )
918 {
919 m_attrs[day]->SetHoliday(FALSE);
920 }
921 }
922 }
923
924 // ----------------------------------------------------------------------------
925 // wxCalendarEvent
926 // ----------------------------------------------------------------------------
927
928 void wxCalendarEvent::Init()
929 {
930 m_wday = wxDateTime::Inv_WeekDay;
931 }
932
933 wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
934 : wxCommandEvent(type, cal->GetId())
935 {
936 m_date = cal->GetDate();
937 }