home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2233.zip / wxOS2-2_3_3.zip / wxWindows-2.3.3 / src / generic / calctrl.cpp < prev    next >
C/C++ Source or Header  |  2002-07-01  |  52KB  |  1,739 lines

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