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