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