1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: generic/calctrl.cpp
3 // Purpose: implementation fo the generic wxCalendarCtrl
4 // Author: Vadim Zeitlin
8 // Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license
10 ///////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
21 #pragma implementation "calctrl.h"
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
37 #include "wx/calctrl.h"
39 // ----------------------------------------------------------------------------
41 // ----------------------------------------------------------------------------
43 BEGIN_EVENT_TABLE(wxCalendarCtrl
, wxControl
)
44 EVT_PAINT(wxCalendarCtrl::OnPaint
)
46 EVT_CHAR(wxCalendarCtrl::OnChar
)
48 EVT_LEFT_DOWN(wxCalendarCtrl::OnClick
)
50 EVT_COMBOBOX(-1, wxCalendarCtrl::OnMonthChange
)
51 EVT_SPINCTRL(-1, wxCalendarCtrl::OnYearChange
)
54 IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl
, wxControl
)
56 // ============================================================================
58 // ============================================================================
60 // ----------------------------------------------------------------------------
62 // ----------------------------------------------------------------------------
64 void wxCalendarCtrl::Init()
72 wxDateTime::WeekDay wd
;
73 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
75 m_weekdays
[wd
] = wxDateTime::GetWeekDayName(wd
, wxDateTime::Name_Abbr
);
79 bool wxCalendarCtrl::Create(wxWindow
*parent
,
81 const wxDateTime
& date
,
87 m_date
= date
.IsValid() ? date
: wxDateTime::Today();
89 wxString monthNames
[12];
91 for ( m
= wxDateTime::Jan
; m
< wxDateTime::Inv_Month
; wxNextMonth(m
) )
93 monthNames
[m
] = wxDateTime::GetMonthName(m
);
96 m_comboMonth
= new wxComboBox(parent
, -1,
97 monthNames
[m_date
.GetMonth()],
100 WXSIZEOF(monthNames
), monthNames
,
103 m_spinYear
= new wxSpinCtrl(parent
, -1,
104 m_date
.Format(_T("%Y")),
108 -4300, 10000, m_date
.GetYear());
110 // we want to process the events from these controls
111 m_comboMonth
->PushEventHandler(this);
112 m_spinYear
->PushEventHandler(this);
115 if ( size
.x
== -1 || size
.y
== -1 )
117 sizeReal
= DoGetBestSize();
130 SetBackgroundColour(*wxWHITE
);
131 SetFont(*wxSWISS_FONT
);
136 // ----------------------------------------------------------------------------
138 // ----------------------------------------------------------------------------
140 void wxCalendarCtrl::SetDate(const wxDateTime
& date
)
142 if ( m_date
.GetMonth() == date
.GetMonth() &&
143 m_date
.GetYear() == date
.GetYear() )
145 // just change the day
153 // update the controls
154 m_comboMonth
->SetSelection(m_date
.GetMonth());
155 m_spinYear
->SetValue(m_date
.Format(_T("%Y")));
157 // update the calendar
162 void wxCalendarCtrl::ChangeDay(const wxDateTime
& date
)
164 if ( m_date
!= date
)
166 // we need to refresh the row containing the old date and the one
167 // containing the new one
168 wxDateTime dateOld
= m_date
;
171 RefreshDate(dateOld
);
173 // if the date is in the same row, it was already drawn correctly
174 if ( GetWeek(m_date
) != GetWeek(dateOld
) )
181 void wxCalendarCtrl::SetDateAndNotify(const wxDateTime
& date
)
183 wxDateTime::Tm tm1
= m_date
.GetTm(),
187 if ( tm1
.year
!= tm2
.year
)
188 type
= wxEVT_CALENDAR_YEAR_CHANGED
;
189 else if ( tm1
.mon
!= tm2
.mon
)
190 type
= wxEVT_CALENDAR_MONTH_CHANGED
;
191 else if ( tm1
.mday
!= tm2
.mday
)
192 type
= wxEVT_CALENDAR_DAY_CHANGED
;
201 // ----------------------------------------------------------------------------
203 // ----------------------------------------------------------------------------
205 wxDateTime
wxCalendarCtrl::GetStartDate() const
207 wxDateTime::Tm tm
= m_date
.GetTm();
209 wxDateTime date
= wxDateTime(1, tm
.mon
, tm
.year
);
210 if ( date
.GetWeekDay() != wxDateTime::Sun
)
212 date
.SetToPrevWeekDay(wxDateTime::Sun
);
214 // be sure to do it or it might gain 1 hour if DST changed in between
217 //else: we already have it
222 bool wxCalendarCtrl::IsDateShown(const wxDateTime
& date
) const
224 return date
.GetMonth() == m_date
.GetMonth();
227 size_t wxCalendarCtrl::GetWeek(const wxDateTime
& date
) const
229 return date
.GetWeekOfMonth(wxDateTime::Sunday_First
);
232 // ----------------------------------------------------------------------------
234 // ----------------------------------------------------------------------------
236 // this is a composite control and it must arrange its parts each time its
237 // size or position changes: the combobox and spinctrl are along the top of
238 // the available area and the calendar takes up therest of the space
240 // the constants used for the layout
241 #define VERT_MARGIN 5 // distance between combo and calendar
242 #define HORZ_MARGIN 15 // spin
244 wxSize
wxCalendarCtrl::DoGetBestSize() const
246 // calc the size of the calendar
247 ((wxCalendarCtrl
*)this)->RecalcGeometry(); // const_cast
249 wxCoord width
= 7*m_widthCol
,
250 height
= 7*m_heightRow
;
252 wxSize sizeCombo
= m_comboMonth
->GetBestSize(),
253 sizeSpin
= m_spinYear
->GetBestSize();
255 height
+= VERT_MARGIN
+ wxMax(sizeCombo
.y
, sizeSpin
.y
);
257 return wxSize(width
, height
);
260 void wxCalendarCtrl::DoSetSize(int x
, int y
,
261 int width
, int height
,
264 wxControl::DoSetSize(x
, y
, width
, height
, sizeFlags
);
267 void wxCalendarCtrl::DoMoveWindow(int x
, int y
, int width
, int height
)
269 wxSize sizeCombo
= m_comboMonth
->GetSize();
270 m_comboMonth
->Move(x
, y
);
272 int xDiff
= sizeCombo
.x
+ HORZ_MARGIN
;
273 m_spinYear
->SetSize(x
+ xDiff
, y
, width
- xDiff
, -1);
275 wxSize sizeSpin
= m_spinYear
->GetSize();
276 int yDiff
= wxMax(sizeSpin
.y
, sizeCombo
.y
) + VERT_MARGIN
;
278 wxControl::DoMoveWindow(x
, y
+ yDiff
, width
, height
- yDiff
);
281 void wxCalendarCtrl::RecalcGeometry()
283 if ( m_widthCol
!= 0 )
290 // determine the column width (we assume that the weekday names are always
291 // wider (in any language) than the numbers)
293 wxDateTime::WeekDay wd
;
294 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
297 dc
.GetTextExtent(m_weekdays
[wd
], &width
, &m_heightRow
);
298 if ( width
> m_widthCol
)
304 // leave some margins
309 // ----------------------------------------------------------------------------
311 // ----------------------------------------------------------------------------
313 void wxCalendarCtrl::OnPaint(wxPaintEvent
& event
)
317 wxDateTime::WeekDay wd
;
323 printf("--- starting to paint, selection: %s, week %u\n",
324 m_date
.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
327 // first draw the week days
328 if ( IsExposed(0, 0, 7*m_widthCol
, m_heightRow
) )
330 puts("painting the header");
332 dc
.SetTextForeground(*wxBLUE
);
333 dc
.SetBrush(wxBrush(*wxLIGHT_GREY
, wxSOLID
));
334 dc
.SetBackgroundMode(wxTRANSPARENT
);
335 dc
.SetPen(*wxLIGHT_GREY_PEN
);
336 dc
.DrawRectangle(0, 0, 7*m_widthCol
, m_heightRow
);
337 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
339 dc
.DrawText(m_weekdays
[wd
], wd
*m_widthCol
+ 1, 0);
343 // then the calendar itself
344 dc
.SetTextForeground(*wxBLACK
);
345 //dc.SetFont(*wxNORMAL_FONT);
347 wxCoord y
= m_heightRow
;
349 wxDateTime date
= GetStartDate();
350 printf("starting calendar from %s\n",
351 date
.Format("%a %d-%m-%Y %H:%M:%S").c_str());
353 dc
.SetBackgroundMode(wxSOLID
);
354 for ( size_t nWeek
= 1; nWeek
<= 6; nWeek
++, y
+= m_heightRow
)
356 // if the update region doesn't intersect this row, don't paint it
357 if ( !IsExposed(0, y
, 7*m_widthCol
, m_heightRow
- 1) )
359 date
+= wxDateSpan::Week();
364 printf("painting week %d at y = %d\n", nWeek
, y
);
366 for ( wd
= wxDateTime::Sun
; wd
< wxDateTime::Inv_WeekDay
; wxNextWDay(wd
) )
368 if ( IsDateShown(date
) )
370 wxString day
= wxString::Format(_T("%u"), date
.GetDay());
372 dc
.GetTextExtent(day
, &width
, (wxCoord
*)NULL
);
374 bool isSel
= m_date
== date
;
377 dc
.SetTextForeground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT
));
378 dc
.SetTextBackground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT
));
381 dc
.DrawText(day
, wd
*m_widthCol
+ (m_widthCol
- width
) / 2, y
);
385 dc
.SetTextForeground(m_foregroundColour
);
386 dc
.SetTextBackground(m_backgroundColour
);
389 //else: just don't draw it
391 date
+= wxDateSpan::Day();
395 puts("+++ finished painting");
398 void wxCalendarCtrl::RefreshDate(const wxDateTime
& date
)
404 // always refresh the whole row at once because our OnPaint() will draw
405 // the whole row anyhow - and this allows the small optimisation in
406 // OnClick() below to work
408 rect
.y
= m_heightRow
* GetWeek(date
);
409 rect
.width
= 7*m_widthCol
;
410 rect
.height
= m_heightRow
;
412 printf("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
415 rect
.x
+ rect
.width
, rect
.y
+ rect
.height
);
417 Refresh(TRUE
, &rect
);
420 // ----------------------------------------------------------------------------
422 // ----------------------------------------------------------------------------
424 void wxCalendarCtrl::OnClick(wxMouseEvent
& event
)
429 if ( !HitTest(event
.GetPosition(), &date
) )
437 GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED
);
441 bool wxCalendarCtrl::HitTest(const wxPoint
& pos
, wxDateTime
*date
)
446 if ( y
< m_heightRow
)
450 int week
= y
/ m_heightRow
,
451 wday
= pos
.x
/ m_widthCol
;
453 if ( week
>= 6 || wday
>= 7 )
456 wxCHECK_MSG( date
, FALSE
, _T("bad pointer in wxCalendarCtrl::HitTest") );
458 *date
= GetStartDate();
459 *date
+= wxDateSpan::Days(7*week
+ wday
);
461 return IsDateShown(*date
);
464 // ----------------------------------------------------------------------------
465 // subcontrols events handling
466 // ----------------------------------------------------------------------------
468 void wxCalendarCtrl::OnMonthChange(wxCommandEvent
& event
)
470 wxDateTime::Tm tm
= m_date
.GetTm();
472 wxDateTime::Month mon
= (wxDateTime::Month
)event
.GetInt();
473 if ( tm
.mday
> wxDateTime::GetNumberOfDays(mon
, tm
.year
) )
475 tm
.mday
= wxDateTime::GetNumberOfDays(mon
, tm
.year
);
478 SetDate(wxDateTime(tm
.mday
, mon
, tm
.year
));
480 GenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED
);
483 void wxCalendarCtrl::OnYearChange(wxSpinEvent
& event
)
485 wxDateTime::Tm tm
= m_date
.GetTm();
487 int year
= event
.GetInt();
488 if ( tm
.mday
> wxDateTime::GetNumberOfDays(tm
.mon
, year
) )
490 tm
.mday
= wxDateTime::GetNumberOfDays(tm
.mon
, year
);
493 SetDate(wxDateTime(tm
.mday
, tm
.mon
, year
));
495 GenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED
);
498 // ----------------------------------------------------------------------------
499 // keyboard interface
500 // ----------------------------------------------------------------------------
502 void wxCalendarCtrl::OnChar(wxKeyEvent
& event
)
504 switch ( event
.KeyCode() )
508 SetDateAndNotify(m_date
+ wxDateSpan::Year());
513 SetDateAndNotify(m_date
- wxDateSpan::Year());
517 SetDateAndNotify(m_date
+ wxDateSpan::Year());
521 SetDateAndNotify(m_date
- wxDateSpan::Year());
525 SetDateAndNotify(m_date
+ wxDateSpan::Day());
529 SetDateAndNotify(m_date
- wxDateSpan::Day());
533 SetDateAndNotify(m_date
- wxDateSpan::Week());
537 SetDateAndNotify(m_date
+ wxDateSpan::Week());
541 SetDateAndNotify(wxDateTime::Today());
549 // ----------------------------------------------------------------------------
551 // ----------------------------------------------------------------------------
553 void wxCalendarCtrl::GenerateEvent(wxEventType type
)
555 // we're called for a change in some particular date field but we always
556 // also generate a generic "changed" event
557 wxCalendarEvent
event(this, type
);
558 wxCalendarEvent
event2(this, wxEVT_CALENDAR_SEL_CHANGED
);
560 (void)GetEventHandler()->ProcessEvent(event
);
561 (void)GetEventHandler()->ProcessEvent(event2
);
564 wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl
*cal
, wxEventType type
)
565 : wxCommandEvent(type
, cal
->GetId())
567 m_date
= cal
->GetDate();