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