]> git.saurik.com Git - wxWidgets.git/blob - src/generic/calctrl.cpp
support for using DIBs for wxBitmap implementation (patch 649866)
[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 #include "wx/textctrl.h"
38 #endif //WX_PRECOMP
39
40 #if wxUSE_CALENDARCTRL
41
42 #include "wx/spinctrl.h"
43
44 #include "wx/calctrl.h"
45
46 #define DEBUG_PAINT 0
47
48 // ----------------------------------------------------------------------------
49 // private classes
50 // ----------------------------------------------------------------------------
51
52 class wxMonthComboBox : public wxComboBox
53 {
54 public:
55 wxMonthComboBox(wxCalendarCtrl *cal);
56
57 void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); }
58
59 private:
60 wxCalendarCtrl *m_cal;
61
62 DECLARE_EVENT_TABLE()
63 };
64
65 class wxYearSpinCtrl : public wxSpinCtrl
66 {
67 public:
68 wxYearSpinCtrl(wxCalendarCtrl *cal);
69
70 void OnYearTextChange(wxCommandEvent& event) { m_cal->OnYearChange(event); }
71 void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); }
72
73 private:
74 wxCalendarCtrl *m_cal;
75
76 DECLARE_EVENT_TABLE()
77 };
78
79 // ----------------------------------------------------------------------------
80 // wxWin macros
81 // ----------------------------------------------------------------------------
82
83 BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl)
84 EVT_PAINT(wxCalendarCtrl::OnPaint)
85
86 EVT_CHAR(wxCalendarCtrl::OnChar)
87
88 EVT_LEFT_DOWN(wxCalendarCtrl::OnClick)
89 EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick)
90 END_EVENT_TABLE()
91
92 BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
93 EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange)
94 END_EVENT_TABLE()
95
96 BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
97 EVT_TEXT(-1, wxYearSpinCtrl::OnYearTextChange)
98 EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange)
99 END_EVENT_TABLE()
100
101 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
102 IMPLEMENT_DYNAMIC_CLASS(wxCalendarEvent, wxCommandEvent)
103
104 // ----------------------------------------------------------------------------
105 // events
106 // ----------------------------------------------------------------------------
107
108 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_SEL_CHANGED)
109 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DAY_CHANGED)
110 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_MONTH_CHANGED)
111 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_YEAR_CHANGED)
112 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DOUBLECLICKED)
113 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_WEEKDAY_CLICKED)
114
115 // ============================================================================
116 // implementation
117 // ============================================================================
118
119 // ----------------------------------------------------------------------------
120 // wxMonthComboBox and wxYearSpinCtrl
121 // ----------------------------------------------------------------------------
122
123 wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
124 : wxComboBox(cal->GetParent(), -1,
125 wxEmptyString,
126 wxDefaultPosition,
127 wxDefaultSize,
128 0, NULL,
129 wxCB_READONLY | wxCLIP_SIBLINGS)
130 {
131 m_cal = cal;
132
133 wxDateTime::Month m;
134 for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
135 {
136 Append(wxDateTime::GetMonthName(m));
137 }
138
139 SetSelection(m_cal->GetDate().GetMonth());
140 SetSize(-1, -1, -1, -1, wxSIZE_AUTO_WIDTH|wxSIZE_AUTO_HEIGHT);
141 }
142
143 wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
144 : wxSpinCtrl(cal->GetParent(), -1,
145 cal->GetDate().Format(_T("%Y")),
146 wxDefaultPosition,
147 wxDefaultSize,
148 wxSP_ARROW_KEYS | wxCLIP_SIBLINGS,
149 -4300, 10000, cal->GetDate().GetYear())
150 {
151 m_cal = cal;
152 }
153
154 // ----------------------------------------------------------------------------
155 // wxCalendarCtrl
156 // ----------------------------------------------------------------------------
157
158 wxCalendarCtrl::wxCalendarCtrl(wxWindow *parent,
159 wxWindowID id,
160 const wxDateTime& date,
161 const wxPoint& pos,
162 const wxSize& size,
163 long style,
164 const wxString& name)
165 {
166 Init();
167
168 (void)Create(parent, id, date, pos, size, style, name);
169 }
170
171 void wxCalendarCtrl::Init()
172 {
173 m_comboMonth = NULL;
174 m_spinYear = NULL;
175
176 m_userChangedYear = FALSE;
177
178 m_widthCol =
179 m_heightRow = 0;
180
181 wxDateTime::WeekDay wd;
182 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
183 {
184 m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
185 }
186
187 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
188 {
189 m_attrs[n] = NULL;
190 }
191
192 m_colHighlightFg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
193 m_colHighlightBg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
194
195 m_colHolidayFg = *wxRED;
196 // don't set m_colHolidayBg - by default, same as our bg colour
197
198 m_colHeaderFg = *wxBLUE;
199 m_colHeaderBg = *wxLIGHT_GREY;
200 }
201
202 bool wxCalendarCtrl::Create(wxWindow *parent,
203 wxWindowID id,
204 const wxDateTime& date,
205 const wxPoint& pos,
206 const wxSize& size,
207 long style,
208 const wxString& name)
209 {
210 if ( !wxControl::Create(parent, id, pos, size,
211 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
212 wxDefaultValidator, name) )
213 {
214 return FALSE;
215 }
216
217 // needed to get the arrow keys normally used for the dialog navigation
218 SetWindowStyle(style | wxWANTS_CHARS);
219
220 m_date = date.IsValid() ? date : wxDateTime::Today();
221
222 m_lowdate = wxDefaultDateTime;
223 m_highdate = wxDefaultDateTime;
224
225 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
226 {
227 m_spinYear = new wxYearSpinCtrl(this);
228 m_staticYear = new wxStaticText(GetParent(), -1, m_date.Format(_T("%Y")),
229 wxDefaultPosition, wxDefaultSize,
230 wxALIGN_CENTRE);
231
232 m_comboMonth = new wxMonthComboBox(this);
233 m_staticMonth = new wxStaticText(GetParent(), -1, m_date.Format(_T("%B")),
234 wxDefaultPosition, wxDefaultSize,
235 wxALIGN_CENTRE);
236 }
237
238 ShowCurrentControls();
239
240 wxSize sizeReal;
241 if ( size.x == -1 || size.y == -1 )
242 {
243 sizeReal = DoGetBestSize();
244 if ( size.x != -1 )
245 sizeReal.x = size.x;
246 if ( size.y != -1 )
247 sizeReal.y = size.y;
248 }
249 else
250 {
251 sizeReal = size;
252 }
253
254 // we need to set the position as well because the main control position
255 // is not the same as the one specified in pos if we have the controls
256 // above it
257 SetSize(pos.x, pos.y, sizeReal.x, sizeReal.y);
258
259 SetBackgroundColour(*wxWHITE);
260 SetFont(*wxSWISS_FONT);
261
262 SetHolidayAttrs();
263
264 return TRUE;
265 }
266
267 wxCalendarCtrl::~wxCalendarCtrl()
268 {
269 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
270 {
271 delete m_attrs[n];
272 }
273 }
274
275 // ----------------------------------------------------------------------------
276 // forward wxWin functions to subcontrols
277 // ----------------------------------------------------------------------------
278
279 bool wxCalendarCtrl::Destroy()
280 {
281 if ( m_staticYear )
282 m_staticYear->Destroy();
283 if ( m_spinYear )
284 m_spinYear->Destroy();
285 if ( m_comboMonth )
286 m_comboMonth->Destroy();
287 if ( m_staticMonth )
288 m_staticMonth->Destroy();
289
290 m_staticYear = NULL;
291 m_spinYear = NULL;
292 m_comboMonth = NULL;
293 m_staticMonth = NULL;
294
295 return wxControl::Destroy();
296 }
297
298 bool wxCalendarCtrl::Show(bool show)
299 {
300 if ( !wxControl::Show(show) )
301 {
302 return FALSE;
303 }
304
305 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
306 {
307 if ( GetMonthControl() )
308 {
309 GetMonthControl()->Show(show);
310 GetYearControl()->Show(show);
311 }
312 }
313
314 return TRUE;
315 }
316
317 bool wxCalendarCtrl::Enable(bool enable)
318 {
319 if ( !wxControl::Enable(enable) )
320 {
321 return FALSE;
322 }
323
324 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
325 {
326 GetMonthControl()->Enable(enable);
327 GetYearControl()->Enable(enable);
328 }
329
330 return TRUE;
331 }
332
333 // ----------------------------------------------------------------------------
334 // enable/disable month/year controls
335 // ----------------------------------------------------------------------------
336
337 void wxCalendarCtrl::ShowCurrentControls()
338 {
339 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
340 {
341 if ( AllowMonthChange() )
342 {
343 m_comboMonth->Show();
344 m_staticMonth->Hide();
345
346 if ( AllowYearChange() )
347 {
348 m_spinYear->Show();
349 m_staticYear->Hide();
350
351 // skip the rest
352 return;
353 }
354 }
355 else
356 {
357 m_comboMonth->Hide();
358 m_staticMonth->Show();
359 }
360
361 // year change not allowed here
362 m_spinYear->Hide();
363 m_staticYear->Show();
364 }
365 }
366
367 wxControl *wxCalendarCtrl::GetMonthControl() const
368 {
369 return AllowMonthChange() ? (wxControl *)m_comboMonth : (wxControl *)m_staticMonth;
370 }
371
372 wxControl *wxCalendarCtrl::GetYearControl() const
373 {
374 return AllowYearChange() ? (wxControl *)m_spinYear : (wxControl *)m_staticYear;
375 }
376
377 void wxCalendarCtrl::EnableYearChange(bool enable)
378 {
379 if ( enable != AllowYearChange() )
380 {
381 long style = GetWindowStyle();
382 if ( enable )
383 style &= ~wxCAL_NO_YEAR_CHANGE;
384 else
385 style |= wxCAL_NO_YEAR_CHANGE;
386 SetWindowStyle(style);
387
388 ShowCurrentControls();
389 if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
390 {
391 Refresh();
392 }
393 }
394 }
395
396 void wxCalendarCtrl::EnableMonthChange(bool enable)
397 {
398 if ( enable != AllowMonthChange() )
399 {
400 long style = GetWindowStyle();
401 if ( enable )
402 style &= ~wxCAL_NO_MONTH_CHANGE;
403 else
404 style |= wxCAL_NO_MONTH_CHANGE;
405 SetWindowStyle(style);
406
407 ShowCurrentControls();
408 if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
409 {
410 Refresh();
411 }
412 }
413 }
414
415 // ----------------------------------------------------------------------------
416 // changing date
417 // ----------------------------------------------------------------------------
418
419 bool wxCalendarCtrl::SetDate(const wxDateTime& date)
420 {
421 bool retval = TRUE;
422
423 bool sameMonth = m_date.GetMonth() == date.GetMonth(),
424 sameYear = m_date.GetYear() == date.GetYear();
425
426 if ( IsDateInRange(date) )
427 {
428 if ( sameMonth && sameYear )
429 {
430 // just change the day
431 ChangeDay(date);
432 }
433 else
434 {
435 if ( AllowMonthChange() && (AllowYearChange() || sameYear) )
436 {
437 // change everything
438 m_date = date;
439
440 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
441 {
442 // update the controls
443 m_comboMonth->SetSelection(m_date.GetMonth());
444
445 if ( AllowYearChange() )
446 {
447 if ( !m_userChangedYear )
448 m_spinYear->SetValue(m_date.Format(_T("%Y")));
449 else // don't overwrite what the user typed in
450 m_userChangedYear = FALSE;
451 }
452 }
453
454 // as the month changed, holidays did too
455 SetHolidayAttrs();
456
457 // update the calendar
458 Refresh();
459 }
460 else
461 {
462 // forbidden
463 retval = FALSE;
464 }
465 }
466 }
467
468 return retval;
469 }
470
471 void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
472 {
473 if ( m_date != date )
474 {
475 // we need to refresh the row containing the old date and the one
476 // containing the new one
477 wxDateTime dateOld = m_date;
478 m_date = date;
479
480 RefreshDate(dateOld);
481
482 // if the date is in the same row, it was already drawn correctly
483 if ( GetWeek(m_date) != GetWeek(dateOld) )
484 {
485 RefreshDate(m_date);
486 }
487 }
488 }
489
490 void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
491 {
492 wxDateTime::Tm tm1 = m_date.GetTm(),
493 tm2 = date.GetTm();
494
495 wxEventType type;
496 if ( tm1.year != tm2.year )
497 type = wxEVT_CALENDAR_YEAR_CHANGED;
498 else if ( tm1.mon != tm2.mon )
499 type = wxEVT_CALENDAR_MONTH_CHANGED;
500 else if ( tm1.mday != tm2.mday )
501 type = wxEVT_CALENDAR_DAY_CHANGED;
502 else
503 return;
504
505 if ( SetDate(date) )
506 {
507 GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
508 }
509 }
510
511 // ----------------------------------------------------------------------------
512 // date range
513 // ----------------------------------------------------------------------------
514
515 bool wxCalendarCtrl::SetLowerDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
516 {
517 bool retval = TRUE;
518
519 if ( !(date.IsValid()) || ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : TRUE ) )
520 {
521 m_lowdate = date;
522 }
523 else
524 {
525 retval = FALSE;
526 }
527
528 return retval;
529 }
530
531 bool wxCalendarCtrl::SetUpperDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
532 {
533 bool retval = TRUE;
534
535 if ( !(date.IsValid()) || ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : TRUE ) )
536 {
537 m_highdate = date;
538 }
539 else
540 {
541 retval = FALSE;
542 }
543
544 return retval;
545 }
546
547 bool wxCalendarCtrl::SetDateRange(const wxDateTime& lowerdate /* = wxDefaultDateTime */, const wxDateTime& upperdate /* = wxDefaultDateTime */)
548 {
549 bool retval = TRUE;
550
551 if (
552 ( !( lowerdate.IsValid() ) || ( ( upperdate.IsValid() ) ? ( lowerdate <= upperdate ) : TRUE ) ) &&
553 ( !( upperdate.IsValid() ) || ( ( lowerdate.IsValid() ) ? ( upperdate >= lowerdate ) : TRUE ) ) )
554 {
555 m_lowdate = lowerdate;
556 m_highdate = upperdate;
557 }
558 else
559 {
560 retval = FALSE;
561 }
562
563 return retval;
564 }
565
566 // ----------------------------------------------------------------------------
567 // date helpers
568 // ----------------------------------------------------------------------------
569
570 wxDateTime wxCalendarCtrl::GetStartDate() const
571 {
572 wxDateTime::Tm tm = m_date.GetTm();
573
574 wxDateTime date = wxDateTime(1, tm.mon, tm.year);
575
576 // rewind back
577 date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
578 ? wxDateTime::Mon : wxDateTime::Sun);
579
580 if ( GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS )
581 {
582 // We want to offset the calendar if we start on the first..
583 if ( date.GetDay() == 1 )
584 {
585 date -= wxDateSpan::Week();
586 }
587 }
588
589 return date;
590 }
591
592 bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
593 {
594 if ( !(GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
595 {
596 return date.GetMonth() == m_date.GetMonth();
597 }
598 else
599 {
600 return TRUE;
601 }
602 }
603
604 bool wxCalendarCtrl::IsDateInRange(const wxDateTime& date) const
605 {
606 bool retval = TRUE;
607 // Check if the given date is in the range specified
608 retval = ( ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : TRUE )
609 && ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : TRUE ) );
610 return retval;
611 }
612
613 bool wxCalendarCtrl::ChangeYear(wxDateTime* target) const
614 {
615 bool retval = FALSE;
616
617 if ( !(IsDateInRange(*target)) )
618 {
619 if ( target->GetYear() < m_date.GetYear() )
620 {
621 if ( target->GetYear() >= GetLowerDateLimit().GetYear() )
622 {
623 *target = GetLowerDateLimit();
624 retval = TRUE;
625 }
626 else
627 {
628 *target = m_date;
629 }
630 }
631 else
632 {
633 if ( target->GetYear() <= GetUpperDateLimit().GetYear() )
634 {
635 *target = GetUpperDateLimit();
636 retval = TRUE;
637 }
638 else
639 {
640 *target = m_date;
641 }
642 }
643 }
644 else
645 {
646 retval = TRUE;
647 }
648
649 return retval;
650 }
651
652 bool wxCalendarCtrl::ChangeMonth(wxDateTime* target) const
653 {
654 bool retval = TRUE;
655
656 if ( !(IsDateInRange(*target)) )
657 {
658 retval = FALSE;
659
660 if ( target->GetMonth() < m_date.GetMonth() )
661 {
662 *target = GetLowerDateLimit();
663 }
664 else
665 {
666 *target = GetUpperDateLimit();
667 }
668 }
669
670 return retval;
671 }
672
673 size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
674 {
675 size_t retval = date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
676 ? wxDateTime::Monday_First
677 : wxDateTime::Sunday_First);
678
679 if ( (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
680 {
681 // we need to offset an extra week if we "start" on the 1st of the month
682 wxDateTime::Tm tm = date.GetTm();
683
684 wxDateTime datetest = wxDateTime(1, tm.mon, tm.year);
685
686 // rewind back
687 datetest.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
688 ? wxDateTime::Mon : wxDateTime::Sun);
689
690 if ( datetest.GetDay() == 1 )
691 {
692 retval += 1;
693 }
694 }
695
696 return retval;
697 }
698
699 // ----------------------------------------------------------------------------
700 // size management
701 // ----------------------------------------------------------------------------
702
703 // this is a composite control and it must arrange its parts each time its
704 // size or position changes: the combobox and spinctrl are along the top of
705 // the available area and the calendar takes up therest of the space
706
707 // the static controls are supposed to be always smaller than combo/spin so we
708 // always use the latter for size calculations and position the static to take
709 // the same space
710
711 // the constants used for the layout
712 #define VERT_MARGIN 5 // distance between combo and calendar
713 #ifdef __WXMAC__
714 #define HORZ_MARGIN 5 // spin
715 #else
716 #define HORZ_MARGIN 15 // spin
717 #endif
718 wxSize wxCalendarCtrl::DoGetBestSize() const
719 {
720 // calc the size of the calendar
721 ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast
722
723 wxCoord width = 7*m_widthCol,
724 height = 7*m_heightRow + m_rowOffset + VERT_MARGIN;
725
726 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
727 {
728 // the combobox doesn't report its height correctly (it returns the
729 // height including the drop down list) so don't use it
730 height += m_spinYear->GetBestSize().y;
731 }
732
733 if ( !HasFlag(wxBORDER_NONE) )
734 {
735 // the border would clip the last line otherwise
736 height += 6;
737 width += 4;
738 }
739
740 return wxSize(width, height);
741 }
742
743 void wxCalendarCtrl::DoSetSize(int x, int y,
744 int width, int height,
745 int sizeFlags)
746 {
747 wxControl::DoSetSize(x, y, width, height, sizeFlags);
748 }
749
750 void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
751 {
752 int yDiff;
753
754 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
755 {
756 wxSize sizeCombo = m_comboMonth->GetSize();
757 wxSize sizeStatic = m_staticMonth->GetSize();
758
759 int dy = (sizeCombo.y - sizeStatic.y) / 2;
760
761 m_comboMonth->Move(x, y);
762 m_staticMonth->SetSize(x, y + dy, sizeCombo.x, sizeStatic.y);
763
764 int xDiff = sizeCombo.x + HORZ_MARGIN;
765
766 m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
767 m_staticYear->SetSize(x + xDiff, y + dy, width - xDiff, sizeStatic.y);
768
769 wxSize sizeSpin = m_spinYear->GetSize();
770 yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
771 }
772 else // no controls on the top
773 {
774 yDiff = 0;
775 }
776
777 wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
778 }
779
780 void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
781 {
782 wxControl::DoGetPosition(x, y);
783
784 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
785 {
786 // our real top corner is not in this position
787 if ( y )
788 {
789 *y -= GetMonthControl()->GetSize().y + VERT_MARGIN;
790 }
791 }
792 }
793
794 void wxCalendarCtrl::DoGetSize(int *width, int *height) const
795 {
796 wxControl::DoGetSize(width, height);
797
798 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
799 {
800 // our real height is bigger
801 if ( height && GetMonthControl())
802 {
803 *height += GetMonthControl()->GetSize().y + VERT_MARGIN;
804 }
805 }
806 }
807
808 void wxCalendarCtrl::RecalcGeometry()
809 {
810 if ( m_widthCol != 0 )
811 return;
812
813 wxClientDC dc(this);
814
815 dc.SetFont(m_font);
816
817 // determine the column width (we assume that the weekday names are always
818 // wider (in any language) than the numbers)
819 m_widthCol = 0;
820 wxDateTime::WeekDay wd;
821 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
822 {
823 wxCoord width;
824 dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
825 if ( width > m_widthCol )
826 {
827 m_widthCol = width;
828 }
829 }
830
831 // leave some margins
832 m_widthCol += 2;
833 m_heightRow += 2;
834
835 m_rowOffset = (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) ? m_heightRow : 0; // conditional in relation to style
836 }
837
838 // ----------------------------------------------------------------------------
839 // drawing
840 // ----------------------------------------------------------------------------
841
842 void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
843 {
844 wxPaintDC dc(this);
845
846 dc.SetFont(m_font);
847
848 RecalcGeometry();
849
850 #if DEBUG_PAINT
851 wxLogDebug("--- starting to paint, selection: %s, week %u\n",
852 m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
853 GetWeek(m_date));
854 #endif
855
856 wxCoord y = 0;
857
858 if ( HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
859 {
860 // draw the sequential month-selector
861
862 dc.SetBackgroundMode(wxTRANSPARENT);
863 dc.SetTextForeground(*wxBLACK);
864 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
865 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
866 dc.DrawRectangle(0, y, 7*m_widthCol, m_heightRow);
867
868 // Get extent of month-name + year
869 wxCoord monthw, monthh;
870 wxString headertext = m_date.Format(wxT("%B %Y"));
871 dc.GetTextExtent(headertext, &monthw, &monthh);
872
873 // draw month-name centered above weekdays
874 wxCoord monthx = ((m_widthCol * 7) - monthw) / 2;
875 wxCoord monthy = ((m_heightRow - monthh) / 2) + y;
876 dc.DrawText(headertext, monthx, monthy);
877
878 // calculate the "month-arrows"
879 wxPoint leftarrow[3];
880 wxPoint rightarrow[3];
881
882 int arrowheight = monthh / 2;
883
884 leftarrow[0] = wxPoint(0, arrowheight / 2);
885 leftarrow[1] = wxPoint(arrowheight / 2, 0);
886 leftarrow[2] = wxPoint(arrowheight / 2, arrowheight - 1);
887
888 rightarrow[0] = wxPoint(0, 0);
889 rightarrow[1] = wxPoint(arrowheight / 2, arrowheight / 2);
890 rightarrow[2] = wxPoint(0, arrowheight - 1);
891
892 // draw the "month-arrows"
893
894 wxCoord arrowy = (m_heightRow - arrowheight) / 2;
895 wxCoord larrowx = (m_widthCol - (arrowheight / 2)) / 2;
896 wxCoord rarrowx = ((m_widthCol - (arrowheight / 2)) / 2) + m_widthCol*6;
897 m_leftArrowRect = wxRect(0, 0, 0, 0);
898 m_rightArrowRect = wxRect(0, 0, 0, 0);
899
900 if ( AllowMonthChange() )
901 {
902 wxDateTime ldpm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) - wxDateSpan::Day(); // last day prev month
903 // Check if range permits change
904 if ( IsDateInRange(ldpm) && ( ( ldpm.GetYear() == m_date.GetYear() ) ? TRUE : AllowYearChange() ) )
905 {
906 m_leftArrowRect = wxRect(larrowx - 3, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
907 dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
908 dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
909 dc.DrawPolygon(3, leftarrow, larrowx , arrowy, wxWINDING_RULE);
910 dc.SetBrush(*wxTRANSPARENT_BRUSH);
911 dc.DrawRectangle(m_leftArrowRect);
912 }
913 wxDateTime fdnm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) + wxDateSpan::Month(); // first day next month
914 if ( IsDateInRange(fdnm) && ( ( fdnm.GetYear() == m_date.GetYear() ) ? TRUE : AllowYearChange() ) )
915 {
916 m_rightArrowRect = wxRect(rarrowx - 4, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
917 dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
918 dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
919 dc.DrawPolygon(3, rightarrow, rarrowx , arrowy, wxWINDING_RULE);
920 dc.SetBrush(*wxTRANSPARENT_BRUSH);
921 dc.DrawRectangle(m_rightArrowRect);
922 }
923 }
924
925 y += m_heightRow;
926 }
927
928 // first draw the week days
929 if ( IsExposed(0, y, 7*m_widthCol, m_heightRow) )
930 {
931 #if DEBUG_PAINT
932 wxLogDebug("painting the header");
933 #endif
934
935 dc.SetBackgroundMode(wxTRANSPARENT);
936 dc.SetTextForeground(m_colHeaderFg);
937 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
938 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
939 dc.DrawRectangle(0, y, GetClientSize().x, m_heightRow);
940
941 bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0;
942 for ( size_t wd = 0; wd < 7; wd++ )
943 {
944 size_t n;
945 if ( startOnMonday )
946 n = wd == 6 ? 0 : wd + 1;
947 else
948 n = wd;
949 wxCoord dayw, dayh;
950 dc.GetTextExtent(m_weekdays[n], &dayw, &dayh);
951 dc.DrawText(m_weekdays[n], (wd*m_widthCol) + ((m_widthCol- dayw) / 2), y); // center the day-name
952 }
953 }
954
955 // then the calendar itself
956 dc.SetTextForeground(*wxBLACK);
957 //dc.SetFont(*wxNORMAL_FONT);
958
959 y += m_heightRow;
960 wxDateTime date = GetStartDate();
961
962 #if DEBUG_PAINT
963 wxLogDebug("starting calendar from %s\n",
964 date.Format("%a %d-%m-%Y %H:%M:%S").c_str());
965 #endif
966
967 dc.SetBackgroundMode(wxSOLID);
968 for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow )
969 {
970 // if the update region doesn't intersect this row, don't paint it
971 if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) )
972 {
973 date += wxDateSpan::Week();
974
975 continue;
976 }
977
978 #if DEBUG_PAINT
979 wxLogDebug("painting week %d at y = %d\n", nWeek, y);
980 #endif
981
982 for ( size_t wd = 0; wd < 7; wd++ )
983 {
984 if ( IsDateShown(date) )
985 {
986 // don't use wxDate::Format() which prepends 0s
987 unsigned int day = date.GetDay();
988 wxString dayStr = wxString::Format(_T("%u"), day);
989 wxCoord width;
990 dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL);
991
992 bool changedColours = FALSE,
993 changedFont = FALSE;
994
995 bool isSel = FALSE;
996 wxCalendarDateAttr *attr = NULL;
997
998 if ( date.GetMonth() != m_date.GetMonth() || !IsDateInRange(date) )
999 {
1000 // surrounding week or out-of-range
1001 // draw "disabled"
1002 dc.SetTextForeground(*wxLIGHT_GREY);
1003 changedColours = TRUE;
1004 }
1005 else
1006 {
1007 isSel = date.IsSameDate(m_date);
1008 attr = m_attrs[day - 1];
1009
1010 if ( isSel )
1011 {
1012 dc.SetTextForeground(m_colHighlightFg);
1013 dc.SetTextBackground(m_colHighlightBg);
1014
1015 changedColours = TRUE;
1016 }
1017 else if ( attr )
1018 {
1019 wxColour colFg, colBg;
1020
1021 if ( attr->IsHoliday() )
1022 {
1023 colFg = m_colHolidayFg;
1024 colBg = m_colHolidayBg;
1025 }
1026 else
1027 {
1028 colFg = attr->GetTextColour();
1029 colBg = attr->GetBackgroundColour();
1030 }
1031
1032 if ( colFg.Ok() )
1033 {
1034 dc.SetTextForeground(colFg);
1035 changedColours = TRUE;
1036 }
1037
1038 if ( colBg.Ok() )
1039 {
1040 dc.SetTextBackground(colBg);
1041 changedColours = TRUE;
1042 }
1043
1044 if ( attr->HasFont() )
1045 {
1046 dc.SetFont(attr->GetFont());
1047 changedFont = TRUE;
1048 }
1049 }
1050 }
1051
1052 wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2;
1053 dc.DrawText(dayStr, x, y + 1);
1054
1055 if ( !isSel && attr && attr->HasBorder() )
1056 {
1057 wxColour colBorder;
1058 if ( attr->HasBorderColour() )
1059 {
1060 colBorder = attr->GetBorderColour();
1061 }
1062 else
1063 {
1064 colBorder = m_foregroundColour;
1065 }
1066
1067 wxPen pen(colBorder, 1, wxSOLID);
1068 dc.SetPen(pen);
1069 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1070
1071 switch ( attr->GetBorder() )
1072 {
1073 case wxCAL_BORDER_SQUARE:
1074 dc.DrawRectangle(x - 2, y,
1075 width + 4, m_heightRow);
1076 break;
1077
1078 case wxCAL_BORDER_ROUND:
1079 dc.DrawEllipse(x - 2, y,
1080 width + 4, m_heightRow);
1081 break;
1082
1083 default:
1084 wxFAIL_MSG(_T("unknown border type"));
1085 }
1086 }
1087
1088 if ( changedColours )
1089 {
1090 dc.SetTextForeground(m_foregroundColour);
1091 dc.SetTextBackground(m_backgroundColour);
1092 }
1093
1094 if ( changedFont )
1095 {
1096 dc.SetFont(m_font);
1097 }
1098 }
1099 //else: just don't draw it
1100
1101 date += wxDateSpan::Day();
1102 }
1103 }
1104
1105 // Greying out out-of-range background
1106 bool showSurrounding = (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) != 0;
1107
1108 date = ( showSurrounding ) ? GetStartDate() : wxDateTime(1, m_date.GetMonth(), m_date.GetYear());
1109 if ( !IsDateInRange(date) )
1110 {
1111 wxDateTime firstOOR = GetLowerDateLimit() - wxDateSpan::Day(); // first out-of-range
1112
1113 wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
1114 oorbrush.SetStyle(wxFDIAGONAL_HATCH);
1115
1116 HighlightRange(&dc, date, firstOOR, wxTRANSPARENT_PEN, &oorbrush);
1117 }
1118
1119 date = ( showSurrounding ) ? GetStartDate() + wxDateSpan::Weeks(6) - wxDateSpan::Day() : wxDateTime().SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear());
1120 if ( !IsDateInRange(date) )
1121 {
1122 wxDateTime firstOOR = GetUpperDateLimit() + wxDateSpan::Day(); // first out-of-range
1123
1124 wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
1125 oorbrush.SetStyle(wxFDIAGONAL_HATCH);
1126
1127 HighlightRange(&dc, firstOOR, date, wxTRANSPARENT_PEN, &oorbrush);
1128 }
1129
1130 #if DEBUG_PAINT
1131 wxLogDebug("+++ finished painting");
1132 #endif
1133 }
1134
1135 void wxCalendarCtrl::RefreshDate(const wxDateTime& date)
1136 {
1137 RecalcGeometry();
1138
1139 wxRect rect;
1140
1141 // always refresh the whole row at once because our OnPaint() will draw
1142 // the whole row anyhow - and this allows the small optimisation in
1143 // OnClick() below to work
1144 rect.x = 0;
1145
1146 rect.y = (m_heightRow * GetWeek(date)) + m_rowOffset;
1147
1148 rect.width = 7*m_widthCol;
1149 rect.height = m_heightRow;
1150
1151 #ifdef __WXMSW__
1152 // VZ: for some reason, the selected date seems to occupy more space under
1153 // MSW - this is probably some bug in the font size calculations, but I
1154 // don't know where exactly. This fix is ugly and leads to more
1155 // refreshes than really needed, but without it the selected days
1156 // leaves even more ugly underscores on screen.
1157 rect.Inflate(0, 1);
1158 #endif // MSW
1159
1160 #if DEBUG_PAINT
1161 wxLogDebug("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
1162 GetWeek(date),
1163 rect.x, rect.y,
1164 rect.x + rect.width, rect.y + rect.height);
1165 #endif
1166
1167 Refresh(TRUE, &rect);
1168 }
1169
1170 void wxCalendarCtrl::HighlightRange(wxPaintDC* pDC, const wxDateTime& fromdate, const wxDateTime& todate, wxPen* pPen, wxBrush* pBrush)
1171 {
1172 // Highlights the given range using pen and brush
1173 // Does nothing if todate < fromdate
1174
1175
1176 #if DEBUG_PAINT
1177 wxLogDebug("+++ HighlightRange: (%s) - (%s) +++", fromdate.Format("%d %m %Y"), todate.Format("%d %m %Y"));
1178 #endif
1179
1180 if ( todate >= fromdate )
1181 {
1182 // do stuff
1183 // date-coordinates
1184 int fd, fw;
1185 int td, tw;
1186
1187 // implicit: both dates must be currently shown - checked by GetDateCoord
1188 if ( GetDateCoord(fromdate, &fd, &fw) && GetDateCoord(todate, &td, &tw) )
1189 {
1190 #if DEBUG_PAINT
1191 wxLogDebug("Highlight range: (%i, %i) - (%i, %i)", fd, fw, td, tw);
1192 #endif
1193 if ( ( (tw - fw) == 1 ) && ( td < fd ) )
1194 {
1195 // special case: interval 7 days or less not in same week
1196 // split in two seperate intervals
1197 wxDateTime tfd = fromdate + wxDateSpan::Days(7-fd);
1198 wxDateTime ftd = tfd + wxDateSpan::Day();
1199 #if DEBUG_PAINT
1200 wxLogDebug("Highlight: Seperate segments");
1201 #endif
1202 // draw seperately
1203 HighlightRange(pDC, fromdate, tfd, pPen, pBrush);
1204 HighlightRange(pDC, ftd, todate, pPen, pBrush);
1205 }
1206 else
1207 {
1208 int numpoints;
1209 wxPoint corners[8]; // potentially 8 corners in polygon
1210
1211 if ( fw == tw )
1212 {
1213 // simple case: same week
1214 numpoints = 4;
1215 corners[0] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset);
1216 corners[1] = wxPoint((fd - 1) * m_widthCol, ((fw + 1 ) * m_heightRow) + m_rowOffset);
1217 corners[2] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset);
1218 corners[3] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset);
1219 }
1220 else
1221 {
1222 int cidx = 0;
1223 // "complex" polygon
1224 corners[cidx] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
1225
1226 if ( fd > 1 )
1227 {
1228 corners[cidx] = wxPoint((fd - 1) * m_widthCol, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
1229 corners[cidx] = wxPoint(0, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
1230 }
1231
1232 corners[cidx] = wxPoint(0, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
1233 corners[cidx] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
1234
1235 if ( td < 7 )
1236 {
1237 corners[cidx] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
1238 corners[cidx] = wxPoint(7 * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
1239 }
1240
1241 corners[cidx] = wxPoint(7 * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
1242
1243 numpoints = cidx;
1244 }
1245
1246 // draw the polygon
1247 pDC->SetBrush(*pBrush);
1248 pDC->SetPen(*pPen);
1249 pDC->DrawPolygon(numpoints, corners);
1250 }
1251 }
1252 }
1253 // else do nothing
1254 #if DEBUG_PAINT
1255 wxLogDebug("--- HighlightRange ---");
1256 #endif
1257 }
1258
1259 bool wxCalendarCtrl::GetDateCoord(const wxDateTime& date, int *day, int *week) const
1260 {
1261 bool retval = TRUE;
1262
1263 #if DEBUG_PAINT
1264 wxLogDebug("+++ GetDateCoord: (%s) +++", date.Format("%d %m %Y"));
1265 #endif
1266
1267 if ( IsDateShown(date) )
1268 {
1269 bool startOnMonday = ( GetWindowStyle() & wxCAL_MONDAY_FIRST ) != 0;
1270
1271 // Find day
1272 *day = date.GetWeekDay();
1273
1274 if ( *day == 0 ) // sunday
1275 {
1276 *day = ( startOnMonday ) ? 7 : 1;
1277 }
1278 else
1279 {
1280 day += ( startOnMonday ) ? 0 : 1;
1281 }
1282
1283 int targetmonth = date.GetMonth() + (12 * date.GetYear());
1284 int thismonth = m_date.GetMonth() + (12 * m_date.GetYear());
1285
1286 // Find week
1287 if ( targetmonth == thismonth )
1288 {
1289 *week = GetWeek(date);
1290 }
1291 else
1292 {
1293 if ( targetmonth < thismonth )
1294 {
1295 *week = 1; // trivial
1296 }
1297 else // targetmonth > thismonth
1298 {
1299 wxDateTime ldcm;
1300 int lastweek;
1301 int lastday;
1302
1303 // get the datecoord of the last day in the month currently shown
1304 #if DEBUG_PAINT
1305 wxLogDebug(" +++ LDOM +++");
1306 #endif
1307 GetDateCoord(ldcm.SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear()), &lastday, &lastweek);
1308 #if DEBUG_PAINT
1309 wxLogDebug(" --- LDOM ---");
1310 #endif
1311
1312 wxTimeSpan span = date - ldcm;
1313
1314 int daysfromlast = span.GetDays();
1315 #if DEBUG_PAINT
1316 wxLogDebug("daysfromlast: %i", daysfromlast);
1317 #endif
1318 if ( daysfromlast + lastday > 7 ) // past week boundary
1319 {
1320 int wholeweeks = (daysfromlast / 7);
1321 *week = wholeweeks + lastweek;
1322 if ( (daysfromlast - (7 * wholeweeks) + lastday) > 7 )
1323 {
1324 *week += 1;
1325 }
1326 }
1327 else
1328 {
1329 *week = lastweek;
1330 }
1331 }
1332 }
1333 }
1334 else
1335 {
1336 *day = -1;
1337 *week = -1;
1338 retval = FALSE;
1339 }
1340
1341 #if DEBUG_PAINT
1342 wxLogDebug("--- GetDateCoord: (%s) = (%i, %i) ---", date.Format("%d %m %Y"), *day, *week);
1343 #endif
1344
1345 return retval;
1346 }
1347
1348 // ----------------------------------------------------------------------------
1349 // mouse handling
1350 // ----------------------------------------------------------------------------
1351
1352 void wxCalendarCtrl::OnDClick(wxMouseEvent& event)
1353 {
1354 if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY )
1355 {
1356 event.Skip();
1357 }
1358 else
1359 {
1360 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
1361 }
1362 }
1363
1364 void wxCalendarCtrl::OnClick(wxMouseEvent& event)
1365 {
1366 wxDateTime date;
1367 wxDateTime::WeekDay wday;
1368 switch ( HitTest(event.GetPosition(), &date, &wday) )
1369 {
1370 case wxCAL_HITTEST_DAY:
1371 if ( IsDateInRange(date) )
1372 {
1373 ChangeDay(date);
1374
1375 GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED,
1376 wxEVT_CALENDAR_SEL_CHANGED);
1377 }
1378 break;
1379
1380 case wxCAL_HITTEST_HEADER:
1381 {
1382 wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED);
1383 event.m_wday = wday;
1384 (void)GetEventHandler()->ProcessEvent(event);
1385 }
1386 break;
1387
1388 case wxCAL_HITTEST_DECMONTH:
1389 case wxCAL_HITTEST_INCMONTH:
1390 case wxCAL_HITTEST_SURROUNDING_WEEK:
1391 SetDateAndNotify(date); // we probably only want to refresh the control. No notification.. (maybe as an option?)
1392 break;
1393
1394 default:
1395 wxFAIL_MSG(_T("unknown hittest code"));
1396 // fall through
1397
1398 case wxCAL_HITTEST_NOWHERE:
1399 event.Skip();
1400 break;
1401 }
1402 }
1403
1404 wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos,
1405 wxDateTime *date,
1406 wxDateTime::WeekDay *wd)
1407 {
1408 RecalcGeometry();
1409
1410 wxCoord y = pos.y;
1411
1412 ///////////////////////////////////////////////////////////////////////////////////////////////////////
1413 if ( (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
1414 {
1415 // Header: month
1416
1417 // we need to find out if the hit is on left arrow, on month or on right arrow
1418 // left arrow?
1419 if ( wxRegion(m_leftArrowRect).Contains(pos) == wxInRegion )
1420 {
1421 if ( date )
1422 {
1423 if ( IsDateInRange(m_date - wxDateSpan::Month()) )
1424 {
1425 *date = m_date - wxDateSpan::Month();
1426 }
1427 else
1428 {
1429 *date = GetLowerDateLimit();
1430 }
1431 }
1432
1433 return wxCAL_HITTEST_DECMONTH;
1434 }
1435
1436 if ( wxRegion(m_rightArrowRect).Contains(pos) == wxInRegion )
1437 {
1438 if ( date )
1439 {
1440 if ( IsDateInRange(m_date + wxDateSpan::Month()) )
1441 {
1442 *date = m_date + wxDateSpan::Month();
1443 }
1444 else
1445 {
1446 *date = GetUpperDateLimit();
1447 }
1448 }
1449
1450 return wxCAL_HITTEST_INCMONTH;
1451 }
1452
1453 }
1454
1455 ///////////////////////////////////////////////////////////////////////////////////////////////////////
1456 // Header: Days
1457 int wday = pos.x / m_widthCol;
1458 // if ( y < m_heightRow )
1459 if ( y < (m_heightRow + m_rowOffset) )
1460 {
1461 if ( y > m_rowOffset )
1462 {
1463 if ( wd )
1464 {
1465 if ( GetWindowStyle() & wxCAL_MONDAY_FIRST )
1466 {
1467 wday = wday == 6 ? 0 : wday + 1;
1468 }
1469
1470 *wd = (wxDateTime::WeekDay)wday;
1471 }
1472
1473 return wxCAL_HITTEST_HEADER;
1474 }
1475 else
1476 {
1477 return wxCAL_HITTEST_NOWHERE;
1478 }
1479 }
1480
1481 // int week = (y - m_heightRow) / m_heightRow;
1482 int week = (y - (m_heightRow + m_rowOffset)) / m_heightRow;
1483 if ( week >= 6 || wday >= 7 )
1484 {
1485 return wxCAL_HITTEST_NOWHERE;
1486 }
1487
1488 wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday);
1489
1490 if ( IsDateShown(dt) )
1491 {
1492 if ( date )
1493 *date = dt;
1494
1495 if ( dt.GetMonth() == m_date.GetMonth() )
1496 {
1497
1498 return wxCAL_HITTEST_DAY;
1499 }
1500 else
1501 {
1502 return wxCAL_HITTEST_SURROUNDING_WEEK;
1503 }
1504 }
1505 else
1506 {
1507 return wxCAL_HITTEST_NOWHERE;
1508 }
1509 }
1510
1511 // ----------------------------------------------------------------------------
1512 // subcontrols events handling
1513 // ----------------------------------------------------------------------------
1514
1515 void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event)
1516 {
1517 wxDateTime::Tm tm = m_date.GetTm();
1518
1519 wxDateTime::Month mon = (wxDateTime::Month)event.GetInt();
1520 if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) )
1521 {
1522 tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year);
1523 }
1524
1525 wxDateTime target = wxDateTime(tm.mday, mon, tm.year);
1526
1527 ChangeMonth(&target);
1528 SetDateAndNotify(target);
1529 }
1530
1531 void wxCalendarCtrl::OnYearChange(wxCommandEvent& event)
1532 {
1533 int year = (int)event.GetInt();
1534 if ( year == INT_MIN )
1535 {
1536 // invalid year in the spin control, ignore it
1537 return;
1538 }
1539
1540 // set the flag for SetDate(): otherwise it would overwrite the year
1541 // typed in by the user
1542 m_userChangedYear = TRUE;
1543
1544 wxDateTime::Tm tm = m_date.GetTm();
1545
1546 if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
1547 {
1548 tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
1549 }
1550
1551 wxDateTime target = wxDateTime(tm.mday, tm.mon, year);
1552
1553 if ( ChangeYear(&target) )
1554 {
1555 SetDateAndNotify(target);
1556 }
1557 else
1558 {
1559 // In this case we don't want to change the date. That would put us
1560 // inside the same year but a strange number of months forward/back..
1561 m_spinYear->SetValue(target.GetYear());
1562 }
1563 }
1564
1565 // ----------------------------------------------------------------------------
1566 // keyboard interface
1567 // ----------------------------------------------------------------------------
1568
1569 void wxCalendarCtrl::OnChar(wxKeyEvent& event)
1570 {
1571 wxDateTime target;
1572 switch ( event.KeyCode() )
1573 {
1574 case _T('+'):
1575 case WXK_ADD:
1576 target = m_date + wxDateSpan::Year();
1577 if ( ChangeYear(&target) )
1578 {
1579 SetDateAndNotify(target);
1580 }
1581 break;
1582
1583 case _T('-'):
1584 case WXK_SUBTRACT:
1585 target = m_date - wxDateSpan::Year();
1586 if ( ChangeYear(&target) )
1587 {
1588 SetDateAndNotify(target);
1589 }
1590 break;
1591
1592 case WXK_PRIOR:
1593 target = m_date - wxDateSpan::Month();
1594 ChangeMonth(&target);
1595 SetDateAndNotify(target); // always
1596 break;
1597
1598 case WXK_NEXT:
1599 target = m_date + wxDateSpan::Month();
1600 ChangeMonth(&target);
1601 SetDateAndNotify(target); // always
1602 break;
1603
1604 case WXK_RIGHT:
1605 if ( event.ControlDown() )
1606 {
1607 target = wxDateTime(m_date).SetToNextWeekDay(
1608 GetWindowStyle() & wxCAL_MONDAY_FIRST
1609 ? wxDateTime::Sun : wxDateTime::Sat);
1610 if ( !IsDateInRange(target) )
1611 {
1612 target = GetUpperDateLimit();
1613 }
1614 SetDateAndNotify(target);
1615 }
1616 else
1617 SetDateAndNotify(m_date + wxDateSpan::Day());
1618 break;
1619
1620 case WXK_LEFT:
1621 if ( event.ControlDown() )
1622 {
1623 target = wxDateTime(m_date).SetToPrevWeekDay(
1624 GetWindowStyle() & wxCAL_MONDAY_FIRST
1625 ? wxDateTime::Mon : wxDateTime::Sun);
1626 if ( !IsDateInRange(target) )
1627 {
1628 target = GetLowerDateLimit();
1629 }
1630 SetDateAndNotify(target);
1631 }
1632 else
1633 SetDateAndNotify(m_date - wxDateSpan::Day());
1634 break;
1635
1636 case WXK_UP:
1637 SetDateAndNotify(m_date - wxDateSpan::Week());
1638 break;
1639
1640 case WXK_DOWN:
1641 SetDateAndNotify(m_date + wxDateSpan::Week());
1642 break;
1643
1644 case WXK_HOME:
1645 if ( event.ControlDown() )
1646 SetDateAndNotify(wxDateTime::Today());
1647 else
1648 SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
1649 break;
1650
1651 case WXK_END:
1652 SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
1653 break;
1654
1655 case WXK_RETURN:
1656 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
1657 break;
1658
1659 default:
1660 event.Skip();
1661 }
1662 }
1663
1664 // ----------------------------------------------------------------------------
1665 // holidays handling
1666 // ----------------------------------------------------------------------------
1667
1668 void wxCalendarCtrl::EnableHolidayDisplay(bool display)
1669 {
1670 long style = GetWindowStyle();
1671 if ( display )
1672 style |= wxCAL_SHOW_HOLIDAYS;
1673 else
1674 style &= ~wxCAL_SHOW_HOLIDAYS;
1675
1676 SetWindowStyle(style);
1677
1678 if ( display )
1679 SetHolidayAttrs();
1680 else
1681 ResetHolidayAttrs();
1682
1683 Refresh();
1684 }
1685
1686 void wxCalendarCtrl::SetHolidayAttrs()
1687 {
1688 if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
1689 {
1690 ResetHolidayAttrs();
1691
1692 wxDateTime::Tm tm = m_date.GetTm();
1693 wxDateTime dtStart(1, tm.mon, tm.year),
1694 dtEnd = dtStart.GetLastMonthDay();
1695
1696 wxDateTimeArray hol;
1697 wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
1698
1699 size_t count = hol.GetCount();
1700 for ( size_t n = 0; n < count; n++ )
1701 {
1702 SetHoliday(hol[n].GetDay());
1703 }
1704 }
1705 }
1706
1707 void wxCalendarCtrl::SetHoliday(size_t day)
1708 {
1709 wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
1710
1711 wxCalendarDateAttr *attr = GetAttr(day);
1712 if ( !attr )
1713 {
1714 attr = new wxCalendarDateAttr;
1715 }
1716
1717 attr->SetHoliday(TRUE);
1718
1719 // can't use SetAttr() because it would delete this pointer
1720 m_attrs[day - 1] = attr;
1721 }
1722
1723 void wxCalendarCtrl::ResetHolidayAttrs()
1724 {
1725 for ( size_t day = 0; day < 31; day++ )
1726 {
1727 if ( m_attrs[day] )
1728 {
1729 m_attrs[day]->SetHoliday(FALSE);
1730 }
1731 }
1732 }
1733
1734 // ----------------------------------------------------------------------------
1735 // wxCalendarEvent
1736 // ----------------------------------------------------------------------------
1737
1738 void wxCalendarEvent::Init()
1739 {
1740 m_wday = wxDateTime::Inv_WeekDay;
1741 }
1742
1743 wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
1744 : wxCommandEvent(type, cal->GetId())
1745 {
1746 m_date = cal->GetDate();
1747 SetEventObject(cal);
1748 }
1749
1750 #endif // wxUSE_CALENDARCTRL
1751