1 /////////////////////////////////////////////////////////////////////////////// 
   2 // Name:        generic/calctrl.cpp 
   3 // Purpose:     implementation fo the generic wxCalendarCtrl 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence 
  10 /////////////////////////////////////////////////////////////////////////////// 
  12 // ============================================================================ 
  14 // ============================================================================ 
  16 // ---------------------------------------------------------------------------- 
  18 // ---------------------------------------------------------------------------- 
  20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA) 
  21     #pragma implementation "calctrl.h" 
  24 // For compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  32     #include "wx/dcclient.h" 
  33     #include "wx/settings.h" 
  35     #include "wx/combobox.h" 
  36     #include "wx/listbox.h" 
  37     #include "wx/stattext.h" 
  38     #include "wx/textctrl.h" 
  41 #if wxUSE_CALENDARCTRL 
  43 #include "wx/spinctrl.h" 
  45 // if wxDatePickerCtrl code doesn't define the date event, do it here as we 
  47 #if !wxUSE_DATEPICKCTRL 
  48     #define _WX_DEFINE_DATE_EVENTS_ 
  51 #include "wx/calctrl.h" 
  55 // ---------------------------------------------------------------------------- 
  57 // ---------------------------------------------------------------------------- 
  59 BEGIN_EVENT_TABLE(wxCalendarCtrl
, wxControl
) 
  60     EVT_PAINT(wxCalendarCtrl::OnPaint
) 
  62     EVT_CHAR(wxCalendarCtrl::OnChar
) 
  64     EVT_LEFT_DOWN(wxCalendarCtrl::OnClick
) 
  65     EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick
) 
  68 #if wxUSE_EXTENDED_RTTI 
  69 WX_DEFINE_FLAGS( wxCalendarCtrlStyle 
) 
  71 wxBEGIN_FLAGS( wxCalendarCtrlStyle 
) 
  72     // new style border flags, we put them first to 
  73     // use them for streaming out 
  74     wxFLAGS_MEMBER(wxBORDER_SIMPLE
) 
  75     wxFLAGS_MEMBER(wxBORDER_SUNKEN
) 
  76     wxFLAGS_MEMBER(wxBORDER_DOUBLE
) 
  77     wxFLAGS_MEMBER(wxBORDER_RAISED
) 
  78     wxFLAGS_MEMBER(wxBORDER_STATIC
) 
  79     wxFLAGS_MEMBER(wxBORDER_NONE
) 
  81     // old style border flags 
  82     wxFLAGS_MEMBER(wxSIMPLE_BORDER
) 
  83     wxFLAGS_MEMBER(wxSUNKEN_BORDER
) 
  84     wxFLAGS_MEMBER(wxDOUBLE_BORDER
) 
  85     wxFLAGS_MEMBER(wxRAISED_BORDER
) 
  86     wxFLAGS_MEMBER(wxSTATIC_BORDER
) 
  87     wxFLAGS_MEMBER(wxBORDER
) 
  89     // standard window styles 
  90     wxFLAGS_MEMBER(wxTAB_TRAVERSAL
) 
  91     wxFLAGS_MEMBER(wxCLIP_CHILDREN
) 
  92     wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW
) 
  93     wxFLAGS_MEMBER(wxWANTS_CHARS
) 
  94     wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE
) 
  95     wxFLAGS_MEMBER(wxALWAYS_SHOW_SB 
) 
  96     wxFLAGS_MEMBER(wxVSCROLL
) 
  97     wxFLAGS_MEMBER(wxHSCROLL
) 
  99     wxFLAGS_MEMBER(wxCAL_SUNDAY_FIRST
) 
 100     wxFLAGS_MEMBER(wxCAL_MONDAY_FIRST
) 
 101     wxFLAGS_MEMBER(wxCAL_SHOW_HOLIDAYS
) 
 102     wxFLAGS_MEMBER(wxCAL_NO_YEAR_CHANGE
) 
 103     wxFLAGS_MEMBER(wxCAL_NO_MONTH_CHANGE
) 
 104     wxFLAGS_MEMBER(wxCAL_SEQUENTIAL_MONTH_SELECTION
) 
 105     wxFLAGS_MEMBER(wxCAL_SHOW_SURROUNDING_WEEKS
) 
 107 wxEND_FLAGS( wxCalendarCtrlStyle 
) 
 109 IMPLEMENT_DYNAMIC_CLASS_XTI(wxCalendarCtrl
, wxControl
,"wx/calctrl.h") 
 111 wxBEGIN_PROPERTIES_TABLE(wxCalendarCtrl
) 
 112     wxEVENT_RANGE_PROPERTY( Updated 
, wxEVT_CALENDAR_SEL_CHANGED 
, wxEVT_CALENDAR_WEEKDAY_CLICKED 
, wxCalendarEvent 
) 
 113     wxHIDE_PROPERTY( Children 
) 
 114     wxPROPERTY( Date
,wxDateTime
, SetDate 
, GetDate
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) 
 115     wxPROPERTY_FLAGS( WindowStyle 
, wxCalendarCtrlStyle 
, long , SetWindowStyleFlag 
, GetWindowStyleFlag 
, , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style 
 116 wxEND_PROPERTIES_TABLE() 
 118 wxBEGIN_HANDLERS_TABLE(wxCalendarCtrl
) 
 119 wxEND_HANDLERS_TABLE() 
 121 wxCONSTRUCTOR_6( wxCalendarCtrl 
, wxWindow
* , Parent 
, wxWindowID 
, Id 
, wxDateTime 
, Date 
, wxPoint 
, Position 
, wxSize 
, Size 
, long , WindowStyle 
) 
 123 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl
, wxControl
) 
 125 IMPLEMENT_DYNAMIC_CLASS(wxCalendarEvent
, wxDateEvent
) 
 127 // ---------------------------------------------------------------------------- 
 129 // ---------------------------------------------------------------------------- 
 131 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_SEL_CHANGED
) 
 132 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DAY_CHANGED
) 
 133 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_MONTH_CHANGED
) 
 134 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_YEAR_CHANGED
) 
 135 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DOUBLECLICKED
) 
 136 DEFINE_EVENT_TYPE(wxEVT_CALENDAR_WEEKDAY_CLICKED
) 
 138 // ============================================================================ 
 140 // ============================================================================ 
 142 // ---------------------------------------------------------------------------- 
 144 // ---------------------------------------------------------------------------- 
 146 wxCalendarCtrl::wxCalendarCtrl(wxWindow 
*parent
, 
 148                    const wxDateTime
& date
, 
 152                    const wxString
& name
) 
 156     (void)Create(parent
, id
, date
, pos
, size
, style
, name
); 
 159 void wxCalendarCtrl::Init() 
 164     m_staticMonth 
= NULL
; 
 166     m_userChangedYear 
= false; 
 171     wxDateTime::WeekDay wd
; 
 172     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 174         m_weekdays
[wd
] = wxDateTime::GetWeekDayName(wd
, wxDateTime::Name_Abbr
); 
 177     for ( size_t n 
= 0; n 
< WXSIZEOF(m_attrs
); n
++ ) 
 182     m_colHighlightFg 
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT
); 
 183     m_colHighlightBg 
= wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
); 
 185     m_colHolidayFg 
= *wxRED
; 
 186     // don't set m_colHolidayBg - by default, same as our bg colour 
 188     m_colHeaderFg 
= *wxBLUE
; 
 189     m_colHeaderBg 
= *wxLIGHT_GREY
; 
 192 bool wxCalendarCtrl::Create(wxWindow 
*parent
, 
 194                             const wxDateTime
& date
, 
 198                             const wxString
& name
) 
 200     if ( !wxControl::Create(parent
, id
, pos
, size
, 
 201                             style 
| wxCLIP_CHILDREN 
| wxWANTS_CHARS
, 
 202                             wxDefaultValidator
, name
) ) 
 207     // needed to get the arrow keys normally used for the dialog navigation 
 208     SetWindowStyle(style 
| wxWANTS_CHARS
); 
 210     m_date 
= date
.IsValid() ? date 
: wxDateTime::Today(); 
 212     m_lowdate 
= wxDefaultDateTime
; 
 213     m_highdate 
= wxDefaultDateTime
; 
 215     if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 217         CreateYearSpinCtrl(); 
 218         m_staticYear 
= new wxStaticText(GetParent(), wxID_ANY
, m_date
.Format(_T("%Y")), 
 219                                         wxDefaultPosition
, wxDefaultSize
, 
 222         CreateMonthComboBox(); 
 223         m_staticMonth 
= new wxStaticText(GetParent(), wxID_ANY
, m_date
.Format(_T("%B")), 
 224                                          wxDefaultPosition
, wxDefaultSize
, 
 228     ShowCurrentControls(); 
 230     // we need to set the position as well because the main control position 
 231     // is not the same as the one specified in pos if we have the controls 
 236     // Since we don't paint the whole background make sure that the platform 
 237     // will use the right one. 
 238     SetBackgroundColour(GetBackgroundColour()); 
 245 wxCalendarCtrl::~wxCalendarCtrl() 
 247     for ( size_t n 
= 0; n 
< WXSIZEOF(m_attrs
); n
++ ) 
 253 // ---------------------------------------------------------------------------- 
 254 // Create the wxComboBox and wxSpinCtrl 
 255 // ---------------------------------------------------------------------------- 
 257 void wxCalendarCtrl::CreateMonthComboBox() 
 259     m_comboMonth 
= new wxComboBox(GetParent(), wxID_ANY
, 
 264                                   wxCB_READONLY 
| wxCLIP_SIBLINGS
); 
 267     for ( m 
= wxDateTime::Jan
; m 
< wxDateTime::Inv_Month
; wxNextMonth(m
) ) 
 269         m_comboMonth
->Append(wxDateTime::GetMonthName(m
)); 
 272     m_comboMonth
->SetSelection(GetDate().GetMonth()); 
 273     m_comboMonth
->SetSize(wxDefaultCoord
, 
 277                           wxSIZE_AUTO_WIDTH
|wxSIZE_AUTO_HEIGHT
); 
 279     m_comboMonth
->Connect(wxEVT_COMMAND_COMBOBOX_SELECTED
, 
 280                           wxCommandEventHandler(wxCalendarCtrl::OnMonthChange
), 
 284 void wxCalendarCtrl::CreateYearSpinCtrl() 
 286     m_spinYear 
= new wxSpinCtrl(GetParent(), wxID_ANY
, 
 287                                 GetDate().Format(_T("%Y")), 
 290                                 wxSP_ARROW_KEYS 
| wxCLIP_SIBLINGS
, 
 291                                 -4300, 10000, GetDate().GetYear()); 
 293     m_spinYear
->Connect(wxEVT_COMMAND_TEXT_UPDATED
, 
 294                         wxCommandEventHandler(wxCalendarCtrl::OnYearTextChange
), 
 297     m_spinYear
->Connect(wxEVT_COMMAND_SPINCTRL_UPDATED
, 
 298                         wxCommandEventHandler(wxCalendarCtrl::OnYearChange
), 
 302 // ---------------------------------------------------------------------------- 
 303 // forward wxWin functions to subcontrols 
 304 // ---------------------------------------------------------------------------- 
 306 bool wxCalendarCtrl::Destroy() 
 309         m_staticYear
->Destroy(); 
 311         m_spinYear
->Destroy(); 
 313         m_comboMonth
->Destroy(); 
 315         m_staticMonth
->Destroy(); 
 320     m_staticMonth 
= NULL
; 
 322     return wxControl::Destroy(); 
 325 bool wxCalendarCtrl::Show(bool show
) 
 327     if ( !wxControl::Show(show
) ) 
 332     if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 334         if ( GetMonthControl() ) 
 336             GetMonthControl()->Show(show
); 
 337             GetYearControl()->Show(show
); 
 344 bool wxCalendarCtrl::Enable(bool enable
) 
 346     if ( !wxControl::Enable(enable
) ) 
 351     if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 353         GetMonthControl()->Enable(enable
); 
 354         GetYearControl()->Enable(enable
); 
 360 // ---------------------------------------------------------------------------- 
 361 // enable/disable month/year controls 
 362 // ---------------------------------------------------------------------------- 
 364 void wxCalendarCtrl::ShowCurrentControls() 
 366     if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 368         if ( AllowMonthChange() ) 
 370             m_comboMonth
->Show(); 
 371             m_staticMonth
->Hide(); 
 373             if ( AllowYearChange() ) 
 376                 m_staticYear
->Hide(); 
 384             m_comboMonth
->Hide(); 
 385             m_staticMonth
->Show(); 
 388         // year change not allowed here 
 390         m_staticYear
->Show(); 
 394 wxControl 
*wxCalendarCtrl::GetMonthControl() const 
 396     return AllowMonthChange() ? (wxControl 
*)m_comboMonth 
: (wxControl 
*)m_staticMonth
; 
 399 wxControl 
*wxCalendarCtrl::GetYearControl() const 
 401     return AllowYearChange() ? (wxControl 
*)m_spinYear 
: (wxControl 
*)m_staticYear
; 
 404 void wxCalendarCtrl::EnableYearChange(bool enable
) 
 406     if ( enable 
!= AllowYearChange() ) 
 408         long style 
= GetWindowStyle(); 
 410             style 
&= ~wxCAL_NO_YEAR_CHANGE
; 
 412             style 
|= wxCAL_NO_YEAR_CHANGE
; 
 413         SetWindowStyle(style
); 
 415         ShowCurrentControls(); 
 416         if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION 
) 
 423 void wxCalendarCtrl::EnableMonthChange(bool enable
) 
 425     if ( enable 
!= AllowMonthChange() ) 
 427         long style 
= GetWindowStyle(); 
 429             style 
&= ~wxCAL_NO_MONTH_CHANGE
; 
 431             style 
|= wxCAL_NO_MONTH_CHANGE
; 
 432         SetWindowStyle(style
); 
 434         ShowCurrentControls(); 
 435         if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION 
) 
 442 // ---------------------------------------------------------------------------- 
 444 // ---------------------------------------------------------------------------- 
 446 bool wxCalendarCtrl::SetDate(const wxDateTime
& date
) 
 450     bool sameMonth 
= m_date
.GetMonth() == date
.GetMonth(), 
 451          sameYear 
= m_date
.GetYear() == date
.GetYear(); 
 453     if ( IsDateInRange(date
) ) 
 455         if ( sameMonth 
&& sameYear 
) 
 457             // just change the day 
 462             if ( AllowMonthChange() && (AllowYearChange() || sameYear
) ) 
 467                 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 469                     // update the controls 
 470                     m_comboMonth
->SetSelection(m_date
.GetMonth()); 
 472                     if ( AllowYearChange() ) 
 474                         if ( !m_userChangedYear 
) 
 475                             m_spinYear
->SetValue(m_date
.Format(_T("%Y"))); 
 479                 // as the month changed, holidays did too 
 482                 // update the calendar 
 493     m_userChangedYear 
= false; 
 498 void wxCalendarCtrl::ChangeDay(const wxDateTime
& date
) 
 500     if ( m_date 
!= date 
) 
 502         // we need to refresh the row containing the old date and the one 
 503         // containing the new one 
 504         wxDateTime dateOld 
= m_date
; 
 507         RefreshDate(dateOld
); 
 509         // if the date is in the same row, it was already drawn correctly 
 510         if ( GetWeek(m_date
) != GetWeek(dateOld
) ) 
 517 void wxCalendarCtrl::SetDateAndNotify(const wxDateTime
& date
) 
 519     wxDateTime::Tm tm1 
= m_date
.GetTm(), 
 523     if ( tm1
.year 
!= tm2
.year 
) 
 524         type 
= wxEVT_CALENDAR_YEAR_CHANGED
; 
 525     else if ( tm1
.mon 
!= tm2
.mon 
) 
 526         type 
= wxEVT_CALENDAR_MONTH_CHANGED
; 
 527     else if ( tm1
.mday 
!= tm2
.mday 
) 
 528         type 
= wxEVT_CALENDAR_DAY_CHANGED
; 
 534         GenerateEvents(type
, wxEVT_CALENDAR_SEL_CHANGED
); 
 538 // ---------------------------------------------------------------------------- 
 540 // ---------------------------------------------------------------------------- 
 542 bool wxCalendarCtrl::SetLowerDateLimit(const wxDateTime
& date 
/* = wxDefaultDateTime */) 
 546     if ( !(date
.IsValid()) || ( ( m_highdate
.IsValid() ) ? ( date 
<= m_highdate 
) : true ) ) 
 558 bool wxCalendarCtrl::SetUpperDateLimit(const wxDateTime
& date 
/* = wxDefaultDateTime */) 
 562     if ( !(date
.IsValid()) || ( ( m_lowdate
.IsValid() ) ? ( date 
>= m_lowdate 
) : true ) ) 
 574 bool wxCalendarCtrl::SetDateRange(const wxDateTime
& lowerdate 
/* = wxDefaultDateTime */, const wxDateTime
& upperdate 
/* = wxDefaultDateTime */) 
 579         ( !( lowerdate
.IsValid() ) || ( ( upperdate
.IsValid() ) ? ( lowerdate 
<= upperdate 
) : true ) ) && 
 580         ( !( upperdate
.IsValid() ) || ( ( lowerdate
.IsValid() ) ? ( upperdate 
>= lowerdate 
) : true ) ) ) 
 582         m_lowdate 
= lowerdate
; 
 583         m_highdate 
= upperdate
; 
 593 // ---------------------------------------------------------------------------- 
 595 // ---------------------------------------------------------------------------- 
 597 wxDateTime 
wxCalendarCtrl::GetStartDate() const 
 599     wxDateTime::Tm tm 
= m_date
.GetTm(); 
 601     wxDateTime date 
= wxDateTime(1, tm
.mon
, tm
.year
); 
 604     date
.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
 
 605                           ? wxDateTime::Mon 
: wxDateTime::Sun
); 
 607     if ( GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS 
) 
 609         // We want to offset the calendar if we start on the first.. 
 610         if ( date
.GetDay() == 1 ) 
 612             date 
-= wxDateSpan::Week(); 
 619 bool wxCalendarCtrl::IsDateShown(const wxDateTime
& date
) const 
 621     if ( !(GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS
) ) 
 623         return date
.GetMonth() == m_date
.GetMonth(); 
 631 bool wxCalendarCtrl::IsDateInRange(const wxDateTime
& date
) const 
 633     // Check if the given date is in the range specified 
 634     return ( ( ( m_lowdate
.IsValid() ) ? ( date 
>= m_lowdate 
) : true ) 
 635         && ( ( m_highdate
.IsValid() ) ? ( date 
<= m_highdate 
) : true ) ); 
 638 bool wxCalendarCtrl::ChangeYear(wxDateTime
* target
) const 
 642     if ( !(IsDateInRange(*target
)) ) 
 644         if ( target
->GetYear() < m_date
.GetYear() ) 
 646             if ( target
->GetYear() >= GetLowerDateLimit().GetYear() ) 
 648                 *target 
= GetLowerDateLimit(); 
 658             if ( target
->GetYear() <= GetUpperDateLimit().GetYear() ) 
 660                 *target 
= GetUpperDateLimit(); 
 677 bool wxCalendarCtrl::ChangeMonth(wxDateTime
* target
) const 
 681     if ( !(IsDateInRange(*target
)) ) 
 685         if ( target
->GetMonth() < m_date
.GetMonth() ) 
 687             *target 
= GetLowerDateLimit(); 
 691             *target 
= GetUpperDateLimit(); 
 698 size_t wxCalendarCtrl::GetWeek(const wxDateTime
& date
) const 
 700     size_t retval 
= date
.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
 
 701                                    ? wxDateTime::Monday_First
 
 702                                    : wxDateTime::Sunday_First
); 
 704     if ( (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS
) ) 
 706         // we need to offset an extra week if we "start" on the 1st of the month 
 707         wxDateTime::Tm tm 
= date
.GetTm(); 
 709         wxDateTime datetest 
= wxDateTime(1, tm
.mon
, tm
.year
); 
 712         datetest
.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
 
 713                               ? wxDateTime::Mon 
: wxDateTime::Sun
); 
 715         if ( datetest
.GetDay() == 1 ) 
 724 // ---------------------------------------------------------------------------- 
 726 // ---------------------------------------------------------------------------- 
 728 // this is a composite control and it must arrange its parts each time its 
 729 // size or position changes: the combobox and spinctrl are along the top of 
 730 // the available area and the calendar takes up therest of the space 
 732 // the static controls are supposed to be always smaller than combo/spin so we 
 733 // always use the latter for size calculations and position the static to take 
 736 // the constants used for the layout 
 737 #define VERT_MARGIN     5           // distance between combo and calendar 
 739 #define HORZ_MARGIN    5           //                            spin 
 741 #define HORZ_MARGIN    15           //                            spin 
 743 wxSize 
wxCalendarCtrl::DoGetBestSize() const 
 745     // calc the size of the calendar 
 746     ((wxCalendarCtrl 
*)this)->RecalcGeometry(); // const_cast 
 748     wxCoord width 
= 7*m_widthCol
, 
 749             height 
= 7*m_heightRow 
+ m_rowOffset 
+ VERT_MARGIN
; 
 751     if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 753         // the combobox doesn't report its height correctly (it returns the 
 754         // height including the drop down list) so don't use it 
 755         height 
+= m_spinYear
->GetBestSize().y
; 
 758         wxCoord w2 
= m_comboMonth
->GetBestSize().x 
+ HORZ_MARGIN 
+ GetCharWidth()*6; 
 763     if ( !HasFlag(wxBORDER_NONE
) ) 
 765         // the border would clip the last line otherwise 
 770     wxSize 
best(width
, height
); 
 775 void wxCalendarCtrl::DoSetSize(int x
, int y
, 
 776                                int width
, int height
, 
 779     wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
); 
 782 void wxCalendarCtrl::DoMoveWindow(int x
, int y
, int width
, int height
) 
 786     if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 788         wxSize sizeCombo 
= m_comboMonth
->GetSize(); 
 789         wxSize sizeStatic 
= m_staticMonth
->GetSize(); 
 790         wxSize sizeSpin 
= m_spinYear
->GetSize(); 
 791         int dy 
= (sizeCombo
.y 
- sizeStatic
.y
) / 2; 
 792         m_comboMonth
->Move(x
, y
); 
 793         m_staticMonth
->SetSize(x
, y 
+ dy
, sizeCombo
.x
, sizeStatic
.y
); 
 795         int xDiff 
= sizeCombo
.x 
+ HORZ_MARGIN
; 
 797         m_spinYear
->SetSize(x 
+ xDiff
, y
, width 
- xDiff
, sizeCombo
.y
); 
 798         m_staticYear
->SetSize(x 
+ xDiff
, y 
+ dy
, width 
- xDiff
, sizeStatic
.y
); 
 800         yDiff 
= wxMax(sizeSpin
.y
, sizeCombo
.y
) + VERT_MARGIN
; 
 802     else // no controls on the top 
 807     wxControl::DoMoveWindow(x
, y 
+ yDiff
, width
, height 
- yDiff
); 
 810 void wxCalendarCtrl::DoGetPosition(int *x
, int *y
) const 
 812     wxControl::DoGetPosition(x
, y
); 
 814     if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 816         // our real top corner is not in this position 
 819             *y 
-= GetMonthControl()->GetSize().y 
+ VERT_MARGIN
; 
 824 void wxCalendarCtrl::DoGetSize(int *width
, int *height
) const 
 826     wxControl::DoGetSize(width
, height
); 
 828     if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 830         // our real height is bigger 
 831         if ( height 
&& GetMonthControl()) 
 833             *height 
+= GetMonthControl()->GetSize().y 
+ VERT_MARGIN
; 
 838 void wxCalendarCtrl::RecalcGeometry() 
 842     dc
.SetFont(GetFont()); 
 844     // determine the column width (weekday names are not necessarily wider 
 845     // than the numbers (in some languages), so let's not assume that they are) 
 847     for ( int day 
= 10; day 
<= 31; day
++) 
 850         dc
.GetTextExtent(wxString::Format(wxT("%d"), day
), &width
, &m_heightRow
); 
 851         if ( width 
> m_widthCol 
) 
 853             // 1.5 times the width gives nice margins even if the weekday 
 855             m_widthCol 
= width
+width
/2; 
 858     wxDateTime::WeekDay wd
; 
 859     for ( wd 
= wxDateTime::Sun
; wd 
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) ) 
 862         dc
.GetTextExtent(m_weekdays
[wd
], &width
, &m_heightRow
); 
 863         if ( width 
> m_widthCol 
) 
 869     // leave some margins 
 873     m_rowOffset 
= (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ? m_heightRow 
: 0; // conditional in relation to style 
 876 // ---------------------------------------------------------------------------- 
 878 // ---------------------------------------------------------------------------- 
 880 void wxCalendarCtrl::OnPaint(wxPaintEvent
& WXUNUSED(event
)) 
 884     dc
.SetFont(GetFont()); 
 889     wxLogDebug("--- starting to paint, selection: %s, week %u\n", 
 890            m_date
.Format("%a %d-%m-%Y %H:%M:%S").c_str(), 
 895     wxCoord x0 
= (GetSize().x 
- m_widthCol
*7) /2; 
 898     if ( HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
 900         // draw the sequential month-selector 
 902         dc
.SetBackgroundMode(wxTRANSPARENT
); 
 903         dc
.SetTextForeground(*wxBLACK
); 
 904         dc
.SetBrush(wxBrush(m_colHeaderBg
, wxSOLID
)); 
 905         dc
.SetPen(wxPen(m_colHeaderBg
, 1, wxSOLID
)); 
 906         dc
.DrawRectangle(0, y
, GetClientSize().x
, m_heightRow
); 
 908         // Get extent of month-name + year 
 909         wxCoord monthw
, monthh
; 
 910         wxString headertext 
= m_date
.Format(wxT("%B %Y")); 
 911         dc
.GetTextExtent(headertext
, &monthw
, &monthh
); 
 913         // draw month-name centered above weekdays 
 914         wxCoord monthx 
= ((m_widthCol 
* 7) - monthw
) / 2 + x0
; 
 915         wxCoord monthy 
= ((m_heightRow 
- monthh
) / 2) + y
; 
 916         dc
.DrawText(headertext
, monthx
,  monthy
); 
 918         // calculate the "month-arrows" 
 919         wxPoint leftarrow
[3]; 
 920         wxPoint rightarrow
[3]; 
 922         int arrowheight 
= monthh 
/ 2; 
 924         leftarrow
[0] = wxPoint(0, arrowheight 
/ 2); 
 925         leftarrow
[1] = wxPoint(arrowheight 
/ 2, 0); 
 926         leftarrow
[2] = wxPoint(arrowheight 
/ 2, arrowheight 
- 1); 
 928         rightarrow
[0] = wxPoint(0,0); 
 929         rightarrow
[1] = wxPoint(arrowheight 
/ 2, arrowheight 
/ 2); 
 930         rightarrow
[2] = wxPoint(0, arrowheight 
- 1); 
 932         // draw the "month-arrows" 
 934         wxCoord arrowy 
= (m_heightRow 
- arrowheight
) / 2; 
 935         wxCoord larrowx 
= (m_widthCol 
- (arrowheight 
/ 2)) / 2 + x0
; 
 936         wxCoord rarrowx 
= ((m_widthCol 
- (arrowheight 
/ 2)) / 2) + m_widthCol
*6 + x0
; 
 937         m_leftArrowRect 
= m_rightArrowRect 
= wxRect(0,0,0,0); 
 939         if ( AllowMonthChange() ) 
 941             wxDateTime ldpm 
= wxDateTime(1,m_date
.GetMonth(), m_date
.GetYear()) - wxDateSpan::Day(); // last day prev month 
 942             // Check if range permits change 
 943             if ( IsDateInRange(ldpm
) && ( ( ldpm
.GetYear() == m_date
.GetYear() ) ? true : AllowYearChange() ) ) 
 945                 m_leftArrowRect 
= wxRect(larrowx 
- 3, arrowy 
- 3, (arrowheight 
/ 2) + 8, (arrowheight 
+ 6)); 
 946                 dc
.SetBrush(wxBrush(*wxBLACK
, wxSOLID
)); 
 947                 dc
.SetPen(wxPen(*wxBLACK
, 1, wxSOLID
)); 
 948                 dc
.DrawPolygon(3, leftarrow
, larrowx 
, arrowy
, wxWINDING_RULE
); 
 949                 dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
 950                 dc
.DrawRectangle(m_leftArrowRect
); 
 952             wxDateTime fdnm 
= wxDateTime(1,m_date
.GetMonth(), m_date
.GetYear()) + wxDateSpan::Month(); // first day next month 
 953             if ( IsDateInRange(fdnm
) && ( ( fdnm
.GetYear() == m_date
.GetYear() ) ? true : AllowYearChange() ) ) 
 955                 m_rightArrowRect 
= wxRect(rarrowx 
- 4, arrowy 
- 3, (arrowheight 
/ 2) + 8, (arrowheight 
+ 6)); 
 956                 dc
.SetBrush(wxBrush(*wxBLACK
, wxSOLID
)); 
 957                 dc
.SetPen(wxPen(*wxBLACK
, 1, wxSOLID
)); 
 958                 dc
.DrawPolygon(3, rightarrow
, rarrowx 
, arrowy
, wxWINDING_RULE
); 
 959                 dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
 960                 dc
.DrawRectangle(m_rightArrowRect
); 
 967     // first draw the week days 
 968     if ( IsExposed(x0
, y
, 7*m_widthCol
, m_heightRow
) ) 
 971         wxLogDebug("painting the header"); 
 974         dc
.SetBackgroundMode(wxTRANSPARENT
); 
 975         dc
.SetTextForeground(m_colHeaderFg
); 
 976         dc
.SetBrush(wxBrush(m_colHeaderBg
, wxSOLID
)); 
 977         dc
.SetPen(wxPen(m_colHeaderBg
, 1, wxSOLID
)); 
 978         dc
.DrawRectangle(0, y
, GetClientSize().x
, m_heightRow
); 
 980         bool startOnMonday 
= (GetWindowStyle() & wxCAL_MONDAY_FIRST
) != 0; 
 981         for ( size_t wd 
= 0; wd 
< 7; wd
++ ) 
 985                 n 
= wd 
== 6 ? 0 : wd 
+ 1; 
 989             dc
.GetTextExtent(m_weekdays
[n
], &dayw
, &dayh
); 
 990             dc
.DrawText(m_weekdays
[n
], x0 
+ (wd
*m_widthCol
) + ((m_widthCol
- dayw
) / 2), y
); // center the day-name 
 994     // then the calendar itself 
 995     dc
.SetTextForeground(*wxBLACK
); 
 996     //dc.SetFont(*wxNORMAL_FONT); 
 999     wxDateTime date 
= GetStartDate(); 
1002     wxLogDebug("starting calendar from %s\n", 
1003             date
.Format("%a %d-%m-%Y %H:%M:%S").c_str()); 
1006     dc
.SetBackgroundMode(wxSOLID
); 
1007     for ( size_t nWeek 
= 1; nWeek 
<= 6; nWeek
++, y 
+= m_heightRow 
) 
1009         // if the update region doesn't intersect this row, don't paint it 
1010         if ( !IsExposed(0, y
, 7*m_widthCol
, m_heightRow 
- 1) ) 
1012             date 
+= wxDateSpan::Week(); 
1018         wxLogDebug("painting week %d at y = %d\n", nWeek
, y
); 
1021         for ( size_t wd 
= 0; wd 
< 7; wd
++ ) 
1023             if ( IsDateShown(date
) ) 
1025                 // don't use wxDate::Format() which prepends 0s 
1026                 unsigned int day 
= date
.GetDay(); 
1027                 wxString dayStr 
= wxString::Format(_T("%u"), day
); 
1029                 dc
.GetTextExtent(dayStr
, &width
, (wxCoord 
*)NULL
); 
1031                 bool changedColours 
= false, 
1032                      changedFont 
= false; 
1035                 wxCalendarDateAttr 
*attr 
= NULL
; 
1037                 if ( date
.GetMonth() != m_date
.GetMonth() || !IsDateInRange(date
) ) 
1039                     // surrounding week or out-of-range 
1041                     dc
.SetTextForeground(*wxLIGHT_GREY
); 
1042                     changedColours 
= true; 
1046                     isSel 
= date
.IsSameDate(m_date
); 
1047                     attr 
= m_attrs
[day 
- 1]; 
1051                         dc
.SetTextForeground(m_colHighlightFg
); 
1052                         dc
.SetTextBackground(m_colHighlightBg
); 
1054                         changedColours 
= true; 
1058                         wxColour colFg
, colBg
; 
1060                         if ( attr
->IsHoliday() ) 
1062                             colFg 
= m_colHolidayFg
; 
1063                             colBg 
= m_colHolidayBg
; 
1067                             colFg 
= attr
->GetTextColour(); 
1068                             colBg 
= attr
->GetBackgroundColour(); 
1073                             dc
.SetTextForeground(colFg
); 
1074                             changedColours 
= true; 
1079                             dc
.SetTextBackground(colBg
); 
1080                             changedColours 
= true; 
1083                         if ( attr
->HasFont() ) 
1085                             dc
.SetFont(attr
->GetFont()); 
1091                 wxCoord x 
= wd
*m_widthCol 
+ (m_widthCol 
- width
) / 2 + x0
; 
1092                 dc
.DrawText(dayStr
, x
, y 
+ 1); 
1094                 if ( !isSel 
&& attr 
&& attr
->HasBorder() ) 
1097                     if ( attr
->HasBorderColour() ) 
1099                         colBorder 
= attr
->GetBorderColour(); 
1103                         colBorder 
= GetForegroundColour(); 
1106                     wxPen 
pen(colBorder
, 1, wxSOLID
); 
1108                     dc
.SetBrush(*wxTRANSPARENT_BRUSH
); 
1110                     switch ( attr
->GetBorder() ) 
1112                         case wxCAL_BORDER_SQUARE
: 
1113                             dc
.DrawRectangle(x 
- 2, y
, 
1114                                              width 
+ 4, m_heightRow
); 
1117                         case wxCAL_BORDER_ROUND
: 
1118                             dc
.DrawEllipse(x 
- 2, y
, 
1119                                            width 
+ 4, m_heightRow
); 
1123                             wxFAIL_MSG(_T("unknown border type")); 
1127                 if ( changedColours 
) 
1129                     dc
.SetTextForeground(GetForegroundColour()); 
1130                     dc
.SetTextBackground(GetBackgroundColour()); 
1135                     dc
.SetFont(GetFont()); 
1138             //else: just don't draw it 
1140             date 
+= wxDateSpan::Day(); 
1144     // Greying out out-of-range background 
1145     bool showSurrounding 
= (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS
) != 0; 
1147     date 
= ( showSurrounding 
) ? GetStartDate() : wxDateTime(1, m_date
.GetMonth(), m_date
.GetYear()); 
1148     if ( !IsDateInRange(date
) ) 
1150         wxDateTime firstOOR 
= GetLowerDateLimit() - wxDateSpan::Day(); // first out-of-range 
1152         wxBrush oorbrush 
= *wxLIGHT_GREY_BRUSH
; 
1153         oorbrush
.SetStyle(wxFDIAGONAL_HATCH
); 
1155         HighlightRange(&dc
, date
, firstOOR
, wxTRANSPARENT_PEN
, &oorbrush
); 
1158     date 
= ( showSurrounding 
) ? GetStartDate() + wxDateSpan::Weeks(6) - wxDateSpan::Day() : wxDateTime().SetToLastMonthDay(m_date
.GetMonth(), m_date
.GetYear()); 
1159     if ( !IsDateInRange(date
) ) 
1161         wxDateTime firstOOR 
= GetUpperDateLimit() + wxDateSpan::Day(); // first out-of-range 
1163         wxBrush oorbrush 
= *wxLIGHT_GREY_BRUSH
; 
1164         oorbrush
.SetStyle(wxFDIAGONAL_HATCH
); 
1166         HighlightRange(&dc
, firstOOR
, date
, wxTRANSPARENT_PEN
, &oorbrush
); 
1170     wxLogDebug("+++ finished painting"); 
1174 void wxCalendarCtrl::RefreshDate(const wxDateTime
& date
) 
1180     // always refresh the whole row at once because our OnPaint() will draw 
1181     // the whole row anyhow - and this allows the small optimisation in 
1182     // OnClick() below to work 
1185     rect
.y 
= (m_heightRow 
* GetWeek(date
)) + m_rowOffset
; 
1187     rect
.width 
= 7*m_widthCol
; 
1188     rect
.height 
= m_heightRow
; 
1191     // VZ: for some reason, the selected date seems to occupy more space under 
1192     //     MSW - this is probably some bug in the font size calculations, but I 
1193     //     don't know where exactly. This fix is ugly and leads to more 
1194     //     refreshes than really needed, but without it the selected days 
1195     //     leaves even more ugly underscores on screen. 
1200     wxLogDebug("*** refreshing week %d at (%d, %d)-(%d, %d)\n", 
1203            rect
.x 
+ rect
.width
, rect
.y 
+ rect
.height
); 
1206     Refresh(true, &rect
); 
1209 void wxCalendarCtrl::HighlightRange(wxPaintDC
* pDC
, const wxDateTime
& fromdate
, const wxDateTime
& todate
, wxPen
* pPen
, wxBrush
* pBrush
) 
1211     // Highlights the given range using pen and brush 
1212     // Does nothing if todate < fromdate 
1216     wxLogDebug("+++ HighlightRange: (%s) - (%s) +++", fromdate
.Format("%d %m %Y"), todate
.Format("%d %m %Y")); 
1219     if ( todate 
>= fromdate 
) 
1226         // implicit: both dates must be currently shown - checked by GetDateCoord 
1227         if ( GetDateCoord(fromdate
, &fd
, &fw
) && GetDateCoord(todate
, &td
, &tw
) ) 
1230             wxLogDebug("Highlight range: (%i, %i) - (%i, %i)", fd
, fw
, td
, tw
); 
1232             if ( ( (tw 
- fw
) == 1 ) && ( td 
< fd 
) ) 
1234                 // special case: interval 7 days or less not in same week 
1235                 // split in two separate intervals 
1236                 wxDateTime tfd 
= fromdate 
+ wxDateSpan::Days(7-fd
); 
1237                 wxDateTime ftd 
= tfd 
+ wxDateSpan::Day(); 
1239                 wxLogDebug("Highlight: Separate segments"); 
1242                 HighlightRange(pDC
, fromdate
, tfd
, pPen
, pBrush
); 
1243                 HighlightRange(pDC
, ftd
, todate
, pPen
, pBrush
); 
1248                 wxPoint corners
[8]; // potentially 8 corners in polygon 
1252                     // simple case: same week 
1254                     corners
[0] = wxPoint((fd 
- 1) * m_widthCol
, (fw 
* m_heightRow
) + m_rowOffset
); 
1255                     corners
[1] = wxPoint((fd 
- 1) * m_widthCol
, ((fw 
+ 1 ) * m_heightRow
) + m_rowOffset
); 
1256                     corners
[2] = wxPoint(td 
* m_widthCol
, ((tw 
+ 1) * m_heightRow
) + m_rowOffset
); 
1257                     corners
[3] = wxPoint(td 
* m_widthCol
, (tw 
* m_heightRow
) + m_rowOffset
); 
1262                     // "complex" polygon 
1263                     corners
[cidx
] = wxPoint((fd 
- 1) * m_widthCol
, (fw 
* m_heightRow
) + m_rowOffset
); cidx
++; 
1267                         corners
[cidx
] = wxPoint((fd 
- 1) * m_widthCol
, ((fw 
+ 1) * m_heightRow
) + m_rowOffset
); cidx
++; 
1268                         corners
[cidx
] = wxPoint(0, ((fw 
+ 1) * m_heightRow
) + m_rowOffset
); cidx
++; 
1271                     corners
[cidx
] = wxPoint(0, ((tw 
+ 1) * m_heightRow
) + m_rowOffset
); cidx
++; 
1272                     corners
[cidx
] = wxPoint(td 
* m_widthCol
, ((tw 
+ 1) * m_heightRow
) + m_rowOffset
); cidx
++; 
1276                         corners
[cidx
] = wxPoint(td 
* m_widthCol
, (tw 
* m_heightRow
) + m_rowOffset
); cidx
++; 
1277                         corners
[cidx
] = wxPoint(7 * m_widthCol
, (tw 
* m_heightRow
) + m_rowOffset
); cidx
++; 
1280                     corners
[cidx
] = wxPoint(7 * m_widthCol
, (fw 
* m_heightRow
) + m_rowOffset
); cidx
++; 
1286                 pDC
->SetBrush(*pBrush
); 
1288                 pDC
->DrawPolygon(numpoints
, corners
); 
1294     wxLogDebug("--- HighlightRange ---"); 
1298 bool wxCalendarCtrl::GetDateCoord(const wxDateTime
& date
, int *day
, int *week
) const 
1303     wxLogDebug("+++ GetDateCoord: (%s) +++", date
.Format("%d %m %Y")); 
1306     if ( IsDateShown(date
) ) 
1308         bool startOnMonday 
= ( GetWindowStyle() & wxCAL_MONDAY_FIRST 
) != 0; 
1311         *day 
= date
.GetWeekDay(); 
1313         if ( *day 
== 0 ) // sunday 
1315             *day 
= ( startOnMonday 
) ? 7 : 1; 
1319             *day 
+= ( startOnMonday 
) ? 0 : 1; 
1322         int targetmonth 
= date
.GetMonth() + (12 * date
.GetYear()); 
1323         int thismonth 
= m_date
.GetMonth() + (12 * m_date
.GetYear()); 
1326         if ( targetmonth 
== thismonth 
) 
1328             *week 
= GetWeek(date
); 
1332             if ( targetmonth 
< thismonth 
) 
1334                 *week 
= 1; // trivial 
1336             else // targetmonth > thismonth 
1342                 // get the datecoord of the last day in the month currently shown 
1344                 wxLogDebug("     +++ LDOM +++"); 
1346                 GetDateCoord(ldcm
.SetToLastMonthDay(m_date
.GetMonth(), m_date
.GetYear()), &lastday
, &lastweek
); 
1348                 wxLogDebug("     --- LDOM ---"); 
1351                 wxTimeSpan span 
= date 
- ldcm
; 
1353                 int daysfromlast 
= span
.GetDays(); 
1355                 wxLogDebug("daysfromlast: %i", daysfromlast
); 
1357                 if ( daysfromlast 
+ lastday 
> 7 ) // past week boundary 
1359                     int wholeweeks 
= (daysfromlast 
/ 7); 
1360                     *week 
= wholeweeks 
+ lastweek
; 
1361                     if ( (daysfromlast 
- (7 * wholeweeks
) + lastday
) > 7 ) 
1381     wxLogDebug("--- GetDateCoord: (%s) = (%i, %i) ---", date
.Format("%d %m %Y"), *day
, *week
); 
1387 // ---------------------------------------------------------------------------- 
1389 // ---------------------------------------------------------------------------- 
1391 void wxCalendarCtrl::OnDClick(wxMouseEvent
& event
) 
1393     if ( HitTest(event
.GetPosition()) != wxCAL_HITTEST_DAY 
) 
1399         GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED
); 
1403 void wxCalendarCtrl::OnClick(wxMouseEvent
& event
) 
1406     wxDateTime::WeekDay wday
; 
1407     switch ( HitTest(event
.GetPosition(), &date
, &wday
) ) 
1409         case wxCAL_HITTEST_DAY
: 
1410             if ( IsDateInRange(date
) ) 
1414                 GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED
, 
1415                                wxEVT_CALENDAR_SEL_CHANGED
); 
1419         case wxCAL_HITTEST_HEADER
: 
1421                 wxCalendarEvent 
event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED
); 
1422                 event
.m_wday 
= wday
; 
1423                 (void)GetEventHandler()->ProcessEvent(event
); 
1427         case wxCAL_HITTEST_DECMONTH
: 
1428         case wxCAL_HITTEST_INCMONTH
: 
1429         case wxCAL_HITTEST_SURROUNDING_WEEK
: 
1430             SetDateAndNotify(date
); // we probably only want to refresh the control. No notification.. (maybe as an option?) 
1434             wxFAIL_MSG(_T("unknown hittest code")); 
1437         case wxCAL_HITTEST_NOWHERE
: 
1443 wxCalendarHitTestResult 
wxCalendarCtrl::HitTest(const wxPoint
& pos
, 
1445                                                 wxDateTime::WeekDay 
*wd
) 
1451 /////////////////////////////////////////////////////////////////////////////////////////////////////// 
1452     if ( (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION
) ) 
1456         // we need to find out if the hit is on left arrow, on month or on right arrow 
1458         if ( wxRegion(m_leftArrowRect
).Contains(pos
) == wxInRegion 
) 
1462                 if ( IsDateInRange(m_date 
- wxDateSpan::Month()) ) 
1464                     *date 
= m_date 
- wxDateSpan::Month(); 
1468                     *date 
= GetLowerDateLimit(); 
1472             return wxCAL_HITTEST_DECMONTH
; 
1475         if ( wxRegion(m_rightArrowRect
).Contains(pos
) == wxInRegion 
) 
1479                 if ( IsDateInRange(m_date 
+ wxDateSpan::Month()) ) 
1481                     *date 
= m_date 
+ wxDateSpan::Month(); 
1485                     *date 
= GetUpperDateLimit(); 
1489             return wxCAL_HITTEST_INCMONTH
; 
1494 /////////////////////////////////////////////////////////////////////////////////////////////////////// 
1496     int wday 
= pos
.x 
/ m_widthCol
; 
1497 //    if ( y < m_heightRow ) 
1498     if ( y 
< (m_heightRow 
+ m_rowOffset
) ) 
1500         if ( y 
> m_rowOffset 
) 
1504                 if ( GetWindowStyle() & wxCAL_MONDAY_FIRST 
) 
1506                     wday 
= wday 
== 6 ? 0 : wday 
+ 1; 
1509                 *wd 
= (wxDateTime::WeekDay
)wday
; 
1512             return wxCAL_HITTEST_HEADER
; 
1516             return wxCAL_HITTEST_NOWHERE
; 
1520 //    int week = (y - m_heightRow) / m_heightRow; 
1521     int week 
= (y 
- (m_heightRow 
+ m_rowOffset
)) / m_heightRow
; 
1522     if ( week 
>= 6 || wday 
>= 7 ) 
1524         return wxCAL_HITTEST_NOWHERE
; 
1527     wxDateTime dt 
= GetStartDate() + wxDateSpan::Days(7*week 
+ wday
); 
1529     if ( IsDateShown(dt
) ) 
1534         if ( dt
.GetMonth() == m_date
.GetMonth() ) 
1537             return wxCAL_HITTEST_DAY
; 
1541             return wxCAL_HITTEST_SURROUNDING_WEEK
; 
1546         return wxCAL_HITTEST_NOWHERE
; 
1550 // ---------------------------------------------------------------------------- 
1551 // subcontrols events handling 
1552 // ---------------------------------------------------------------------------- 
1554 void wxCalendarCtrl::OnMonthChange(wxCommandEvent
& event
) 
1556     wxDateTime::Tm tm 
= m_date
.GetTm(); 
1558     wxDateTime::Month mon 
= (wxDateTime::Month
)event
.GetInt(); 
1559     if ( tm
.mday 
> wxDateTime::GetNumberOfDays(mon
, tm
.year
) ) 
1561         tm
.mday 
= wxDateTime::GetNumberOfDays(mon
, tm
.year
); 
1564     wxDateTime target 
= wxDateTime(tm
.mday
, mon
, tm
.year
); 
1566     ChangeMonth(&target
); 
1567     SetDateAndNotify(target
); 
1570 void wxCalendarCtrl::OnYearChange(wxCommandEvent
& event
) 
1572     int year 
= (int)event
.GetInt(); 
1573     if ( year 
== INT_MIN 
) 
1575         // invalid year in the spin control, ignore it 
1579     wxDateTime::Tm tm 
= m_date
.GetTm(); 
1581     if ( tm
.mday 
> wxDateTime::GetNumberOfDays(tm
.mon
, year
) ) 
1583         tm
.mday 
= wxDateTime::GetNumberOfDays(tm
.mon
, year
); 
1586     wxDateTime target 
= wxDateTime(tm
.mday
, tm
.mon
, year
); 
1588     if ( ChangeYear(&target
) ) 
1590         SetDateAndNotify(target
); 
1594         // In this case we don't want to change the date. That would put us 
1595         // inside the same year but a strange number of months forward/back.. 
1596         m_spinYear
->SetValue(target
.GetYear()); 
1600 void wxCalendarCtrl::OnYearTextChange(wxCommandEvent
& event
) 
1602     SetUserChangedYear(); 
1603     OnYearChange(event
); 
1606 // ---------------------------------------------------------------------------- 
1607 // keyboard interface 
1608 // ---------------------------------------------------------------------------- 
1610 void wxCalendarCtrl::OnChar(wxKeyEvent
& event
) 
1613     switch ( event
.GetKeyCode() ) 
1617             target 
= m_date 
+ wxDateSpan::Year(); 
1618             if ( ChangeYear(&target
) ) 
1620                 SetDateAndNotify(target
); 
1626             target 
= m_date 
- wxDateSpan::Year(); 
1627             if ( ChangeYear(&target
) ) 
1629                 SetDateAndNotify(target
); 
1634             target 
= m_date 
- wxDateSpan::Month(); 
1635             ChangeMonth(&target
); 
1636             SetDateAndNotify(target
); // always 
1640             target 
= m_date 
+ wxDateSpan::Month(); 
1641             ChangeMonth(&target
); 
1642             SetDateAndNotify(target
); // always 
1646             if ( event
.ControlDown() ) 
1648                 target 
= wxDateTime(m_date
).SetToNextWeekDay( 
1649                                  GetWindowStyle() & wxCAL_MONDAY_FIRST
 
1650                                  ? wxDateTime::Sun 
: wxDateTime::Sat
); 
1651                 if ( !IsDateInRange(target
) ) 
1653                     target 
= GetUpperDateLimit(); 
1655                 SetDateAndNotify(target
); 
1658                 SetDateAndNotify(m_date 
+ wxDateSpan::Day()); 
1662             if ( event
.ControlDown() ) 
1664                 target 
= wxDateTime(m_date
).SetToPrevWeekDay( 
1665                                  GetWindowStyle() & wxCAL_MONDAY_FIRST
 
1666                                  ? wxDateTime::Mon 
: wxDateTime::Sun
); 
1667                 if ( !IsDateInRange(target
) ) 
1669                     target 
= GetLowerDateLimit(); 
1671                 SetDateAndNotify(target
); 
1674                 SetDateAndNotify(m_date 
- wxDateSpan::Day()); 
1678             SetDateAndNotify(m_date 
- wxDateSpan::Week()); 
1682             SetDateAndNotify(m_date 
+ wxDateSpan::Week()); 
1686             if ( event
.ControlDown() ) 
1687                 SetDateAndNotify(wxDateTime::Today()); 
1689                 SetDateAndNotify(wxDateTime(1, m_date
.GetMonth(), m_date
.GetYear())); 
1693             SetDateAndNotify(wxDateTime(m_date
).SetToLastMonthDay()); 
1697             GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED
); 
1705 // ---------------------------------------------------------------------------- 
1706 // holidays handling 
1707 // ---------------------------------------------------------------------------- 
1709 void wxCalendarCtrl::EnableHolidayDisplay(bool display
) 
1711     long style 
= GetWindowStyle(); 
1713         style 
|= wxCAL_SHOW_HOLIDAYS
; 
1715         style 
&= ~wxCAL_SHOW_HOLIDAYS
; 
1717     SetWindowStyle(style
); 
1722         ResetHolidayAttrs(); 
1727 void wxCalendarCtrl::SetHolidayAttrs() 
1729     if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS 
) 
1731         ResetHolidayAttrs(); 
1733         wxDateTime::Tm tm 
= m_date
.GetTm(); 
1734         wxDateTime 
dtStart(1, tm
.mon
, tm
.year
), 
1735                    dtEnd 
= dtStart
.GetLastMonthDay(); 
1737         wxDateTimeArray hol
; 
1738         wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart
, dtEnd
, hol
); 
1740         size_t count 
= hol
.GetCount(); 
1741         for ( size_t n 
= 0; n 
< count
; n
++ ) 
1743             SetHoliday(hol
[n
].GetDay()); 
1748 void wxCalendarCtrl::SetHoliday(size_t day
) 
1750     wxCHECK_RET( day 
> 0 && day 
< 32, _T("invalid day in SetHoliday") ); 
1752     wxCalendarDateAttr 
*attr 
= GetAttr(day
); 
1755         attr 
= new wxCalendarDateAttr
; 
1758     attr
->SetHoliday(true); 
1760     // can't use SetAttr() because it would delete this pointer 
1761     m_attrs
[day 
- 1] = attr
; 
1764 void wxCalendarCtrl::ResetHolidayAttrs() 
1766     for ( size_t day 
= 0; day 
< 31; day
++ ) 
1770             m_attrs
[day
]->SetHoliday(false); 
1778 wxCalendarCtrl::GetClassDefaultAttributes(wxWindowVariant variant
) 
1780     // Use the same color scheme as wxListBox 
1781     return wxListBox::GetClassDefaultAttributes(variant
); 
1784 #endif // wxUSE_CALENDARCTRL