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