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