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