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