]> git.saurik.com Git - wxWidgets.git/blame_incremental - src/generic/calctrl.cpp
Fixed typos and added test case that changes the font of a CalendarCtrl
[wxWidgets.git] / src / generic / calctrl.cpp
... / ...
CommitLineData
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 licence
10///////////////////////////////////////////////////////////////////////////////
11
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
20#if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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
32 #include "wx/dcclient.h"
33 #include "wx/settings.h"
34 #include "wx/brush.h"
35 #include "wx/combobox.h"
36 #include "wx/listbox"
37 #include "wx/stattext.h"
38 #include "wx/textctrl.h"
39#endif //WX_PRECOMP
40
41#if wxUSE_CALENDARCTRL
42
43#include "wx/spinctrl.h"
44
45#include "wx/calctrl.h"
46
47#define DEBUG_PAINT 0
48
49// ----------------------------------------------------------------------------
50// private classes
51// ----------------------------------------------------------------------------
52
53class wxMonthComboBox : public wxComboBox
54{
55public:
56 wxMonthComboBox(wxCalendarCtrl *cal);
57
58 void OnMonthChange(wxCommandEvent& event) { m_cal->OnMonthChange(event); }
59
60private:
61 wxCalendarCtrl *m_cal;
62
63 DECLARE_EVENT_TABLE()
64 DECLARE_NO_COPY_CLASS(wxMonthComboBox)
65};
66
67class wxYearSpinCtrl : public wxSpinCtrl
68{
69public:
70 wxYearSpinCtrl(wxCalendarCtrl *cal);
71
72 void OnYearTextChange(wxCommandEvent& event)
73 {
74 m_cal->SetUserChangedYear();
75 m_cal->OnYearChange(event);
76 }
77 void OnYearChange(wxSpinEvent& event) { m_cal->OnYearChange(event); }
78
79private:
80 wxCalendarCtrl *m_cal;
81
82 DECLARE_EVENT_TABLE()
83 DECLARE_NO_COPY_CLASS(wxYearSpinCtrl)
84};
85
86// ----------------------------------------------------------------------------
87// wxWin macros
88// ----------------------------------------------------------------------------
89
90BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl)
91 EVT_PAINT(wxCalendarCtrl::OnPaint)
92
93 EVT_CHAR(wxCalendarCtrl::OnChar)
94
95 EVT_LEFT_DOWN(wxCalendarCtrl::OnClick)
96 EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick)
97END_EVENT_TABLE()
98
99BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox)
100 EVT_COMBOBOX(wxID_ANY, wxMonthComboBox::OnMonthChange)
101END_EVENT_TABLE()
102
103BEGIN_EVENT_TABLE(wxYearSpinCtrl, wxSpinCtrl)
104 EVT_TEXT(wxID_ANY, wxYearSpinCtrl::OnYearTextChange)
105 EVT_SPINCTRL(wxID_ANY, wxYearSpinCtrl::OnYearChange)
106END_EVENT_TABLE()
107
108#if wxUSE_EXTENDED_RTTI
109WX_DEFINE_FLAGS( wxCalendarCtrlStyle )
110
111wxBEGIN_FLAGS( wxCalendarCtrlStyle )
112 // new style border flags, we put them first to
113 // use them for streaming out
114 wxFLAGS_MEMBER(wxBORDER_SIMPLE)
115 wxFLAGS_MEMBER(wxBORDER_SUNKEN)
116 wxFLAGS_MEMBER(wxBORDER_DOUBLE)
117 wxFLAGS_MEMBER(wxBORDER_RAISED)
118 wxFLAGS_MEMBER(wxBORDER_STATIC)
119 wxFLAGS_MEMBER(wxBORDER_NONE)
120
121 // old style border flags
122 wxFLAGS_MEMBER(wxSIMPLE_BORDER)
123 wxFLAGS_MEMBER(wxSUNKEN_BORDER)
124 wxFLAGS_MEMBER(wxDOUBLE_BORDER)
125 wxFLAGS_MEMBER(wxRAISED_BORDER)
126 wxFLAGS_MEMBER(wxSTATIC_BORDER)
127 wxFLAGS_MEMBER(wxBORDER)
128
129 // standard window styles
130 wxFLAGS_MEMBER(wxTAB_TRAVERSAL)
131 wxFLAGS_MEMBER(wxCLIP_CHILDREN)
132 wxFLAGS_MEMBER(wxTRANSPARENT_WINDOW)
133 wxFLAGS_MEMBER(wxWANTS_CHARS)
134 wxFLAGS_MEMBER(wxFULL_REPAINT_ON_RESIZE)
135 wxFLAGS_MEMBER(wxALWAYS_SHOW_SB )
136 wxFLAGS_MEMBER(wxVSCROLL)
137 wxFLAGS_MEMBER(wxHSCROLL)
138
139 wxFLAGS_MEMBER(wxCAL_SUNDAY_FIRST)
140 wxFLAGS_MEMBER(wxCAL_MONDAY_FIRST)
141 wxFLAGS_MEMBER(wxCAL_SHOW_HOLIDAYS)
142 wxFLAGS_MEMBER(wxCAL_NO_YEAR_CHANGE)
143 wxFLAGS_MEMBER(wxCAL_NO_MONTH_CHANGE)
144 wxFLAGS_MEMBER(wxCAL_SEQUENTIAL_MONTH_SELECTION)
145 wxFLAGS_MEMBER(wxCAL_SHOW_SURROUNDING_WEEKS)
146
147wxEND_FLAGS( wxCalendarCtrlStyle )
148
149IMPLEMENT_DYNAMIC_CLASS_XTI(wxCalendarCtrl, wxControl,"wx/calctrl.h")
150
151wxBEGIN_PROPERTIES_TABLE(wxCalendarCtrl)
152 wxEVENT_RANGE_PROPERTY( Updated , wxEVT_CALENDAR_SEL_CHANGED , wxEVT_CALENDAR_WEEKDAY_CLICKED , wxCalendarEvent )
153 wxHIDE_PROPERTY( Children )
154 wxPROPERTY( Date,wxDateTime, SetDate , GetDate, , 0 /*flags*/ , wxT("Helpstring") , wxT("group"))
155 wxPROPERTY_FLAGS( WindowStyle , wxCalendarCtrlStyle , long , SetWindowStyleFlag , GetWindowStyleFlag , , 0 /*flags*/ , wxT("Helpstring") , wxT("group")) // style
156wxEND_PROPERTIES_TABLE()
157
158wxBEGIN_HANDLERS_TABLE(wxCalendarCtrl)
159wxEND_HANDLERS_TABLE()
160
161wxCONSTRUCTOR_6( wxCalendarCtrl , wxWindow* , Parent , wxWindowID , Id , wxDateTime , Date , wxPoint , Position , wxSize , Size , long , WindowStyle )
162#else
163IMPLEMENT_DYNAMIC_CLASS(wxCalendarCtrl, wxControl)
164#endif
165IMPLEMENT_DYNAMIC_CLASS(wxCalendarEvent, wxCommandEvent)
166
167// ----------------------------------------------------------------------------
168// events
169// ----------------------------------------------------------------------------
170
171DEFINE_EVENT_TYPE(wxEVT_CALENDAR_SEL_CHANGED)
172DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DAY_CHANGED)
173DEFINE_EVENT_TYPE(wxEVT_CALENDAR_MONTH_CHANGED)
174DEFINE_EVENT_TYPE(wxEVT_CALENDAR_YEAR_CHANGED)
175DEFINE_EVENT_TYPE(wxEVT_CALENDAR_DOUBLECLICKED)
176DEFINE_EVENT_TYPE(wxEVT_CALENDAR_WEEKDAY_CLICKED)
177
178// ============================================================================
179// implementation
180// ============================================================================
181
182// ----------------------------------------------------------------------------
183// wxMonthComboBox and wxYearSpinCtrl
184// ----------------------------------------------------------------------------
185
186wxMonthComboBox::wxMonthComboBox(wxCalendarCtrl *cal)
187 : wxComboBox(cal->GetParent(), wxID_ANY,
188 wxEmptyString,
189 wxDefaultPosition,
190 wxDefaultSize,
191 0, NULL,
192 wxCB_READONLY | wxCLIP_SIBLINGS)
193{
194 m_cal = cal;
195
196 wxDateTime::Month m;
197 for ( m = wxDateTime::Jan; m < wxDateTime::Inv_Month; wxNextMonth(m) )
198 {
199 Append(wxDateTime::GetMonthName(m));
200 }
201
202 SetSelection(m_cal->GetDate().GetMonth());
203 SetSize(wxDefaultPosition.x,
204 wxDefaultPosition.y,
205 wxDefaultSize.x,
206 wxDefaultSize.y,
207 wxSIZE_AUTO_WIDTH|wxSIZE_AUTO_HEIGHT);
208}
209
210wxYearSpinCtrl::wxYearSpinCtrl(wxCalendarCtrl *cal)
211 : wxSpinCtrl(cal->GetParent(), wxID_ANY,
212 cal->GetDate().Format(_T("%Y")),
213 wxDefaultPosition,
214 wxDefaultSize,
215 wxSP_ARROW_KEYS | wxCLIP_SIBLINGS,
216 -4300, 10000, cal->GetDate().GetYear())
217
218{
219 m_cal = cal;
220}
221
222// ----------------------------------------------------------------------------
223// wxCalendarCtrl
224// ----------------------------------------------------------------------------
225
226wxCalendarCtrl::wxCalendarCtrl(wxWindow *parent,
227 wxWindowID id,
228 const wxDateTime& date,
229 const wxPoint& pos,
230 const wxSize& size,
231 long style,
232 const wxString& name)
233{
234 Init();
235
236 (void)Create(parent, id, date, pos, size, style, name);
237}
238
239void wxCalendarCtrl::Init()
240{
241 m_comboMonth = NULL;
242 m_spinYear = NULL;
243 m_staticYear = NULL;
244 m_staticMonth = NULL;
245
246 m_userChangedYear = false;
247
248 m_widthCol =
249 m_heightRow = 0;
250
251 wxDateTime::WeekDay wd;
252 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
253 {
254 m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr);
255 }
256
257 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
258 {
259 m_attrs[n] = NULL;
260 }
261
262 m_colHighlightFg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
263 m_colHighlightBg = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT);
264
265 m_colHolidayFg = *wxRED;
266 // don't set m_colHolidayBg - by default, same as our bg colour
267
268 m_colHeaderFg = *wxBLUE;
269 m_colHeaderBg = *wxLIGHT_GREY;
270}
271
272bool wxCalendarCtrl::Create(wxWindow *parent,
273 wxWindowID id,
274 const wxDateTime& date,
275 const wxPoint& pos,
276 const wxSize& size,
277 long style,
278 const wxString& name)
279{
280 if ( !wxControl::Create(parent, id, pos, size,
281 style | wxCLIP_CHILDREN | wxWANTS_CHARS,
282 wxDefaultValidator, name) )
283 {
284 return false;
285 }
286
287 // needed to get the arrow keys normally used for the dialog navigation
288 SetWindowStyle(style | wxWANTS_CHARS);
289
290 m_date = date.IsValid() ? date : wxDateTime::Today();
291
292 m_lowdate = wxDefaultDateTime;
293 m_highdate = wxDefaultDateTime;
294
295 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
296 {
297 m_spinYear = new wxYearSpinCtrl(this);
298 m_staticYear = new wxStaticText(GetParent(), wxID_ANY, m_date.Format(_T("%Y")),
299 wxDefaultPosition, wxDefaultSize,
300 wxALIGN_CENTRE);
301
302 m_comboMonth = new wxMonthComboBox(this);
303 m_staticMonth = new wxStaticText(GetParent(), wxID_ANY, m_date.Format(_T("%B")),
304 wxDefaultPosition, wxDefaultSize,
305 wxALIGN_CENTRE);
306 }
307
308 ShowCurrentControls();
309
310 // we need to set the position as well because the main control position
311 // is not the same as the one specified in pos if we have the controls
312 // above it
313 SetBestSize(size);
314 SetPosition(pos);
315
316 SetHolidayAttrs();
317
318 return true;
319}
320
321wxCalendarCtrl::~wxCalendarCtrl()
322{
323 for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ )
324 {
325 delete m_attrs[n];
326 }
327}
328
329// ----------------------------------------------------------------------------
330// forward wxWin functions to subcontrols
331// ----------------------------------------------------------------------------
332
333bool wxCalendarCtrl::Destroy()
334{
335 if ( m_staticYear )
336 m_staticYear->Destroy();
337 if ( m_spinYear )
338 m_spinYear->Destroy();
339 if ( m_comboMonth )
340 m_comboMonth->Destroy();
341 if ( m_staticMonth )
342 m_staticMonth->Destroy();
343
344 m_staticYear = NULL;
345 m_spinYear = NULL;
346 m_comboMonth = NULL;
347 m_staticMonth = NULL;
348
349 return wxControl::Destroy();
350}
351
352bool wxCalendarCtrl::Show(bool show)
353{
354 if ( !wxControl::Show(show) )
355 {
356 return false;
357 }
358
359 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
360 {
361 if ( GetMonthControl() )
362 {
363 GetMonthControl()->Show(show);
364 GetYearControl()->Show(show);
365 }
366 }
367
368 return true;
369}
370
371bool wxCalendarCtrl::Enable(bool enable)
372{
373 if ( !wxControl::Enable(enable) )
374 {
375 return false;
376 }
377
378 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
379 {
380 GetMonthControl()->Enable(enable);
381 GetYearControl()->Enable(enable);
382 }
383
384 return true;
385}
386
387// ----------------------------------------------------------------------------
388// enable/disable month/year controls
389// ----------------------------------------------------------------------------
390
391void wxCalendarCtrl::ShowCurrentControls()
392{
393 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
394 {
395 if ( AllowMonthChange() )
396 {
397 m_comboMonth->Show();
398 m_staticMonth->Hide();
399
400 if ( AllowYearChange() )
401 {
402 m_spinYear->Show();
403 m_staticYear->Hide();
404
405 // skip the rest
406 return;
407 }
408 }
409 else
410 {
411 m_comboMonth->Hide();
412 m_staticMonth->Show();
413 }
414
415 // year change not allowed here
416 m_spinYear->Hide();
417 m_staticYear->Show();
418 }
419}
420
421wxControl *wxCalendarCtrl::GetMonthControl() const
422{
423 return AllowMonthChange() ? (wxControl *)m_comboMonth : (wxControl *)m_staticMonth;
424}
425
426wxControl *wxCalendarCtrl::GetYearControl() const
427{
428 return AllowYearChange() ? (wxControl *)m_spinYear : (wxControl *)m_staticYear;
429}
430
431void wxCalendarCtrl::EnableYearChange(bool enable)
432{
433 if ( enable != AllowYearChange() )
434 {
435 long style = GetWindowStyle();
436 if ( enable )
437 style &= ~wxCAL_NO_YEAR_CHANGE;
438 else
439 style |= wxCAL_NO_YEAR_CHANGE;
440 SetWindowStyle(style);
441
442 ShowCurrentControls();
443 if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
444 {
445 Refresh();
446 }
447 }
448}
449
450void wxCalendarCtrl::EnableMonthChange(bool enable)
451{
452 if ( enable != AllowMonthChange() )
453 {
454 long style = GetWindowStyle();
455 if ( enable )
456 style &= ~wxCAL_NO_MONTH_CHANGE;
457 else
458 style |= wxCAL_NO_MONTH_CHANGE;
459 SetWindowStyle(style);
460
461 ShowCurrentControls();
462 if ( GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION )
463 {
464 Refresh();
465 }
466 }
467}
468
469// ----------------------------------------------------------------------------
470// changing date
471// ----------------------------------------------------------------------------
472
473bool wxCalendarCtrl::SetDate(const wxDateTime& date)
474{
475 bool retval = true;
476
477 bool sameMonth = m_date.GetMonth() == date.GetMonth(),
478 sameYear = m_date.GetYear() == date.GetYear();
479
480 if ( IsDateInRange(date) )
481 {
482 if ( sameMonth && sameYear )
483 {
484 // just change the day
485 ChangeDay(date);
486 }
487 else
488 {
489 if ( AllowMonthChange() && (AllowYearChange() || sameYear) )
490 {
491 // change everything
492 m_date = date;
493
494 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
495 {
496 // update the controls
497 m_comboMonth->SetSelection(m_date.GetMonth());
498
499 if ( AllowYearChange() )
500 {
501 if ( !m_userChangedYear )
502 m_spinYear->SetValue(m_date.Format(_T("%Y")));
503 }
504 }
505
506 // as the month changed, holidays did too
507 SetHolidayAttrs();
508
509 // update the calendar
510 Refresh();
511 }
512 else
513 {
514 // forbidden
515 retval = false;
516 }
517 }
518 }
519
520 m_userChangedYear = false;
521
522 return retval;
523}
524
525void wxCalendarCtrl::ChangeDay(const wxDateTime& date)
526{
527 if ( m_date != date )
528 {
529 // we need to refresh the row containing the old date and the one
530 // containing the new one
531 wxDateTime dateOld = m_date;
532 m_date = date;
533
534 RefreshDate(dateOld);
535
536 // if the date is in the same row, it was already drawn correctly
537 if ( GetWeek(m_date) != GetWeek(dateOld) )
538 {
539 RefreshDate(m_date);
540 }
541 }
542}
543
544void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date)
545{
546 wxDateTime::Tm tm1 = m_date.GetTm(),
547 tm2 = date.GetTm();
548
549 wxEventType type;
550 if ( tm1.year != tm2.year )
551 type = wxEVT_CALENDAR_YEAR_CHANGED;
552 else if ( tm1.mon != tm2.mon )
553 type = wxEVT_CALENDAR_MONTH_CHANGED;
554 else if ( tm1.mday != tm2.mday )
555 type = wxEVT_CALENDAR_DAY_CHANGED;
556 else
557 return;
558
559 if ( SetDate(date) )
560 {
561 GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED);
562 }
563}
564
565// ----------------------------------------------------------------------------
566// date range
567// ----------------------------------------------------------------------------
568
569bool wxCalendarCtrl::SetLowerDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
570{
571 bool retval = true;
572
573 if ( !(date.IsValid()) || ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : true ) )
574 {
575 m_lowdate = date;
576 }
577 else
578 {
579 retval = false;
580 }
581
582 return retval;
583}
584
585bool wxCalendarCtrl::SetUpperDateLimit(const wxDateTime& date /* = wxDefaultDateTime */)
586{
587 bool retval = true;
588
589 if ( !(date.IsValid()) || ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : true ) )
590 {
591 m_highdate = date;
592 }
593 else
594 {
595 retval = false;
596 }
597
598 return retval;
599}
600
601bool wxCalendarCtrl::SetDateRange(const wxDateTime& lowerdate /* = wxDefaultDateTime */, const wxDateTime& upperdate /* = wxDefaultDateTime */)
602{
603 bool retval = true;
604
605 if (
606 ( !( lowerdate.IsValid() ) || ( ( upperdate.IsValid() ) ? ( lowerdate <= upperdate ) : true ) ) &&
607 ( !( upperdate.IsValid() ) || ( ( lowerdate.IsValid() ) ? ( upperdate >= lowerdate ) : true ) ) )
608 {
609 m_lowdate = lowerdate;
610 m_highdate = upperdate;
611 }
612 else
613 {
614 retval = false;
615 }
616
617 return retval;
618}
619
620// ----------------------------------------------------------------------------
621// date helpers
622// ----------------------------------------------------------------------------
623
624wxDateTime wxCalendarCtrl::GetStartDate() const
625{
626 wxDateTime::Tm tm = m_date.GetTm();
627
628 wxDateTime date = wxDateTime(1, tm.mon, tm.year);
629
630 // rewind back
631 date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
632 ? wxDateTime::Mon : wxDateTime::Sun);
633
634 if ( GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS )
635 {
636 // We want to offset the calendar if we start on the first..
637 if ( date.GetDay() == 1 )
638 {
639 date -= wxDateSpan::Week();
640 }
641 }
642
643 return date;
644}
645
646bool wxCalendarCtrl::IsDateShown(const wxDateTime& date) const
647{
648 if ( !(GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
649 {
650 return date.GetMonth() == m_date.GetMonth();
651 }
652 else
653 {
654 return true;
655 }
656}
657
658bool wxCalendarCtrl::IsDateInRange(const wxDateTime& date) const
659{
660 // Check if the given date is in the range specified
661 return ( ( ( m_lowdate.IsValid() ) ? ( date >= m_lowdate ) : true )
662 && ( ( m_highdate.IsValid() ) ? ( date <= m_highdate ) : true ) );
663}
664
665bool wxCalendarCtrl::ChangeYear(wxDateTime* target) const
666{
667 bool retval = false;
668
669 if ( !(IsDateInRange(*target)) )
670 {
671 if ( target->GetYear() < m_date.GetYear() )
672 {
673 if ( target->GetYear() >= GetLowerDateLimit().GetYear() )
674 {
675 *target = GetLowerDateLimit();
676 retval = true;
677 }
678 else
679 {
680 *target = m_date;
681 }
682 }
683 else
684 {
685 if ( target->GetYear() <= GetUpperDateLimit().GetYear() )
686 {
687 *target = GetUpperDateLimit();
688 retval = true;
689 }
690 else
691 {
692 *target = m_date;
693 }
694 }
695 }
696 else
697 {
698 retval = true;
699 }
700
701 return retval;
702}
703
704bool wxCalendarCtrl::ChangeMonth(wxDateTime* target) const
705{
706 bool retval = true;
707
708 if ( !(IsDateInRange(*target)) )
709 {
710 retval = false;
711
712 if ( target->GetMonth() < m_date.GetMonth() )
713 {
714 *target = GetLowerDateLimit();
715 }
716 else
717 {
718 *target = GetUpperDateLimit();
719 }
720 }
721
722 return retval;
723}
724
725size_t wxCalendarCtrl::GetWeek(const wxDateTime& date) const
726{
727 size_t retval = date.GetWeekOfMonth(GetWindowStyle() & wxCAL_MONDAY_FIRST
728 ? wxDateTime::Monday_First
729 : wxDateTime::Sunday_First);
730
731 if ( (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) )
732 {
733 // we need to offset an extra week if we "start" on the 1st of the month
734 wxDateTime::Tm tm = date.GetTm();
735
736 wxDateTime datetest = wxDateTime(1, tm.mon, tm.year);
737
738 // rewind back
739 datetest.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST
740 ? wxDateTime::Mon : wxDateTime::Sun);
741
742 if ( datetest.GetDay() == 1 )
743 {
744 retval += 1;
745 }
746 }
747
748 return retval;
749}
750
751// ----------------------------------------------------------------------------
752// size management
753// ----------------------------------------------------------------------------
754
755// this is a composite control and it must arrange its parts each time its
756// size or position changes: the combobox and spinctrl are along the top of
757// the available area and the calendar takes up therest of the space
758
759// the static controls are supposed to be always smaller than combo/spin so we
760// always use the latter for size calculations and position the static to take
761// the same space
762
763// the constants used for the layout
764#define VERT_MARGIN 5 // distance between combo and calendar
765#ifdef __WXMAC__
766#define HORZ_MARGIN 5 // spin
767#else
768#define HORZ_MARGIN 15 // spin
769#endif
770wxSize wxCalendarCtrl::DoGetBestSize() const
771{
772 // calc the size of the calendar
773 ((wxCalendarCtrl *)this)->RecalcGeometry(); // const_cast
774
775 wxCoord width = 7*m_widthCol,
776 height = 7*m_heightRow + m_rowOffset + VERT_MARGIN;
777
778 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
779 {
780 // the combobox doesn't report its height correctly (it returns the
781 // height including the drop down list) so don't use it
782 height += m_spinYear->GetBestSize().y;
783 }
784
785 if ( !HasFlag(wxBORDER_NONE) )
786 {
787 // the border would clip the last line otherwise
788 height += 6;
789 width += 4;
790 }
791
792 wxSize best(width, height);
793 CacheBestSize(best);
794 return best;
795}
796
797void wxCalendarCtrl::DoSetSize(int x, int y,
798 int width, int height,
799 int sizeFlags)
800{
801 wxControl::DoSetSize(x, y, width, height, sizeFlags);
802}
803
804void wxCalendarCtrl::DoMoveWindow(int x, int y, int width, int height)
805{
806 int yDiff;
807
808 if ( !HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
809 {
810 wxSize sizeCombo = m_comboMonth->GetSize();
811 wxSize sizeStatic = m_staticMonth->GetSize();
812 wxSize sizeSpin = m_spinYear->GetSize();
813 int dy = (sizeCombo.y - sizeStatic.y) / 2;
814/*
815In the calender the size of the combobox for the year
816is just defined by a margin from the month combobox to
817the left border. While in wxUniv the year control can't
818show all 4 digits, in wxMsw it show almost twice as
819much. Instead the year should use it's best size and be
820left aligned to the calendar. Just in case the month in
821any language is longer than it has space in the
822calendar it is shortend.This way the year always can
823show the 4 digits.
824
825This patch relies on the fact that a combobox has a
826good best size implementation. This is not the case
827with wxMSW but I don't know why.
828
829Otto Wyss
830*/
831
832#ifdef __WXUNIVERSAL__
833 if (sizeCombo.x + HORZ_MARGIN - sizeSpin.x > width)
834 {
835 m_comboMonth->SetSize(x, y, width - HORZ_MARGIN - sizeSpin.x, sizeCombo.y);
836 }
837 else
838 {
839 m_comboMonth->Move(x, y);
840 }
841 m_staticMonth->Move(x, y + dy);
842 m_spinYear->Move(x + width - sizeSpin.x, y);
843 m_staticYear->Move(x + width - sizeSpin.x, y + dy);
844#else
845 m_comboMonth->Move(x, y);
846 m_staticMonth->SetSize(x, y + dy, sizeCombo.x, sizeStatic.y);
847
848 int xDiff = sizeCombo.x + HORZ_MARGIN;
849
850 m_spinYear->SetSize(x + xDiff, y, width - xDiff, sizeCombo.y);
851 m_staticYear->SetSize(x + xDiff, y + dy, width - xDiff, sizeStatic.y);
852#endif
853 yDiff = wxMax(sizeSpin.y, sizeCombo.y) + VERT_MARGIN;
854 }
855 else // no controls on the top
856 {
857 yDiff = 0;
858 }
859
860 wxControl::DoMoveWindow(x, y + yDiff, width, height - yDiff);
861}
862
863void wxCalendarCtrl::DoGetPosition(int *x, int *y) const
864{
865 wxControl::DoGetPosition(x, y);
866
867 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
868 {
869 // our real top corner is not in this position
870 if ( y )
871 {
872 *y -= GetMonthControl()->GetSize().y + VERT_MARGIN;
873 }
874 }
875}
876
877void wxCalendarCtrl::DoGetSize(int *width, int *height) const
878{
879 wxControl::DoGetSize(width, height);
880
881 if ( !(GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
882 {
883 // our real height is bigger
884 if ( height && GetMonthControl())
885 {
886 *height += GetMonthControl()->GetSize().y + VERT_MARGIN;
887 }
888 }
889}
890
891void wxCalendarCtrl::RecalcGeometry()
892{
893 wxClientDC dc(this);
894
895 dc.SetFont(GetFont());
896
897 // determine the column width (we assume that the weekday names are always
898 // wider (in any language) than the numbers)
899 m_widthCol = 0;
900 wxDateTime::WeekDay wd;
901 for ( wd = wxDateTime::Sun; wd < wxDateTime::Inv_WeekDay; wxNextWDay(wd) )
902 {
903 wxCoord width;
904 dc.GetTextExtent(m_weekdays[wd], &width, &m_heightRow);
905 if ( width > m_widthCol )
906 {
907 m_widthCol = width;
908 }
909 }
910
911 // leave some margins
912 m_widthCol += 2;
913 m_heightRow += 2;
914
915 m_rowOffset = (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) ? m_heightRow : 0; // conditional in relation to style
916}
917
918// ----------------------------------------------------------------------------
919// drawing
920// ----------------------------------------------------------------------------
921
922void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event))
923{
924 wxPaintDC dc(this);
925
926 dc.SetFont(GetFont());
927
928 RecalcGeometry();
929
930#if DEBUG_PAINT
931 wxLogDebug("--- starting to paint, selection: %s, week %u\n",
932 m_date.Format("%a %d-%m-%Y %H:%M:%S").c_str(),
933 GetWeek(m_date));
934#endif
935
936 wxCoord y = 0;
937
938 if ( HasFlag(wxCAL_SEQUENTIAL_MONTH_SELECTION) )
939 {
940 // draw the sequential month-selector
941
942 dc.SetBackgroundMode(wxTRANSPARENT);
943 dc.SetTextForeground(*wxBLACK);
944 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
945 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
946 dc.DrawRectangle(0, y, GetClientSize().x, m_heightRow);
947
948 // Get extent of month-name + year
949 wxCoord monthw, monthh;
950 wxString headertext = m_date.Format(wxT("%B %Y"));
951 dc.GetTextExtent(headertext, &monthw, &monthh);
952
953 // draw month-name centered above weekdays
954 wxCoord monthx = ((m_widthCol * 7) - monthw) / 2;
955 wxCoord monthy = ((m_heightRow - monthh) / 2) + y;
956 dc.DrawText(headertext, monthx, monthy);
957
958 // calculate the "month-arrows"
959 wxPoint leftarrow[3];
960 wxPoint rightarrow[3];
961
962 int arrowheight = monthh / 2;
963
964 leftarrow[0] = wxPoint(0, arrowheight / 2);
965 leftarrow[1] = wxPoint(arrowheight / 2, 0);
966 leftarrow[2] = wxPoint(arrowheight / 2, arrowheight - 1);
967
968 rightarrow[0] = wxPoint(0, 0);
969 rightarrow[1] = wxPoint(arrowheight / 2, arrowheight / 2);
970 rightarrow[2] = wxPoint(0, arrowheight - 1);
971
972 // draw the "month-arrows"
973
974 wxCoord arrowy = (m_heightRow - arrowheight) / 2;
975 wxCoord larrowx = (m_widthCol - (arrowheight / 2)) / 2;
976 wxCoord rarrowx = ((m_widthCol - (arrowheight / 2)) / 2) + m_widthCol*6;
977 m_leftArrowRect = wxRect(0, 0, 0, 0);
978 m_rightArrowRect = wxRect(0, 0, 0, 0);
979
980 if ( AllowMonthChange() )
981 {
982 wxDateTime ldpm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) - wxDateSpan::Day(); // last day prev month
983 // Check if range permits change
984 if ( IsDateInRange(ldpm) && ( ( ldpm.GetYear() == m_date.GetYear() ) ? true : AllowYearChange() ) )
985 {
986 m_leftArrowRect = wxRect(larrowx - 3, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
987 dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
988 dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
989 dc.DrawPolygon(3, leftarrow, larrowx , arrowy, wxWINDING_RULE);
990 dc.SetBrush(*wxTRANSPARENT_BRUSH);
991 dc.DrawRectangle(m_leftArrowRect);
992 }
993 wxDateTime fdnm = wxDateTime(1,m_date.GetMonth(), m_date.GetYear()) + wxDateSpan::Month(); // first day next month
994 if ( IsDateInRange(fdnm) && ( ( fdnm.GetYear() == m_date.GetYear() ) ? true : AllowYearChange() ) )
995 {
996 m_rightArrowRect = wxRect(rarrowx - 4, arrowy - 3, (arrowheight / 2) + 8, (arrowheight + 6));
997 dc.SetBrush(wxBrush(*wxBLACK, wxSOLID));
998 dc.SetPen(wxPen(*wxBLACK, 1, wxSOLID));
999 dc.DrawPolygon(3, rightarrow, rarrowx , arrowy, wxWINDING_RULE);
1000 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1001 dc.DrawRectangle(m_rightArrowRect);
1002 }
1003 }
1004
1005 y += m_heightRow;
1006 }
1007
1008 // first draw the week days
1009 if ( IsExposed(0, y, 7*m_widthCol, m_heightRow) )
1010 {
1011#if DEBUG_PAINT
1012 wxLogDebug("painting the header");
1013#endif
1014
1015 dc.SetBackgroundMode(wxTRANSPARENT);
1016 dc.SetTextForeground(m_colHeaderFg);
1017 dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID));
1018 dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID));
1019 dc.DrawRectangle(0, y, GetClientSize().x, m_heightRow);
1020
1021 bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0;
1022 for ( size_t wd = 0; wd < 7; wd++ )
1023 {
1024 size_t n;
1025 if ( startOnMonday )
1026 n = wd == 6 ? 0 : wd + 1;
1027 else
1028 n = wd;
1029 wxCoord dayw, dayh;
1030 dc.GetTextExtent(m_weekdays[n], &dayw, &dayh);
1031 dc.DrawText(m_weekdays[n], (wd*m_widthCol) + ((m_widthCol- dayw) / 2), y); // center the day-name
1032 }
1033 }
1034
1035 // then the calendar itself
1036 dc.SetTextForeground(*wxBLACK);
1037 //dc.SetFont(*wxNORMAL_FONT);
1038
1039 y += m_heightRow;
1040 wxDateTime date = GetStartDate();
1041
1042#if DEBUG_PAINT
1043 wxLogDebug("starting calendar from %s\n",
1044 date.Format("%a %d-%m-%Y %H:%M:%S").c_str());
1045#endif
1046
1047 dc.SetBackgroundMode(wxSOLID);
1048 for ( size_t nWeek = 1; nWeek <= 6; nWeek++, y += m_heightRow )
1049 {
1050 // if the update region doesn't intersect this row, don't paint it
1051 if ( !IsExposed(0, y, 7*m_widthCol, m_heightRow - 1) )
1052 {
1053 date += wxDateSpan::Week();
1054
1055 continue;
1056 }
1057
1058#if DEBUG_PAINT
1059 wxLogDebug("painting week %d at y = %d\n", nWeek, y);
1060#endif
1061
1062 for ( size_t wd = 0; wd < 7; wd++ )
1063 {
1064 if ( IsDateShown(date) )
1065 {
1066 // don't use wxDate::Format() which prepends 0s
1067 unsigned int day = date.GetDay();
1068 wxString dayStr = wxString::Format(_T("%u"), day);
1069 wxCoord width;
1070 dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL);
1071
1072 bool changedColours = false,
1073 changedFont = false;
1074
1075 bool isSel = false;
1076 wxCalendarDateAttr *attr = NULL;
1077
1078 if ( date.GetMonth() != m_date.GetMonth() || !IsDateInRange(date) )
1079 {
1080 // surrounding week or out-of-range
1081 // draw "disabled"
1082 dc.SetTextForeground(*wxLIGHT_GREY);
1083 changedColours = true;
1084 }
1085 else
1086 {
1087 isSel = date.IsSameDate(m_date);
1088 attr = m_attrs[day - 1];
1089
1090 if ( isSel )
1091 {
1092 dc.SetTextForeground(m_colHighlightFg);
1093 dc.SetTextBackground(m_colHighlightBg);
1094
1095 changedColours = true;
1096 }
1097 else if ( attr )
1098 {
1099 wxColour colFg, colBg;
1100
1101 if ( attr->IsHoliday() )
1102 {
1103 colFg = m_colHolidayFg;
1104 colBg = m_colHolidayBg;
1105 }
1106 else
1107 {
1108 colFg = attr->GetTextColour();
1109 colBg = attr->GetBackgroundColour();
1110 }
1111
1112 if ( colFg.Ok() )
1113 {
1114 dc.SetTextForeground(colFg);
1115 changedColours = true;
1116 }
1117
1118 if ( colBg.Ok() )
1119 {
1120 dc.SetTextBackground(colBg);
1121 changedColours = true;
1122 }
1123
1124 if ( attr->HasFont() )
1125 {
1126 dc.SetFont(attr->GetFont());
1127 changedFont = true;
1128 }
1129 }
1130 }
1131
1132 wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2;
1133 dc.DrawText(dayStr, x, y + 1);
1134
1135 if ( !isSel && attr && attr->HasBorder() )
1136 {
1137 wxColour colBorder;
1138 if ( attr->HasBorderColour() )
1139 {
1140 colBorder = attr->GetBorderColour();
1141 }
1142 else
1143 {
1144 colBorder = GetForegroundColour();
1145 }
1146
1147 wxPen pen(colBorder, 1, wxSOLID);
1148 dc.SetPen(pen);
1149 dc.SetBrush(*wxTRANSPARENT_BRUSH);
1150
1151 switch ( attr->GetBorder() )
1152 {
1153 case wxCAL_BORDER_SQUARE:
1154 dc.DrawRectangle(x - 2, y,
1155 width + 4, m_heightRow);
1156 break;
1157
1158 case wxCAL_BORDER_ROUND:
1159 dc.DrawEllipse(x - 2, y,
1160 width + 4, m_heightRow);
1161 break;
1162
1163 default:
1164 wxFAIL_MSG(_T("unknown border type"));
1165 }
1166 }
1167
1168 if ( changedColours )
1169 {
1170 dc.SetTextForeground(GetForegroundColour());
1171 dc.SetTextBackground(GetBackgroundColour());
1172 }
1173
1174 if ( changedFont )
1175 {
1176 dc.SetFont(GetFont());
1177 }
1178 }
1179 //else: just don't draw it
1180
1181 date += wxDateSpan::Day();
1182 }
1183 }
1184
1185 // Greying out out-of-range background
1186 bool showSurrounding = (GetWindowStyle() & wxCAL_SHOW_SURROUNDING_WEEKS) != 0;
1187
1188 date = ( showSurrounding ) ? GetStartDate() : wxDateTime(1, m_date.GetMonth(), m_date.GetYear());
1189 if ( !IsDateInRange(date) )
1190 {
1191 wxDateTime firstOOR = GetLowerDateLimit() - wxDateSpan::Day(); // first out-of-range
1192
1193 wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
1194 oorbrush.SetStyle(wxFDIAGONAL_HATCH);
1195
1196 HighlightRange(&dc, date, firstOOR, wxTRANSPARENT_PEN, &oorbrush);
1197 }
1198
1199 date = ( showSurrounding ) ? GetStartDate() + wxDateSpan::Weeks(6) - wxDateSpan::Day() : wxDateTime().SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear());
1200 if ( !IsDateInRange(date) )
1201 {
1202 wxDateTime firstOOR = GetUpperDateLimit() + wxDateSpan::Day(); // first out-of-range
1203
1204 wxBrush oorbrush = *wxLIGHT_GREY_BRUSH;
1205 oorbrush.SetStyle(wxFDIAGONAL_HATCH);
1206
1207 HighlightRange(&dc, firstOOR, date, wxTRANSPARENT_PEN, &oorbrush);
1208 }
1209
1210#if DEBUG_PAINT
1211 wxLogDebug("+++ finished painting");
1212#endif
1213}
1214
1215void wxCalendarCtrl::RefreshDate(const wxDateTime& date)
1216{
1217 RecalcGeometry();
1218
1219 wxRect rect;
1220
1221 // always refresh the whole row at once because our OnPaint() will draw
1222 // the whole row anyhow - and this allows the small optimisation in
1223 // OnClick() below to work
1224 rect.x = 0;
1225
1226 rect.y = (m_heightRow * GetWeek(date)) + m_rowOffset;
1227
1228 rect.width = 7*m_widthCol;
1229 rect.height = m_heightRow;
1230
1231#ifdef __WXMSW__
1232 // VZ: for some reason, the selected date seems to occupy more space under
1233 // MSW - this is probably some bug in the font size calculations, but I
1234 // don't know where exactly. This fix is ugly and leads to more
1235 // refreshes than really needed, but without it the selected days
1236 // leaves even more ugly underscores on screen.
1237 rect.Inflate(0, 1);
1238#endif // MSW
1239
1240#if DEBUG_PAINT
1241 wxLogDebug("*** refreshing week %d at (%d, %d)-(%d, %d)\n",
1242 GetWeek(date),
1243 rect.x, rect.y,
1244 rect.x + rect.width, rect.y + rect.height);
1245#endif
1246
1247 Refresh(true, &rect);
1248}
1249
1250void wxCalendarCtrl::HighlightRange(wxPaintDC* pDC, const wxDateTime& fromdate, const wxDateTime& todate, wxPen* pPen, wxBrush* pBrush)
1251{
1252 // Highlights the given range using pen and brush
1253 // Does nothing if todate < fromdate
1254
1255
1256#if DEBUG_PAINT
1257 wxLogDebug("+++ HighlightRange: (%s) - (%s) +++", fromdate.Format("%d %m %Y"), todate.Format("%d %m %Y"));
1258#endif
1259
1260 if ( todate >= fromdate )
1261 {
1262 // do stuff
1263 // date-coordinates
1264 int fd, fw;
1265 int td, tw;
1266
1267 // implicit: both dates must be currently shown - checked by GetDateCoord
1268 if ( GetDateCoord(fromdate, &fd, &fw) && GetDateCoord(todate, &td, &tw) )
1269 {
1270#if DEBUG_PAINT
1271 wxLogDebug("Highlight range: (%i, %i) - (%i, %i)", fd, fw, td, tw);
1272#endif
1273 if ( ( (tw - fw) == 1 ) && ( td < fd ) )
1274 {
1275 // special case: interval 7 days or less not in same week
1276 // split in two seperate intervals
1277 wxDateTime tfd = fromdate + wxDateSpan::Days(7-fd);
1278 wxDateTime ftd = tfd + wxDateSpan::Day();
1279#if DEBUG_PAINT
1280 wxLogDebug("Highlight: Seperate segments");
1281#endif
1282 // draw seperately
1283 HighlightRange(pDC, fromdate, tfd, pPen, pBrush);
1284 HighlightRange(pDC, ftd, todate, pPen, pBrush);
1285 }
1286 else
1287 {
1288 int numpoints;
1289 wxPoint corners[8]; // potentially 8 corners in polygon
1290
1291 if ( fw == tw )
1292 {
1293 // simple case: same week
1294 numpoints = 4;
1295 corners[0] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset);
1296 corners[1] = wxPoint((fd - 1) * m_widthCol, ((fw + 1 ) * m_heightRow) + m_rowOffset);
1297 corners[2] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset);
1298 corners[3] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset);
1299 }
1300 else
1301 {
1302 int cidx = 0;
1303 // "complex" polygon
1304 corners[cidx] = wxPoint((fd - 1) * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
1305
1306 if ( fd > 1 )
1307 {
1308 corners[cidx] = wxPoint((fd - 1) * m_widthCol, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
1309 corners[cidx] = wxPoint(0, ((fw + 1) * m_heightRow) + m_rowOffset); cidx++;
1310 }
1311
1312 corners[cidx] = wxPoint(0, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
1313 corners[cidx] = wxPoint(td * m_widthCol, ((tw + 1) * m_heightRow) + m_rowOffset); cidx++;
1314
1315 if ( td < 7 )
1316 {
1317 corners[cidx] = wxPoint(td * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
1318 corners[cidx] = wxPoint(7 * m_widthCol, (tw * m_heightRow) + m_rowOffset); cidx++;
1319 }
1320
1321 corners[cidx] = wxPoint(7 * m_widthCol, (fw * m_heightRow) + m_rowOffset); cidx++;
1322
1323 numpoints = cidx;
1324 }
1325
1326 // draw the polygon
1327 pDC->SetBrush(*pBrush);
1328 pDC->SetPen(*pPen);
1329 pDC->DrawPolygon(numpoints, corners);
1330 }
1331 }
1332 }
1333 // else do nothing
1334#if DEBUG_PAINT
1335 wxLogDebug("--- HighlightRange ---");
1336#endif
1337}
1338
1339bool wxCalendarCtrl::GetDateCoord(const wxDateTime& date, int *day, int *week) const
1340{
1341 bool retval = true;
1342
1343#if DEBUG_PAINT
1344 wxLogDebug("+++ GetDateCoord: (%s) +++", date.Format("%d %m %Y"));
1345#endif
1346
1347 if ( IsDateShown(date) )
1348 {
1349 bool startOnMonday = ( GetWindowStyle() & wxCAL_MONDAY_FIRST ) != 0;
1350
1351 // Find day
1352 *day = date.GetWeekDay();
1353
1354 if ( *day == 0 ) // sunday
1355 {
1356 *day = ( startOnMonday ) ? 7 : 1;
1357 }
1358 else
1359 {
1360 *day += ( startOnMonday ) ? 0 : 1;
1361 }
1362
1363 int targetmonth = date.GetMonth() + (12 * date.GetYear());
1364 int thismonth = m_date.GetMonth() + (12 * m_date.GetYear());
1365
1366 // Find week
1367 if ( targetmonth == thismonth )
1368 {
1369 *week = GetWeek(date);
1370 }
1371 else
1372 {
1373 if ( targetmonth < thismonth )
1374 {
1375 *week = 1; // trivial
1376 }
1377 else // targetmonth > thismonth
1378 {
1379 wxDateTime ldcm;
1380 int lastweek;
1381 int lastday;
1382
1383 // get the datecoord of the last day in the month currently shown
1384#if DEBUG_PAINT
1385 wxLogDebug(" +++ LDOM +++");
1386#endif
1387 GetDateCoord(ldcm.SetToLastMonthDay(m_date.GetMonth(), m_date.GetYear()), &lastday, &lastweek);
1388#if DEBUG_PAINT
1389 wxLogDebug(" --- LDOM ---");
1390#endif
1391
1392 wxTimeSpan span = date - ldcm;
1393
1394 int daysfromlast = span.GetDays();
1395#if DEBUG_PAINT
1396 wxLogDebug("daysfromlast: %i", daysfromlast);
1397#endif
1398 if ( daysfromlast + lastday > 7 ) // past week boundary
1399 {
1400 int wholeweeks = (daysfromlast / 7);
1401 *week = wholeweeks + lastweek;
1402 if ( (daysfromlast - (7 * wholeweeks) + lastday) > 7 )
1403 {
1404 *week += 1;
1405 }
1406 }
1407 else
1408 {
1409 *week = lastweek;
1410 }
1411 }
1412 }
1413 }
1414 else
1415 {
1416 *day = -1;
1417 *week = -1;
1418 retval = false;
1419 }
1420
1421#if DEBUG_PAINT
1422 wxLogDebug("--- GetDateCoord: (%s) = (%i, %i) ---", date.Format("%d %m %Y"), *day, *week);
1423#endif
1424
1425 return retval;
1426}
1427
1428// ----------------------------------------------------------------------------
1429// mouse handling
1430// ----------------------------------------------------------------------------
1431
1432void wxCalendarCtrl::OnDClick(wxMouseEvent& event)
1433{
1434 if ( HitTest(event.GetPosition()) != wxCAL_HITTEST_DAY )
1435 {
1436 event.Skip();
1437 }
1438 else
1439 {
1440 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
1441 }
1442}
1443
1444void wxCalendarCtrl::OnClick(wxMouseEvent& event)
1445{
1446 wxDateTime date;
1447 wxDateTime::WeekDay wday;
1448 switch ( HitTest(event.GetPosition(), &date, &wday) )
1449 {
1450 case wxCAL_HITTEST_DAY:
1451 if ( IsDateInRange(date) )
1452 {
1453 ChangeDay(date);
1454
1455 GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED,
1456 wxEVT_CALENDAR_SEL_CHANGED);
1457 }
1458 break;
1459
1460 case wxCAL_HITTEST_HEADER:
1461 {
1462 wxCalendarEvent event(this, wxEVT_CALENDAR_WEEKDAY_CLICKED);
1463 event.m_wday = wday;
1464 (void)GetEventHandler()->ProcessEvent(event);
1465 }
1466 break;
1467
1468 case wxCAL_HITTEST_DECMONTH:
1469 case wxCAL_HITTEST_INCMONTH:
1470 case wxCAL_HITTEST_SURROUNDING_WEEK:
1471 SetDateAndNotify(date); // we probably only want to refresh the control. No notification.. (maybe as an option?)
1472 break;
1473
1474 default:
1475 wxFAIL_MSG(_T("unknown hittest code"));
1476 // fall through
1477
1478 case wxCAL_HITTEST_NOWHERE:
1479 event.Skip();
1480 break;
1481 }
1482}
1483
1484wxCalendarHitTestResult wxCalendarCtrl::HitTest(const wxPoint& pos,
1485 wxDateTime *date,
1486 wxDateTime::WeekDay *wd)
1487{
1488 RecalcGeometry();
1489
1490 wxCoord y = pos.y;
1491
1492///////////////////////////////////////////////////////////////////////////////////////////////////////
1493 if ( (GetWindowStyle() & wxCAL_SEQUENTIAL_MONTH_SELECTION) )
1494 {
1495 // Header: month
1496
1497 // we need to find out if the hit is on left arrow, on month or on right arrow
1498 // left arrow?
1499 if ( wxRegion(m_leftArrowRect).Contains(pos) == wxInRegion )
1500 {
1501 if ( date )
1502 {
1503 if ( IsDateInRange(m_date - wxDateSpan::Month()) )
1504 {
1505 *date = m_date - wxDateSpan::Month();
1506 }
1507 else
1508 {
1509 *date = GetLowerDateLimit();
1510 }
1511 }
1512
1513 return wxCAL_HITTEST_DECMONTH;
1514 }
1515
1516 if ( wxRegion(m_rightArrowRect).Contains(pos) == wxInRegion )
1517 {
1518 if ( date )
1519 {
1520 if ( IsDateInRange(m_date + wxDateSpan::Month()) )
1521 {
1522 *date = m_date + wxDateSpan::Month();
1523 }
1524 else
1525 {
1526 *date = GetUpperDateLimit();
1527 }
1528 }
1529
1530 return wxCAL_HITTEST_INCMONTH;
1531 }
1532
1533 }
1534
1535///////////////////////////////////////////////////////////////////////////////////////////////////////
1536 // Header: Days
1537 int wday = pos.x / m_widthCol;
1538// if ( y < m_heightRow )
1539 if ( y < (m_heightRow + m_rowOffset) )
1540 {
1541 if ( y > m_rowOffset )
1542 {
1543 if ( wd )
1544 {
1545 if ( GetWindowStyle() & wxCAL_MONDAY_FIRST )
1546 {
1547 wday = wday == 6 ? 0 : wday + 1;
1548 }
1549
1550 *wd = (wxDateTime::WeekDay)wday;
1551 }
1552
1553 return wxCAL_HITTEST_HEADER;
1554 }
1555 else
1556 {
1557 return wxCAL_HITTEST_NOWHERE;
1558 }
1559 }
1560
1561// int week = (y - m_heightRow) / m_heightRow;
1562 int week = (y - (m_heightRow + m_rowOffset)) / m_heightRow;
1563 if ( week >= 6 || wday >= 7 )
1564 {
1565 return wxCAL_HITTEST_NOWHERE;
1566 }
1567
1568 wxDateTime dt = GetStartDate() + wxDateSpan::Days(7*week + wday);
1569
1570 if ( IsDateShown(dt) )
1571 {
1572 if ( date )
1573 *date = dt;
1574
1575 if ( dt.GetMonth() == m_date.GetMonth() )
1576 {
1577
1578 return wxCAL_HITTEST_DAY;
1579 }
1580 else
1581 {
1582 return wxCAL_HITTEST_SURROUNDING_WEEK;
1583 }
1584 }
1585 else
1586 {
1587 return wxCAL_HITTEST_NOWHERE;
1588 }
1589}
1590
1591// ----------------------------------------------------------------------------
1592// subcontrols events handling
1593// ----------------------------------------------------------------------------
1594
1595void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event)
1596{
1597 wxDateTime::Tm tm = m_date.GetTm();
1598
1599 wxDateTime::Month mon = (wxDateTime::Month)event.GetInt();
1600 if ( tm.mday > wxDateTime::GetNumberOfDays(mon, tm.year) )
1601 {
1602 tm.mday = wxDateTime::GetNumberOfDays(mon, tm.year);
1603 }
1604
1605 wxDateTime target = wxDateTime(tm.mday, mon, tm.year);
1606
1607 ChangeMonth(&target);
1608 SetDateAndNotify(target);
1609}
1610
1611void wxCalendarCtrl::OnYearChange(wxCommandEvent& event)
1612{
1613 int year = (int)event.GetInt();
1614 if ( year == INT_MIN )
1615 {
1616 // invalid year in the spin control, ignore it
1617 return;
1618 }
1619
1620 wxDateTime::Tm tm = m_date.GetTm();
1621
1622 if ( tm.mday > wxDateTime::GetNumberOfDays(tm.mon, year) )
1623 {
1624 tm.mday = wxDateTime::GetNumberOfDays(tm.mon, year);
1625 }
1626
1627 wxDateTime target = wxDateTime(tm.mday, tm.mon, year);
1628
1629 if ( ChangeYear(&target) )
1630 {
1631 SetDateAndNotify(target);
1632 }
1633 else
1634 {
1635 // In this case we don't want to change the date. That would put us
1636 // inside the same year but a strange number of months forward/back..
1637 m_spinYear->SetValue(target.GetYear());
1638 }
1639}
1640
1641// ----------------------------------------------------------------------------
1642// keyboard interface
1643// ----------------------------------------------------------------------------
1644
1645void wxCalendarCtrl::OnChar(wxKeyEvent& event)
1646{
1647 wxDateTime target;
1648 switch ( event.GetKeyCode() )
1649 {
1650 case _T('+'):
1651 case WXK_ADD:
1652 target = m_date + wxDateSpan::Year();
1653 if ( ChangeYear(&target) )
1654 {
1655 SetDateAndNotify(target);
1656 }
1657 break;
1658
1659 case _T('-'):
1660 case WXK_SUBTRACT:
1661 target = m_date - wxDateSpan::Year();
1662 if ( ChangeYear(&target) )
1663 {
1664 SetDateAndNotify(target);
1665 }
1666 break;
1667
1668 case WXK_PRIOR:
1669 target = m_date - wxDateSpan::Month();
1670 ChangeMonth(&target);
1671 SetDateAndNotify(target); // always
1672 break;
1673
1674 case WXK_NEXT:
1675 target = m_date + wxDateSpan::Month();
1676 ChangeMonth(&target);
1677 SetDateAndNotify(target); // always
1678 break;
1679
1680 case WXK_RIGHT:
1681 if ( event.ControlDown() )
1682 {
1683 target = wxDateTime(m_date).SetToNextWeekDay(
1684 GetWindowStyle() & wxCAL_MONDAY_FIRST
1685 ? wxDateTime::Sun : wxDateTime::Sat);
1686 if ( !IsDateInRange(target) )
1687 {
1688 target = GetUpperDateLimit();
1689 }
1690 SetDateAndNotify(target);
1691 }
1692 else
1693 SetDateAndNotify(m_date + wxDateSpan::Day());
1694 break;
1695
1696 case WXK_LEFT:
1697 if ( event.ControlDown() )
1698 {
1699 target = wxDateTime(m_date).SetToPrevWeekDay(
1700 GetWindowStyle() & wxCAL_MONDAY_FIRST
1701 ? wxDateTime::Mon : wxDateTime::Sun);
1702 if ( !IsDateInRange(target) )
1703 {
1704 target = GetLowerDateLimit();
1705 }
1706 SetDateAndNotify(target);
1707 }
1708 else
1709 SetDateAndNotify(m_date - wxDateSpan::Day());
1710 break;
1711
1712 case WXK_UP:
1713 SetDateAndNotify(m_date - wxDateSpan::Week());
1714 break;
1715
1716 case WXK_DOWN:
1717 SetDateAndNotify(m_date + wxDateSpan::Week());
1718 break;
1719
1720 case WXK_HOME:
1721 if ( event.ControlDown() )
1722 SetDateAndNotify(wxDateTime::Today());
1723 else
1724 SetDateAndNotify(wxDateTime(1, m_date.GetMonth(), m_date.GetYear()));
1725 break;
1726
1727 case WXK_END:
1728 SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay());
1729 break;
1730
1731 case WXK_RETURN:
1732 GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
1733 break;
1734
1735 default:
1736 event.Skip();
1737 }
1738}
1739
1740// ----------------------------------------------------------------------------
1741// holidays handling
1742// ----------------------------------------------------------------------------
1743
1744void wxCalendarCtrl::EnableHolidayDisplay(bool display)
1745{
1746 long style = GetWindowStyle();
1747 if ( display )
1748 style |= wxCAL_SHOW_HOLIDAYS;
1749 else
1750 style &= ~wxCAL_SHOW_HOLIDAYS;
1751
1752 SetWindowStyle(style);
1753
1754 if ( display )
1755 SetHolidayAttrs();
1756 else
1757 ResetHolidayAttrs();
1758
1759 Refresh();
1760}
1761
1762void wxCalendarCtrl::SetHolidayAttrs()
1763{
1764 if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS )
1765 {
1766 ResetHolidayAttrs();
1767
1768 wxDateTime::Tm tm = m_date.GetTm();
1769 wxDateTime dtStart(1, tm.mon, tm.year),
1770 dtEnd = dtStart.GetLastMonthDay();
1771
1772 wxDateTimeArray hol;
1773 wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol);
1774
1775 size_t count = hol.GetCount();
1776 for ( size_t n = 0; n < count; n++ )
1777 {
1778 SetHoliday(hol[n].GetDay());
1779 }
1780 }
1781}
1782
1783void wxCalendarCtrl::SetHoliday(size_t day)
1784{
1785 wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") );
1786
1787 wxCalendarDateAttr *attr = GetAttr(day);
1788 if ( !attr )
1789 {
1790 attr = new wxCalendarDateAttr;
1791 }
1792
1793 attr->SetHoliday(true);
1794
1795 // can't use SetAttr() because it would delete this pointer
1796 m_attrs[day - 1] = attr;
1797}
1798
1799void wxCalendarCtrl::ResetHolidayAttrs()
1800{
1801 for ( size_t day = 0; day < 31; day++ )
1802 {
1803 if ( m_attrs[day] )
1804 {
1805 m_attrs[day]->SetHoliday(false);
1806 }
1807 }
1808}
1809
1810
1811//static
1812wxVisualAttributes
1813wxCalendarCtrl::GetClassDefaultAttributes(wxWindowVariant variant)
1814{
1815 // Use the same color scheme as wxListBox
1816 return wxListBox::GetClassDefaultAttributes(variant);
1817}
1818
1819
1820// ----------------------------------------------------------------------------
1821// wxCalendarEvent
1822// ----------------------------------------------------------------------------
1823
1824void wxCalendarEvent::Init()
1825{
1826 m_wday = wxDateTime::Inv_WeekDay;
1827}
1828
1829wxCalendarEvent::wxCalendarEvent(wxCalendarCtrl *cal, wxEventType type)
1830 : wxCommandEvent(type, cal->GetId())
1831{
1832 m_date = cal->GetDate();
1833 SetEventObject(cal);
1834}
1835
1836#endif // wxUSE_CALENDARCTRL
1837