]> git.saurik.com Git - wxWidgets.git/blame - src/generic/calctrl.cpp
Typo in doc, wxCHECK2_MSG replaced with wxCHECK_MSG
[wxWidgets.git] / src / generic / calctrl.cpp
CommitLineData
2ef31e80
VZ
1///////////////////////////////////////////////////////////////////////////////
2// Name: generic/calctrl.cpp
3// Purpose: implementation fo the generic wxCalendarCtrl
4// Author: Vadim Zeitlin
5// Modified by:
6// Created: 29.12.99
7// RCS-ID: $Id$
8// Copyright: (c) 1999 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9// Licence: wxWindows license
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#ifdef __GNUG__
21 #pragma implementation "calctrl.h"
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
28 #pragma hdrstop
29#endif
30
31#ifndef WX_PRECOMP
9d9b7755
VZ
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/brush.h"
4f6aed9c 35 #include "wx/combobox.h"
bc385ba9 36 #include "wx/stattext.h"
2ef31e80
VZ
37#endif //WX_PRECOMP
38
2fa7c206
JS
39// Can only use wxSpinEvent if this is enabled
40#if wxUSE_SPINBTN
41
9d9b7755 42#include "wx/calctrl.h"
2ef31e80 43
882a8f40
VZ
44#define DEBUG_PAINT 0
45
46// ----------------------------------------------------------------------------
47// private classes
48// ----------------------------------------------------------------------------
49
50class wxMonthComboBox : public wxComboBox
51{
52public:
53 wxMonthComboBox(wxCalendarCtrl *cal);
54
55 void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); }
56
57private:
58 wxCalendarCtrl *m_cal;
59
60 DECLARE_EVENT_TABLE()
61};
62
63class wxYearSpinCtrl : public wxSpinCtrl
64{
65public:
66 wxYearSpinCtrl(wxCalendarCtrl *cal);
67
68 void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); }
69
70private:
71 wxCalendarCtrl *m_cal;
72
73 DECLARE_EVENT_TABLE()
74};
75
2ef31e80
VZ
76// ----------------------------------------------------------------------------
77// wxWin macros
78// ----------------------------------------------------------------------------
79
80BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl)
81 EVT_PAINT(wxCalendarCtrl::OnPaint)
82
9d9b7755
VZ
83 EVT_CHAR(wxCalendarCtrl::OnChar)
84
2ef31e80 85 EVT_LEFT_DOWN(wxCalendarCtrl::OnClick)
0185cd09 86 EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick)
882a8f40
VZ
87END_EVENT_TABLE()
88
89BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
90 EVT_COMBOBOX(-1, wxMonthComboBox::OnMonthChange)
91END_EVENT_TABLE()
9d9b7755 92
882a8f40
VZ
93BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
94 EVT_SPINCTRL(-1, wxYearSpinCtrl::OnYearChange)
2ef31e80
VZ
95END_EVENT_TABLE()
96
97IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
f6bcfd97 98IMPLEMENT_DYNAMIC_CLASS(wxCalendarEvent, wxCommandEvent)
2ef31e80
VZ
99
100// ============================================================================
101// implementation
102// ============================================================================
103
882a8f40
VZ
104// ----------------------------------------------------------------------------
105// wxMonthComboBox and wxYearSpinCtrl
106// ----------------------------------------------------------------------------
107
108wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
109 : wxComboBox(cal->GetParent(), -1,
110 wxEmptyString,
111 wxDefaultPosition,
112 wxDefaultSize,
113 0, NULL,
114 wxCB_READONLY)
115{
116 m_cal = cal;
117
118 wxDateTime::Month m;
119 for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
120 {
121 Append(wxDateTime::GetMonthName(m));
122 }
123
124 SetSelection(m_cal->GetDate().GetMonth());
f6bcfd97 125 SetSize(-1, -1, -1, -1, wxSIZE_AUTO_WIDTH|wxSIZE_AUTO_HEIGHT);
882a8f40
VZ
126}
127
128wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
129 : wxSpinCtrl(cal->GetParent(), -1,
130 cal->GetDate().Format(_T("%Y")),
131 wxDefaultPosition,
132 wxDefaultSize,
133 wxSP_ARROW_KEYS,
134 -4300, 10000, cal->GetDate().GetYear())
135{
136 m_cal = cal;
137}
138
2ef31e80
VZ
139// ----------------------------------------------------------------------------
140// wxCalendarCtrl
141// ----------------------------------------------------------------------------
142
143void wxCalendarCtrl::Init()
144{
145 m_comboMonth = NULL;
146 m_spinYear = NULL;
147
148 m_widthCol =
149 m_heightRow = 0;
9d9b7755
VZ
150
151 wxDateTime::WeekDay wd;
152 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
153 {
154 m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
155 }
3965571c 156
4f6aed9c
VZ
157 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
158 {
159 m_attrs[n] = NULL;
160 }
161
162 wxSystemSettings ss;
163 m_colHighlightFg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
164 m_colHighlightBg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHT);
165
166 m_colHolidayFg = *wxRED;
167 // don't set m_colHolidayBg - by default, same as our bg colour
168
169 m_colHeaderFg = *wxBLUE;
170 m_colHeaderBg = *wxLIGHT_GREY;
2ef31e80
VZ
171}
172
13111b2a
VZ
173bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent),
174 wxWindowID WXUNUSED(id),
2ef31e80 175 const wxDateTime& date,
13111b2a 176 const wxPoint& WXUNUSED(pos),
2ef31e80
VZ
177 const wxSize& size,
178 long style,
13111b2a 179 const wxString& WXUNUSED(name))
2ef31e80 180{
bc385ba9
VZ
181 // needed to get the arrow keys normally used for the dialog navigation
182 SetWindowStyle(style | wxWANTS_CHARS);
2ef31e80 183
882a8f40 184 m_date = date.IsValid() ? date : wxDateTime::Today();
9d9b7755 185
882a8f40 186 m_spinYear = new wxYearSpinCtrl(this);
bc385ba9
VZ
187 m_staticYear = new wxStaticText(GetParent(), -1, m_date.Format(_T("%Y")),
188 wxDefaultPosition, wxDefaultSize,
189 wxALIGN_CENTRE);
190
191 m_comboMonth = new wxMonthComboBox(this);
192 m_staticMonth = new wxStaticText(GetParent(), -1, m_date.Format(_T("%B")),
193 wxDefaultPosition, wxDefaultSize,
194 wxALIGN_CENTRE);
195
196 ShowCurrentControls();
9d9b7755 197
2ef31e80
VZ
198 wxSize sizeReal;
199 if ( size.x == -1 || size.y == -1 )
200 {
201 sizeReal = DoGetBestSize();
202 if ( size.x != -1 )
203 sizeReal.x = size.x;
204 if ( size.y != -1 )
205 sizeReal.y = size.y;
206 }
207 else
208 {
209 sizeReal = size;
210 }
211
212 SetSize(sizeReal);
213
214 SetBackgroundColour(*wxWHITE);
9d9b7755 215 SetFont(*wxSWISS_FONT);
2ef31e80 216
4f6aed9c
VZ
217 SetHolidayAttrs();
218
2ef31e80
VZ
219 return TRUE;
220}
221
882a8f40
VZ
222wxCalendarCtrl::~wxCalendarCtrl()
223{
4f6aed9c
VZ
224 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
225 {
226 delete m_attrs[n];
227 }
882a8f40
VZ
228}
229
230// ----------------------------------------------------------------------------
231// forward wxWin functions to subcontrols
232// ----------------------------------------------------------------------------
233
234bool wxCalendarCtrl::Show(bool show)
235{
236 if ( !wxControl::Show(show) )
237 {
238 return FALSE;
239 }
240
bc385ba9
VZ
241 GetMonthControl()->Show(show);
242 GetYearControl()->Show(show);
882a8f40
VZ
243
244 return TRUE;
245}
246
247bool wxCalendarCtrl::Enable(bool enable)
248{
249 if ( !wxControl::Enable(enable) )
250 {
251 return FALSE;
252 }
253
bc385ba9
VZ
254 GetMonthControl()->Enable(enable);
255 GetYearControl()->Enable(enable);
882a8f40
VZ
256
257 return TRUE;
258}
259
bc385ba9
VZ
260// ----------------------------------------------------------------------------
261// enable/disable month/year controls
262// ----------------------------------------------------------------------------
263
264void wxCalendarCtrl::ShowCurrentControls()
265{
266 if ( AllowMonthChange() )
267 {
268 m_comboMonth->Show();
269 m_staticMonth->Hide();
270
271 if ( AllowYearChange() )
272 {
273 m_spinYear->Show();
274 m_staticYear->Hide();
275
276 // skip the rest
277 return;
278 }
279 }
280 else
281 {
282 m_comboMonth->Hide();
283 m_staticMonth->Show();
284 }
285
286 // year change not allowed here
287 m_spinYear->Hide();
288 m_staticYear->Show();
289}
290
291wxControl *wxCalendarCtrl::GetMonthControl() const
292{
380d9d62 293 return AllowMonthChange() ? (wxControl *)m_comboMonth : (wxControl *)m_staticMonth;
bc385ba9
VZ
294}
295
296wxControl *wxCalendarCtrl::GetYearControl() const
297{
380d9d62 298 return AllowYearChange() ? (wxControl *)m_spinYear : (wxControl *)m_staticYear;
bc385ba9
VZ
299}
300
301void wxCalendarCtrl::EnableYearChange(bool enable)
302{
303 if ( enable != AllowYearChange() )
304 {
305 long style = GetWindowStyle();
306 if ( enable )
307 style &= ~wxCAL_NO_YEAR_CHANGE;
308 else
309 style |= wxCAL_NO_YEAR_CHANGE;
310 SetWindowStyle(style);
311
312 ShowCurrentControls();
313 }
314}
315
316void wxCalendarCtrl::EnableMonthChange(bool enable)
317{
318 if ( enable != AllowMonthChange() )
319 {
320 long style = GetWindowStyle();
321 if ( enable )
322 style &= ~wxCAL_NO_MONTH_CHANGE;
323 else
324 style |= wxCAL_NO_MONTH_CHANGE;
325 SetWindowStyle(style);
326
327 ShowCurrentControls();
328 }
329}
330
9d9b7755
VZ
331// ----------------------------------------------------------------------------
332// changing date
333// ----------------------------------------------------------------------------
334
335void wxCalendarCtrl::SetDate(const wxDateTime& date)
336{
bc385ba9
VZ
337 bool sameMonth = m_date.GetMonth() == date.GetMonth(),
338 sameYear = m_date.GetYear() == date.GetYear();
339
340 if ( sameMonth && sameYear )
9d9b7755
VZ
341 {
342 // just change the day
343 ChangeDay(date);
344 }
345 else
346 {
bc385ba9
VZ
347 if ( !AllowMonthChange() || (!AllowYearChange() && !sameYear) )
348 {
349 // forbidden
350 return;
351 }
352
9d9b7755
VZ
353 // change everything
354 m_date = date;
355
356 // update the controls
357 m_comboMonth->SetSelection(m_date.GetMonth());
bc385ba9
VZ
358
359 if ( AllowYearChange() )
360 {
361 m_spinYear->SetValue(m_date.Format(_T("%Y")));
362 }
9d9b7755 363
0de868d9
VZ
364 // as the month changed, holidays did too
365 SetHolidayAttrs();
366
9d9b7755
VZ
367 // update the calendar
368 Refresh();
369 }
370}
371
372void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
373{
374 if ( m_date != date )
375 {
376 // we need to refresh the row containing the old date and the one
377 // containing the new one
378 wxDateTime dateOld = m_date;
379 m_date = date;
380
381 RefreshDate(dateOld);
382
383 // if the date is in the same row, it was already drawn correctly
384 if ( GetWeek(m_date) != GetWeek(dateOld) )
385 {
386 RefreshDate(m_date);
387 }
388 }
389}
390
391void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
392{
393 wxDateTime::Tm tm1 = m_date.GetTm(),
394 tm2 = date.GetTm();
395
396 wxEventType type;
397 if ( tm1.year != tm2.year )
398 type = wxEVT_CALENDAR_YEAR_CHANGED;
399 else if ( tm1.mon != tm2.mon )
400 type = wxEVT_CALENDAR_MONTH_CHANGED;
401 else if ( tm1.mday != tm2.mday )
402 type = wxEVT_CALENDAR_DAY_CHANGED;
403 else
404 return;
405
406 SetDate(date);
407
4f6aed9c 408 GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
9d9b7755
VZ
409}
410
2ef31e80
VZ
411// ----------------------------------------------------------------------------
412// date helpers
413// ----------------------------------------------------------------------------
414
415wxDateTime wxCalendarCtrl::GetStartDate() const
416{
417 wxDateTime::Tm tm = m_date.GetTm();
418
419 wxDateTime date = wxDateTime(1, tm.mon, tm.year);
9d9b7755 420
1a8557b1
VZ
421 // rewind back
422 date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
423 ? wxDateTime::Mon : wxDateTime::Sun);
424
2ef31e80
VZ
425 return date;
426}
427
428bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
429{
430 return date.GetMonth() == m_date.GetMonth();
431}
432
9d9b7755
VZ
433size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
434{
1a8557b1
VZ
435 return date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
436 ? wxDateTime::Monday_First
437 : wxDateTime::Sunday_First);
9d9b7755
VZ
438}
439
2ef31e80
VZ
440// ----------------------------------------------------------------------------
441// size management
442// ----------------------------------------------------------------------------
443
9d9b7755
VZ
444// this is a composite control and it must arrange its parts each time its
445// size or position changes: the combobox and spinctrl are along the top of
446// the available area and the calendar takes up therest of the space
447
bc385ba9
VZ
448// the static controls are supposed to be always smaller than combo/spin so we
449// always use the latter for size calculations and position the static to take
450// the same space
451
9d9b7755
VZ
452// the constants used for the layout
453#define VERT_MARGIN 5 // distance between combo and calendar
454#define HORZ_MARGIN 15 // spin
455
2ef31e80
VZ
456wxSize wxCalendarCtrl::DoGetBestSize() const
457{
9d9b7755
VZ
458 // calc the size of the calendar
459 ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast
460
461 wxCoord width = 7*m_widthCol,
462 height = 7*m_heightRow;
463
464 wxSize sizeCombo = m_comboMonth->GetBestSize(),
465 sizeSpin = m_spinYear->GetBestSize();
466
467 height += VERT_MARGIN + wxMax(sizeCombo.y, sizeSpin.y);
468
bc385ba9
VZ
469 if ( GetWindowStyle() & (wxRAISED_BORDER | wxSUNKEN_BORDER) )
470 {
471 // the border would clip the last line otherwise
472 height += 4;
473 }
474
9d9b7755 475 return wxSize(width, height);
2ef31e80
VZ
476}
477
478void wxCalendarCtrl::DoSetSize(int x, int y,
479 int width, int height,
480 int sizeFlags)
481{
482 wxControl::DoSetSize(x, y, width, height, sizeFlags);
483}
484
485void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
486{
9d9b7755 487 wxSize sizeCombo = m_comboMonth->GetSize();
bc385ba9
VZ
488 wxSize sizeStatic = m_staticMonth->GetSize();
489
490 int dy = (sizeCombo.y - sizeStatic.y) / 2;
9d9b7755 491 m_comboMonth->Move(x, y);
bc385ba9 492 m_staticMonth->SetSize(x, y + dy, sizeCombo.x, sizeStatic.y);
2ef31e80 493
9d9b7755 494 int xDiff = sizeCombo.x + HORZ_MARGIN;
882a8f40 495 m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
bc385ba9 496 m_staticYear->SetSize(x + xDiff, y + dy, width - xDiff, sizeStatic.y);
2ef31e80 497
9d9b7755
VZ
498 wxSize sizeSpin = m_spinYear->GetSize();
499 int yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
500
501 wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
502}
503
882a8f40
VZ
504void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
505{
506 wxControl::DoGetPosition(x, y);
507
508 // our real top corner is not in this position
509 if ( y )
510 {
bc385ba9 511 *y -= GetMonthControl()->GetSize().y + VERT_MARGIN;
882a8f40
VZ
512 }
513}
514
515void wxCalendarCtrl::DoGetSize(int *width, int *height) const
516{
517 wxControl::DoGetSize(width, height);
518
519 // our real height is bigger
520 if ( height )
521 {
bc385ba9 522 *height += GetMonthControl()->GetSize().y + VERT_MARGIN;
882a8f40
VZ
523 }
524}
525
9d9b7755 526void wxCalendarCtrl::RecalcGeometry()
2ef31e80 527{
9d9b7755
VZ
528 if ( m_widthCol != 0 )
529 return;
2ef31e80 530
9d9b7755 531 wxClientDC dc(this);
3965571c 532
9d9b7755 533 dc.SetFont(m_font);
3965571c 534
2ef31e80
VZ
535 // determine the column width (we assume that the weekday names are always
536 // wider (in any language) than the numbers)
537 m_widthCol = 0;
9d9b7755 538 wxDateTime::WeekDay wd;
2ef31e80
VZ
539 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
540 {
2ef31e80 541 wxCoord width;
9d9b7755 542 dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
2ef31e80
VZ
543 if ( width > m_widthCol )
544 {
545 m_widthCol = width;
546 }
547 }
3965571c 548
2ef31e80
VZ
549 // leave some margins
550 m_widthCol += 2;
551 m_heightRow += 2;
9d9b7755
VZ
552}
553
554// ----------------------------------------------------------------------------
555// drawing
556// ----------------------------------------------------------------------------
557
13111b2a 558void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
9d9b7755 559{
07e87221 560 wxPaintDC dc(this);
3965571c 561
9d9b7755
VZ
562 dc.SetFont(m_font);
563
3965571c 564 RecalcGeometry();
9d9b7755 565
882a8f40 566#if DEBUG_PAINT
f6bcfd97 567 wxLogDebug("--- starting to paint, selection: %s, week %u\n",
9d9b7755
VZ
568 m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
569 GetWeek(m_date));
882a8f40 570#endif
2ef31e80
VZ
571
572 // first draw the week days
9d9b7755 573 if ( IsExposed(0, 0, 7*m_widthCol, m_heightRow) )
2ef31e80 574 {
882a8f40 575#if DEBUG_PAINT
f6bcfd97 576 wxLogDebug("painting the header");
882a8f40 577#endif
9d9b7755 578
9d9b7755 579 dc.SetBackgroundMode(wxTRANSPARENT);
4f6aed9c
VZ
580 dc.SetTextForeground(m_colHeaderFg);
581 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
582 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
9d9b7755 583 dc.DrawRectangle(0, 0, 7*m_widthCol, m_heightRow);
1a8557b1
VZ
584
585 bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0;
586 for ( size_t wd = 0; wd < 7; wd++ )
9d9b7755 587 {
1a8557b1
VZ
588 size_t n;
589 if ( startOnMonday )
590 n = wd == 6 ? 0 : wd + 1;
591 else
592 n = wd;
593
3965571c 594 dc.DrawText(m_weekdays[n], wd*m_widthCol + 1, 0);
9d9b7755 595 }
2ef31e80
VZ
596 }
597
598 // then the calendar itself
599 dc.SetTextForeground(*wxBLACK);
600 //dc.SetFont(*wxNORMAL_FONT);
601
602 wxCoord y = m_heightRow;
603
604 wxDateTime date = GetStartDate();
882a8f40 605#if DEBUG_PAINT
f6bcfd97 606 wxLogDebug("starting calendar from %s\n",
9d9b7755 607 date.Format("%a %d-%m-%Y %H:%M:%S").c_str());
882a8f40 608#endif
9d9b7755 609
2ef31e80 610 dc.SetBackgroundMode(wxSOLID);
9d9b7755 611 for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow )
2ef31e80 612 {
9d9b7755 613 // if the update region doesn't intersect this row, don't paint it
15807266 614 if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) )
9d9b7755
VZ
615 {
616 date += wxDateSpan::Week();
617
618 continue;
619 }
882a8f40 620
1a8557b1 621#if DEBUG_PAINT
f6bcfd97 622 wxLogDebug("painting week %d at y = %d\n", nWeek, y);
882a8f40 623#endif
9d9b7755 624
1a8557b1 625 for ( size_t wd = 0; wd < 7; wd++ )
2ef31e80
VZ
626 {
627 if ( IsDateShown(date) )
628 {
882a8f40 629 // don't use wxDate::Format() which prepends 0s
4f6aed9c
VZ
630 unsigned int day = date.GetDay();
631 wxString dayStr = wxString::Format(_T("%u"), day);
3965571c 632 wxCoord width;
4f6aed9c
VZ
633 dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL);
634
635 bool changedColours = FALSE,
636 changedFont = FALSE;
637
638 wxCalendarDateAttr *attr = m_attrs[day - 1];
2ef31e80
VZ
639
640 bool isSel = m_date == date;
641 if ( isSel )
642 {
4f6aed9c
VZ
643 dc.SetTextForeground(m_colHighlightFg);
644 dc.SetTextBackground(m_colHighlightBg);
645
646 changedColours = TRUE;
647 }
648 else if ( attr )
649 {
650 wxColour colFg, colBg;
651
652 if ( attr->IsHoliday() )
653 {
654 colFg = m_colHolidayFg;
655 colBg = m_colHolidayBg;
656 }
657 else
658 {
659 colFg = attr->GetTextColour();
660 colBg = attr->GetBackgroundColour();
661 }
662
663 if ( colFg.Ok() )
664 {
665 dc.SetTextForeground(colFg);
666 changedColours = TRUE;
667 }
668
669 if ( colBg.Ok() )
670 {
671 dc.SetTextBackground(colBg);
672 changedColours = TRUE;
673 }
674
675 if ( attr->HasFont() )
676 {
677 dc.SetFont(attr->GetFont());
678 changedFont = TRUE;
679 }
2ef31e80
VZ
680 }
681
4f6aed9c
VZ
682 wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2;
683 dc.DrawText(dayStr, x, y + 1);
2ef31e80 684
4f6aed9c
VZ
685 if ( !isSel && attr && attr->HasBorder() )
686 {
687 wxColour colBorder;
688 if ( attr->HasBorderColour() )
689 {
690 colBorder = attr->GetBorderColour();
691 }
692 else
693 {
694 colBorder = m_foregroundColour;
695 }
696
697 wxPen pen(colBorder, 1, wxSOLID);
698 dc.SetPen(pen);
699 dc.SetBrush(*wxTRANSPARENT_BRUSH);
700
701 switch ( attr->GetBorder() )
702 {
703 case wxCAL_BORDER_SQUARE:
704 dc.DrawRectangle(x - 2, y,
705 width + 4, m_heightRow);
706 break;
707
708 case wxCAL_BORDER_ROUND:
709 dc.DrawEllipse(x - 2, y,
710 width + 4, m_heightRow);
711 break;
712
713 default:
714 wxFAIL_MSG(_T("unknown border type"));
715 }
716 }
717
718 if ( changedColours )
2ef31e80 719 {
9d9b7755 720 dc.SetTextForeground(m_foregroundColour);
2ef31e80
VZ
721 dc.SetTextBackground(m_backgroundColour);
722 }
4f6aed9c
VZ
723
724 if ( changedFont )
725 {
726 dc.SetFont(m_font);
727 }
2ef31e80
VZ
728 }
729 //else: just don't draw it
730
731 date += wxDateSpan::Day();
732 }
2ef31e80 733 }
882a8f40 734#if DEBUG_PAINT
f6bcfd97 735 wxLogDebug("+++ finished painting");
882a8f40 736#endif
9d9b7755
VZ
737}
738
739void wxCalendarCtrl::RefreshDate(const wxDateTime& date)
740{
741 RecalcGeometry();
742
743 wxRect rect;
744
745 // always refresh the whole row at once because our OnPaint() will draw
746 // the whole row anyhow - and this allows the small optimisation in
747 // OnClick() below to work
748 rect.x = 0;
749 rect.y = m_heightRow * GetWeek(date);
750 rect.width = 7*m_widthCol;
751 rect.height = m_heightRow;
752
f6bcfd97
BP
753#ifdef __WXMSW__
754 // VZ: for some reason, the selected date seems to occupy more space under
755 // MSW - this is probably some bug in the font size calculations, but I
756 // don't know where exactly. This fix is ugly and leads to more
757 // refreshes than really needed, but without it the selected days
758 // leaves even more ugly underscores on screen.
759 rect.Inflate(0, 1);
760#endif // MSW
761
882a8f40 762#if DEBUG_PAINT
f6bcfd97 763 wxLogDebug("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
9d9b7755
VZ
764 GetWeek(date),
765 rect.x, rect.y,
766 rect.x + rect.width, rect.y + rect.height);
882a8f40 767#endif
9d9b7755
VZ
768
769 Refresh(TRUE, &rect);
2ef31e80
VZ
770}
771
772// ----------------------------------------------------------------------------
773// mouse handling
774// ----------------------------------------------------------------------------
775
0185cd09 776void wxCalendarCtrl::OnDClick(wxMouseEvent& event)
2ef31e80 777{
0185cd09 778 if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY )
2ef31e80
VZ
779 {
780 event.Skip();
781 }
782 else
783 {
4f6aed9c 784 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
0185cd09
VZ
785 }
786}
787
788void wxCalendarCtrl::OnClick(wxMouseEvent& event)
789{
790 wxDateTime date;
791 wxDateTime::WeekDay wday;
792 switch ( HitTest(event.GetPosition(), &date, &wday) )
793 {
794 case wxCAL_HITTEST_DAY:
795 ChangeDay(date);
2ef31e80 796
4f6aed9c
VZ
797 GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED,
798 wxEVT_CALENDAR_SEL_CHANGED);
0185cd09
VZ
799 break;
800
801 case wxCAL_HITTEST_HEADER:
802 {
803 wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED);
804 event.m_wday = wday;
805 (void)GetEventHandler()->ProcessEvent(event);
806 }
807 break;
808
809 default:
810 wxFAIL_MSG(_T("unknown hittest code"));
811 // fall through
812
813 case wxCAL_HITTEST_NOWHERE:
814 event.Skip();
815 break;
2ef31e80
VZ
816 }
817}
818
0185cd09
VZ
819wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos,
820 wxDateTime *date,
821 wxDateTime::WeekDay *wd)
2ef31e80 822{
9d9b7755
VZ
823 RecalcGeometry();
824
0185cd09
VZ
825 int wday = pos.x / m_widthCol;
826
2ef31e80
VZ
827 wxCoord y = pos.y;
828 if ( y < m_heightRow )
0185cd09
VZ
829 {
830 if ( wd )
831 {
832 if ( GetWindowStyle() & wxCAL_MONDAY_FIRST )
833 {
834 wday = wday == 6 ? 0 : wday + 1;
835 }
836
837 *wd = (wxDateTime::WeekDay)wday;
838 }
2ef31e80 839
0185cd09
VZ
840 return wxCAL_HITTEST_HEADER;
841 }
2ef31e80 842
0185cd09 843 int week = (y - m_heightRow) / m_heightRow;
2ef31e80 844 if ( week >= 6 || wday >= 7 )
0185cd09
VZ
845 {
846 return wxCAL_HITTEST_NOWHERE;
847 }
2ef31e80 848
0185cd09 849 wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday);
2ef31e80 850
0185cd09
VZ
851 if ( IsDateShown(dt) )
852 {
853 if ( date )
854 *date = dt;
9d9b7755 855
0185cd09
VZ
856 return wxCAL_HITTEST_DAY;
857 }
858 else
859 {
860 return wxCAL_HITTEST_NOWHERE;
861 }
2ef31e80 862}
9d9b7755
VZ
863
864// ----------------------------------------------------------------------------
865// subcontrols events handling
866// ----------------------------------------------------------------------------
867
868void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event)
869{
870 wxDateTime::Tm tm = m_date.GetTm();
871
872 wxDateTime::Month mon = (wxDateTime::Month)event.GetInt();
873 if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) )
874 {
875 tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year);
876 }
877
0de868d9 878 SetDateAndNotify(wxDateTime(tm.mday, mon, tm.year));
9d9b7755
VZ
879}
880
881void wxCalendarCtrl::OnYearChange(wxSpinEvent& event)
882{
883 wxDateTime::Tm tm = m_date.GetTm();
884
13111b2a 885 int year = (int)event.GetInt();
9d9b7755
VZ
886 if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
887 {
888 tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
889 }
890
0de868d9 891 SetDateAndNotify(wxDateTime(tm.mday, tm.mon, year));
9d9b7755
VZ
892}
893
894// ----------------------------------------------------------------------------
895// keyboard interface
896// ----------------------------------------------------------------------------
897
898void wxCalendarCtrl::OnChar(wxKeyEvent& event)
899{
900 switch ( event.KeyCode() )
901 {
902 case _T('+'):
903 case WXK_ADD:
904 SetDateAndNotify(m_date + wxDateSpan::Year());
905 break;
906
907 case _T('-'):
908 case WXK_SUBTRACT:
909 SetDateAndNotify(m_date - wxDateSpan::Year());
910 break;
911
882a8f40
VZ
912 case WXK_PRIOR:
913 SetDateAndNotify(m_date - wxDateSpan::Month());
9d9b7755
VZ
914 break;
915
882a8f40
VZ
916 case WXK_NEXT:
917 SetDateAndNotify(m_date + wxDateSpan::Month());
9d9b7755
VZ
918 break;
919
920 case WXK_RIGHT:
1a8557b1
VZ
921 if ( event.ControlDown() )
922 SetDateAndNotify(wxDateTime(m_date).SetToNextWeekDay(
923 GetWindowStyle() & wxCAL_MONDAY_FIRST
924 ? wxDateTime::Sun : wxDateTime::Sat));
925 else
926 SetDateAndNotify(m_date + wxDateSpan::Day());
9d9b7755
VZ
927 break;
928
929 case WXK_LEFT:
1a8557b1
VZ
930 if ( event.ControlDown() )
931 SetDateAndNotify(wxDateTime(m_date).SetToPrevWeekDay(
932 GetWindowStyle() & wxCAL_MONDAY_FIRST
933 ? wxDateTime::Mon : wxDateTime::Sun));
934 else
935 SetDateAndNotify(m_date - wxDateSpan::Day());
9d9b7755
VZ
936 break;
937
938 case WXK_UP:
939 SetDateAndNotify(m_date - wxDateSpan::Week());
940 break;
941
942 case WXK_DOWN:
943 SetDateAndNotify(m_date + wxDateSpan::Week());
944 break;
945
946 case WXK_HOME:
1a8557b1
VZ
947 if ( event.ControlDown() )
948 SetDateAndNotify(wxDateTime::Today());
949 else
950 SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
951 break;
952
953 case WXK_END:
954 SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
9d9b7755
VZ
955 break;
956
4f6aed9c
VZ
957 case WXK_RETURN:
958 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
959 break;
960
9d9b7755
VZ
961 default:
962 event.Skip();
963 }
964}
965
966// ----------------------------------------------------------------------------
4f6aed9c 967// holidays handling
9d9b7755
VZ
968// ----------------------------------------------------------------------------
969
4f6aed9c 970void wxCalendarCtrl::EnableHolidayDisplay(bool display)
9d9b7755 971{
4f6aed9c
VZ
972 long style = GetWindowStyle();
973 if ( display )
974 style |= wxCAL_SHOW_HOLIDAYS;
975 else
976 style &= ~wxCAL_SHOW_HOLIDAYS;
977
978 SetWindowStyle(style);
979
980 if ( display )
981 SetHolidayAttrs();
982 else
983 ResetHolidayAttrs();
984
985 Refresh();
986}
987
988void wxCalendarCtrl::SetHolidayAttrs()
989{
990 if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
991 {
992 ResetHolidayAttrs();
993
994 wxDateTime::Tm tm = m_date.GetTm();
995 wxDateTime dtStart(1, tm.mon, tm.year),
996 dtEnd = dtStart.GetLastMonthDay();
997
998 wxDateTimeArray hol;
999 wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
1000
1001 size_t count = hol.GetCount();
1002 for ( size_t n = 0; n < count; n++ )
1003 {
1004 SetHoliday(hol[n].GetDay());
1005 }
1006 }
1007}
1008
1009void wxCalendarCtrl::SetHoliday(size_t day)
1010{
1011 wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
0185cd09 1012
4f6aed9c
VZ
1013 wxCalendarDateAttr *attr = GetAttr(day);
1014 if ( !attr )
0185cd09 1015 {
4f6aed9c
VZ
1016 attr = new wxCalendarDateAttr;
1017 }
0185cd09 1018
4f6aed9c
VZ
1019 attr->SetHoliday(TRUE);
1020
1021 // can't use SetAttr() because it would delete this pointer
1022 m_attrs[day - 1] = attr;
1023}
1024
1025void wxCalendarCtrl::ResetHolidayAttrs()
1026{
1027 for ( size_t day = 0; day < 31; day++ )
1028 {
1029 if ( m_attrs[day] )
1030 {
1031 m_attrs[day]->SetHoliday(FALSE);
1032 }
0185cd09
VZ
1033 }
1034}
1035
4f6aed9c
VZ
1036// ----------------------------------------------------------------------------
1037// wxCalendarEvent
1038// ----------------------------------------------------------------------------
1039
0185cd09
VZ
1040void wxCalendarEvent::Init()
1041{
1042 m_wday = wxDateTime::Inv_WeekDay;
9d9b7755
VZ
1043}
1044
1045wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
1046 : wxCommandEvent(type, cal->GetId())
1047{
1048 m_date = cal->GetDate();
1049}
2fa7c206
JS
1050
1051#endif // wxUSE_SPINBTN
1052