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