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