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