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