]> git.saurik.com Git - wxWidgets.git/blob - src/generic/calctrl.cpp
Cast both operands of ?: to (wxControl*) so BC++ compiles it
[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
85 EVT_CALENDAR_MONTH(-1, wxCalendarCtrl::OnCalMonthChange)
86 EVT_CALENDAR_YEAR(-1, wxCalendarCtrl::OnCalMonthChange)
87 END_EVENT_TABLE()
88
89 BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
90 EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange)
91 END_EVENT_TABLE()
92
93 BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
94 EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange)
95 END_EVENT_TABLE()
96
97 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
98
99 // ============================================================================
100 // implementation
101 // ============================================================================
102
103 // ----------------------------------------------------------------------------
104 // wxMonthComboBox and wxYearSpinCtrl
105 // ----------------------------------------------------------------------------
106
107 wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
108 : wxComboBox(cal->GetParent(), -1,
109 wxEmptyString,
110 wxDefaultPosition,
111 wxDefaultSize,
112 0, NULL,
113 wxCB_READONLY)
114 {
115 m_cal = cal;
116
117 wxDateTime::Month m;
118 for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
119 {
120 Append(wxDateTime::GetMonthName(m));
121 }
122
123 SetSelection(m_cal->GetDate().GetMonth());
124 }
125
126 wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
127 : wxSpinCtrl(cal->GetParent(), -1,
128 cal->GetDate().Format(_T("%Y")),
129 wxDefaultPosition,
130 wxDefaultSize,
131 wxSP_ARROW_KEYS,
132 -4300, 10000, cal->GetDate().GetYear())
133 {
134 m_cal = cal;
135 }
136
137 // ----------------------------------------------------------------------------
138 // wxCalendarCtrl
139 // ----------------------------------------------------------------------------
140
141 void wxCalendarCtrl::Init()
142 {
143 m_comboMonth = NULL;
144 m_spinYear = NULL;
145
146 m_widthCol =
147 m_heightRow = 0;
148
149 wxDateTime::WeekDay wd;
150 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
151 {
152 m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
153 }
154
155 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
156 {
157 m_attrs[n] = NULL;
158 }
159
160 wxSystemSettings ss;
161 m_colHighlightFg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
162 m_colHighlightBg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
163
164 m_colHolidayFg = *wxRED;
165 // don't set m_colHolidayBg - by default, same as our bg colour
166
167 m_colHeaderFg = *wxBLUE;
168 m_colHeaderBg = *wxLIGHT_GREY;
169 }
170
171 bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent),
172 wxWindowID WXUNUSED(id),
173 const wxDateTime& date,
174 const wxPoint& WXUNUSED(pos),
175 const wxSize& size,
176 long style,
177 const wxString& WXUNUSED(name))
178 {
179 // needed to get the arrow keys normally used for the dialog navigation
180 SetWindowStyle(style | wxWANTS_CHARS);
181
182 m_date = date.IsValid() ? date : wxDateTime::Today();
183
184 m_spinYear = new wxYearSpinCtrl(this);
185 m_staticYear = new wxStaticText(GetParent(), -1, m_date.Format(_T("%Y")),
186 wxDefaultPosition, wxDefaultSize,
187 wxALIGN_CENTRE);
188
189 m_comboMonth = new wxMonthComboBox(this);
190 m_staticMonth = new wxStaticText(GetParent(), -1, m_date.Format(_T("%B")),
191 wxDefaultPosition, wxDefaultSize,
192 wxALIGN_CENTRE);
193
194 ShowCurrentControls();
195
196 wxSize sizeReal;
197 if ( size.x == -1 || size.y == -1 )
198 {
199 sizeReal = DoGetBestSize();
200 if ( size.x != -1 )
201 sizeReal.x = size.x;
202 if ( size.y != -1 )
203 sizeReal.y = size.y;
204 }
205 else
206 {
207 sizeReal = size;
208 }
209
210 SetSize(sizeReal);
211
212 SetBackgroundColour(*wxWHITE);
213 SetFont(*wxSWISS_FONT);
214
215 SetHolidayAttrs();
216
217 return TRUE;
218 }
219
220 wxCalendarCtrl::~wxCalendarCtrl()
221 {
222 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
223 {
224 delete m_attrs[n];
225 }
226 }
227
228 // ----------------------------------------------------------------------------
229 // forward wxWin functions to subcontrols
230 // ----------------------------------------------------------------------------
231
232 bool wxCalendarCtrl::Show(bool show)
233 {
234 if ( !wxControl::Show(show) )
235 {
236 return FALSE;
237 }
238
239 GetMonthControl()->Show(show);
240 GetYearControl()->Show(show);
241
242 return TRUE;
243 }
244
245 bool wxCalendarCtrl::Enable(bool enable)
246 {
247 if ( !wxControl::Enable(enable) )
248 {
249 return FALSE;
250 }
251
252 GetMonthControl()->Enable(enable);
253 GetYearControl()->Enable(enable);
254
255 return TRUE;
256 }
257
258 // ----------------------------------------------------------------------------
259 // enable/disable month/year controls
260 // ----------------------------------------------------------------------------
261
262 void wxCalendarCtrl::ShowCurrentControls()
263 {
264 if ( AllowMonthChange() )
265 {
266 m_comboMonth->Show();
267 m_staticMonth->Hide();
268
269 if ( AllowYearChange() )
270 {
271 m_spinYear->Show();
272 m_staticYear->Hide();
273
274 // skip the rest
275 return;
276 }
277 }
278 else
279 {
280 m_comboMonth->Hide();
281 m_staticMonth->Show();
282 }
283
284 // year change not allowed here
285 m_spinYear->Hide();
286 m_staticYear->Show();
287 }
288
289 wxControl *wxCalendarCtrl::GetMonthControl() const
290 {
291 return AllowMonthChange() ? (wxControl *)m_comboMonth : (wxControl *)m_staticMonth;
292 }
293
294 wxControl *wxCalendarCtrl::GetYearControl() const
295 {
296 return AllowYearChange() ? (wxControl *)m_spinYear : (wxControl *)m_staticYear;
297 }
298
299 void wxCalendarCtrl::EnableYearChange(bool enable)
300 {
301 if ( enable != AllowYearChange() )
302 {
303 long style = GetWindowStyle();
304 if ( enable )
305 style &= ~wxCAL_NO_YEAR_CHANGE;
306 else
307 style |= wxCAL_NO_YEAR_CHANGE;
308 SetWindowStyle(style);
309
310 ShowCurrentControls();
311 }
312 }
313
314 void wxCalendarCtrl::EnableMonthChange(bool enable)
315 {
316 if ( enable != AllowMonthChange() )
317 {
318 long style = GetWindowStyle();
319 if ( enable )
320 style &= ~wxCAL_NO_MONTH_CHANGE;
321 else
322 style |= wxCAL_NO_MONTH_CHANGE;
323 SetWindowStyle(style);
324
325 ShowCurrentControls();
326 }
327 }
328
329 // ----------------------------------------------------------------------------
330 // changing date
331 // ----------------------------------------------------------------------------
332
333 void wxCalendarCtrl::SetDate(const wxDateTime& date)
334 {
335 bool sameMonth = m_date.GetMonth() == date.GetMonth(),
336 sameYear = m_date.GetYear() == date.GetYear();
337
338 if ( sameMonth && sameYear )
339 {
340 // just change the day
341 ChangeDay(date);
342 }
343 else
344 {
345 if ( !AllowMonthChange() || (!AllowYearChange() && !sameYear) )
346 {
347 // forbidden
348 return;
349 }
350
351 // change everything
352 m_date = date;
353
354 // update the controls
355 m_comboMonth->SetSelection(m_date.GetMonth());
356
357 if ( AllowYearChange() )
358 {
359 m_spinYear->SetValue(m_date.Format(_T("%Y")));
360 }
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 SetDate(wxDateTime(tm.mday, mon, tm.year));
865
866 GenerateEvents(wxEVT_CALENDAR_MONTH_CHANGED, wxEVT_CALENDAR_SEL_CHANGED);
867 }
868
869 void wxCalendarCtrl::OnYearChange(wxSpinEvent& event)
870 {
871 wxDateTime::Tm tm = m_date.GetTm();
872
873 int year = (int)event.GetInt();
874 if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
875 {
876 tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
877 }
878
879 SetDate(wxDateTime(tm.mday, tm.mon, year));
880
881 GenerateEvents(wxEVT_CALENDAR_YEAR_CHANGED, wxEVT_CALENDAR_SEL_CHANGED);
882 }
883
884 // ----------------------------------------------------------------------------
885 // keyboard interface
886 // ----------------------------------------------------------------------------
887
888 void wxCalendarCtrl::OnChar(wxKeyEvent& event)
889 {
890 switch ( event.KeyCode() )
891 {
892 case _T('+'):
893 case WXK_ADD:
894 SetDateAndNotify(m_date + wxDateSpan::Year());
895 break;
896
897 case _T('-'):
898 case WXK_SUBTRACT:
899 SetDateAndNotify(m_date - wxDateSpan::Year());
900 break;
901
902 case WXK_PRIOR:
903 SetDateAndNotify(m_date - wxDateSpan::Month());
904 break;
905
906 case WXK_NEXT:
907 SetDateAndNotify(m_date + wxDateSpan::Month());
908 break;
909
910 case WXK_RIGHT:
911 if ( event.ControlDown() )
912 SetDateAndNotify(wxDateTime(m_date).SetToNextWeekDay(
913 GetWindowStyle() & wxCAL_MONDAY_FIRST
914 ? wxDateTime::Sun : wxDateTime::Sat));
915 else
916 SetDateAndNotify(m_date + wxDateSpan::Day());
917 break;
918
919 case WXK_LEFT:
920 if ( event.ControlDown() )
921 SetDateAndNotify(wxDateTime(m_date).SetToPrevWeekDay(
922 GetWindowStyle() & wxCAL_MONDAY_FIRST
923 ? wxDateTime::Mon : wxDateTime::Sun));
924 else
925 SetDateAndNotify(m_date - wxDateSpan::Day());
926 break;
927
928 case WXK_UP:
929 SetDateAndNotify(m_date - wxDateSpan::Week());
930 break;
931
932 case WXK_DOWN:
933 SetDateAndNotify(m_date + wxDateSpan::Week());
934 break;
935
936 case WXK_HOME:
937 if ( event.ControlDown() )
938 SetDateAndNotify(wxDateTime::Today());
939 else
940 SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
941 break;
942
943 case WXK_END:
944 SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
945 break;
946
947 case WXK_RETURN:
948 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
949 break;
950
951 default:
952 event.Skip();
953 }
954 }
955
956 // ----------------------------------------------------------------------------
957 // holidays handling
958 // ----------------------------------------------------------------------------
959
960 void wxCalendarCtrl::OnCalMonthChange(wxCalendarEvent& event)
961 {
962 SetHolidayAttrs();
963
964 event.Skip();
965 }
966
967 void wxCalendarCtrl::EnableHolidayDisplay(bool display)
968 {
969 long style = GetWindowStyle();
970 if ( display )
971 style |= wxCAL_SHOW_HOLIDAYS;
972 else
973 style &= ~wxCAL_SHOW_HOLIDAYS;
974
975 SetWindowStyle(style);
976
977 if ( display )
978 SetHolidayAttrs();
979 else
980 ResetHolidayAttrs();
981
982 Refresh();
983 }
984
985 void wxCalendarCtrl::SetHolidayAttrs()
986 {
987 if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
988 {
989 ResetHolidayAttrs();
990
991 wxDateTime::Tm tm = m_date.GetTm();
992 wxDateTime dtStart(1, tm.mon, tm.year),
993 dtEnd = dtStart.GetLastMonthDay();
994
995 wxDateTimeArray hol;
996 wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
997
998 size_t count = hol.GetCount();
999 for ( size_t n = 0; n < count; n++ )
1000 {
1001 SetHoliday(hol[n].GetDay());
1002 }
1003 }
1004 }
1005
1006 void wxCalendarCtrl::SetHoliday(size_t day)
1007 {
1008 wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
1009
1010 wxCalendarDateAttr *attr = GetAttr(day);
1011 if ( !attr )
1012 {
1013 attr = new wxCalendarDateAttr;
1014 }
1015
1016 attr->SetHoliday(TRUE);
1017
1018 // can't use SetAttr() because it would delete this pointer
1019 m_attrs[day - 1] = attr;
1020 }
1021
1022 void wxCalendarCtrl::ResetHolidayAttrs()
1023 {
1024 for ( size_t day = 0; day < 31; day++ )
1025 {
1026 if ( m_attrs[day] )
1027 {
1028 m_attrs[day]->SetHoliday(FALSE);
1029 }
1030 }
1031 }
1032
1033 // ----------------------------------------------------------------------------
1034 // wxCalendarEvent
1035 // ----------------------------------------------------------------------------
1036
1037 void wxCalendarEvent::Init()
1038 {
1039 m_wday = wxDateTime::Inv_WeekDay;
1040 }
1041
1042 wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
1043 : wxCommandEvent(type, cal->GetId())
1044 {
1045 m_date = cal->GetDate();
1046 }