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