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