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