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