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