From 4f6aed9c6a21d4bc732579aa564adf4cdb2c9aab Mon Sep 17 00:00:00 2001 From: Vadim Zeitlin Date: Sun, 2 Jan 2000 23:17:55 +0000 Subject: [PATCH] 1. wxDateTimeHolidayAuthority class for calculating the holidays added 2. wxCalendarCtrl adjustments and some new features git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@5182 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- docs/changes.txt | 9 ++ docs/latex/wx/array.tex | 13 +- docs/latex/wx/calctrl.tex | 22 ++++ docs/latex/wx/tsamples.tex | 6 + include/wx/calctrl.h | 94 ++++++++++++++- include/wx/datetime.h | 112 +++++++++++++++-- include/wx/datetime.inl | 69 ++++++++++- include/wx/dynarray.h | 10 ++ include/wx/generic/calctrl.h | 110 +++++++++++++++-- src/common/datetime.cpp | 207 +++++++++++++++++++++++++++++++- src/generic/calctrl.cpp | 225 ++++++++++++++++++++++++++++++----- 11 files changed, 817 insertions(+), 60 deletions(-) create mode 100644 docs/latex/wx/calctrl.tex diff --git a/docs/changes.txt b/docs/changes.txt index b4f2733bc5..84dd1cf743 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -6,6 +6,11 @@ next release (2.1.12 or 2.2?) wxBase: +- wxDateTime replaces and extends old wxDate and wxTime classes (still + available but strongly deprecated) with many new features + +- wxLongLong class provides support for (signed) 64 bit integers + - it is now possible to build wxBase under Win32 (using VC++ only so far) and BeOS (without thread support yet) @@ -22,10 +27,14 @@ wxBase: - wxArray::RemoveAt() replaces deprectaed wxArray::Remove(index) all (GUI): + +- new wxCalendarCtrl class for picking a date interactively + - wxMenu(Bar)::Insert() and Remove() functions for dynamic menu menagament - wxToolBar supports arbitrary controls (not only buttons) and can be dynamically changed (Delete/Insert functions) - vertical toolbars supported by MSW and GTK native wxToolBar classes + - wxTreeCtrl and wxListCtrl allow setting colour/fonts for individual items - "file open" dialog allows selecting multiple files at once (contributed by John Norris) diff --git a/docs/latex/wx/array.tex b/docs/latex/wx/array.tex index 9142a69eb9..e449ca6ad8 100644 --- a/docs/latex/wx/array.tex +++ b/docs/latex/wx/array.tex @@ -193,7 +193,8 @@ does exactly the same as \helpref{Item()}{wxarrayitem} method. \membersection{Adding items} \helpref{Add}{wxarrayadd}\\ -\helpref{Insert}{wxarrayinsert} +\helpref{Insert}{wxarrayinsert}\\ +\helpref{WX\_APPEND\_ARRAY}{wxappendarray} \membersection{Removing items} @@ -311,6 +312,13 @@ public: WX_DEFINE_OBJARRAY(wxArrayOfMyClass); \end{verbatim} +\membersection{WX\_APPEND\_ARRAY}\label{wxappendarray} + +\func{void}{WX\_APPEND\_ARRAY}{\param{wxArray\& }{array}, \param{wxArray\& }{other}} + +This macro may be used to append all elements of the {\it other} array to the +{\it array}. The two arrays must be of the same type. + \membersection{WX\_CLEAR\_ARRAY}\label{wxcleararray} \func{void}{WX\_CLEAR\_ARRAY}{\param{wxArray\& }{array}} @@ -385,6 +393,9 @@ to the array, however, the array will make a copy of the item and will not take ownership of the original item. Once again, it only makes sense for wxObjArrays because the other array types never take ownership of their elements. +You may also use \helpref{WX\_APPEND\_ARRAY}{wxappendarray} macro to append all +elements of one array to another one. + \membersection{wxArray::Alloc}\label{wxarrayalloc} \func{void}{Alloc}{\param{size\_t }{count}} diff --git a/docs/latex/wx/calctrl.tex b/docs/latex/wx/calctrl.tex new file mode 100644 index 0000000000..c3e828748a --- /dev/null +++ b/docs/latex/wx/calctrl.tex @@ -0,0 +1,22 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Name: calctrl.tex +%% Purpose: wxCalendarCtrl documentation +%% Author: Vadim Zeitlin +%% Modified by: +%% Created: 03.01.00 +%% RCS-ID: $Id$ +%% Copyright: (c) Vadim Zeitlin +%% Licence: wxWindows licence +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\section{\class{wxCalendarCtrl}}\label{wxcalendarctrl} + +The calendar control allows the user to pick a date interactively. + +\wxheading{Include files} + + + +\wxheading{See also:} + +\helpref{Calendar sample}{samplecalendar} diff --git a/docs/latex/wx/tsamples.tex b/docs/latex/wx/tsamples.tex index 6d1974e513..a94b0b3184 100644 --- a/docs/latex/wx/tsamples.tex +++ b/docs/latex/wx/tsamples.tex @@ -19,6 +19,12 @@ make it easier to find the relevant one if a simple grep through all sources didn't help. They also provide some notes about using the samples and what features of wxWindows are they supposed to test. +\subsection{Calendar sample}\label{samplecalendar} + +This font shows the \helpref{calendar control}{wxcalendarctrl} in action. It +shows how to configure the control (see the different options in the calendar +menu) and also how to process the notifications from it. + \subsection{Checklist sample}\label{samplechecklist} This sample demonstrates the use of the \helpref{wxCheckListBox}{wxchecklistbox} diff --git a/include/wx/calctrl.h b/include/wx/calctrl.h index 3fe059bea3..94e7e2002c 100644 --- a/include/wx/calctrl.h +++ b/include/wx/calctrl.h @@ -9,9 +9,18 @@ // Licence: wxWindows license /////////////////////////////////////////////////////////////////////////////// +/* + TODO + + 1. implement multiple selections for date ranges + 2. background bitmap for the calendar? + */ + #ifndef _WX_CALCTRL_H #define _WX_CALCTRL_H +#include "wx/datetime.h" + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -24,17 +33,85 @@ enum wxCalendarHitTestResult wxCAL_HITTEST_DAY // on a day in the calendar }; +// border types for a date +enum wxCalendarDateBorder +{ + wxCAL_BORDER_NONE, // no border (default) + wxCAL_BORDER_SQUARE, // a rectangular border + wxCAL_BORDER_ROUND // a round border +}; + // ---------------------------------------------------------------------------- -// wxCalendarCtrl +// wxCalendarDateAttr: custom attributes for a calendar date // ---------------------------------------------------------------------------- -// so far we only have a generic version, so keep it simple -#include "wx/generic/calctrl.h" +class WXDLLEXPORT wxCalendarDateAttr +{ +public: + // ctors + wxCalendarDateAttr() { Init(); } + wxCalendarDateAttr(const wxColour& colText, + const wxColour& colBack = wxNullColour, + const wxColour& colBorder = wxNullColour, + const wxFont& font = wxNullFont, + wxCalendarDateBorder border = wxCAL_BORDER_NONE) + : m_colText(colText), m_colBack(colBack), + m_colBorder(colBorder), m_font(font) + { + Init(border); + } + wxCalendarDateAttr(wxCalendarDateBorder border, + const wxColour& colBorder = wxNullColour) + : m_colBorder(colBorder) + { + Init(border); + } + + // setters + void SetTextColour(const wxColour& colText) { m_colText = colText; } + void SetBackgroundColour(const wxColour& colBack) { m_colBack = colBack; } + void SetBorderColour(const wxColour& col) { m_colBorder = col; } + void SetFont(const wxFont& font) { m_font = font; } + void SetBorder(wxCalendarDateBorder border) { m_border = border; } + void SetHoliday(bool holiday) { m_holiday = holiday; } + + // accessors + bool HasTextColour() const { return m_colText.Ok(); } + bool HasBackgroundColour() const { return m_colBack.Ok(); } + bool HasBorderColour() const { return m_colBorder.Ok(); } + bool HasFont() const { return m_font.Ok(); } + bool HasBorder() const { return m_border != wxCAL_BORDER_NONE; } + + bool IsHoliday() const { return m_holiday; } + + const wxColour& GetTextColour() const { return m_colText; } + const wxColour& GetBackgroundColour() const { return m_colBack; } + const wxColour& GetBorderColour() const { return m_colBorder; } + const wxFont& GetFont() const { return m_font; } + wxCalendarDateBorder GetBorder() const { return m_border; } + +protected: + void Init(wxCalendarDateBorder border = wxCAL_BORDER_NONE) + { + m_border = border; + m_holiday = FALSE; + } + +private: + wxColour m_colText, + m_colBack, + m_colBorder; + wxFont m_font; + wxCalendarDateBorder m_border; + bool m_holiday; +}; // ---------------------------------------------------------------------------- // wxCalendarCtrl events // ---------------------------------------------------------------------------- +class WXDLLEXPORT wxCalendarCtrl; + class WXDLLEXPORT wxCalendarEvent : public wxCommandEvent { friend class wxCalendarCtrl; @@ -53,6 +130,17 @@ private: wxDateTime::WeekDay m_wday; }; +// ---------------------------------------------------------------------------- +// wxCalendarCtrl +// ---------------------------------------------------------------------------- + +// so far we only have a generic version, so keep it simple +#include "wx/generic/calctrl.h" + +// ---------------------------------------------------------------------------- +// calendar events macros +// ---------------------------------------------------------------------------- + #define EVT_CALENDAR(id, fn) { wxEVT_CALENDAR_DOUBLECLICKED, id, -1, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & fn, (wxObject *) NULL }, #define EVT_CALENDAR_SEL_CHANGED(id, fn) { wxEVT_CALENDAR_SEL_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & fn, (wxObject *) NULL }, #define EVT_CALENDAR_DAY(id, fn) { wxEVT_CALENDAR_DAY_CHANGED, id, -1, (wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & fn, (wxObject *) NULL }, diff --git a/include/wx/datetime.h b/include/wx/datetime.h index 82ce040f11..e51a7934bf 100644 --- a/include/wx/datetime.h +++ b/include/wx/datetime.h @@ -38,9 +38,10 @@ class WXDLLEXPORT wxDateSpan; * TODO Well, everything :-) * * + 1. Time zones with minutes (make TimeZone a class) - * 2. getdate() function like under Solaris + * ? 2. getdate() function like under Solaris * + 3. text conversion for wxDateSpan - * 4. pluggable modules for the workdays calculations + * + 4. pluggable modules for the workdays calculations + * 5. wxDateTimeHolidayAuthority for Easter and other christian feasts */ /* @@ -618,17 +619,21 @@ public: // default assignment operator is ok // calendar calculations (functions which set the date only leave the time - // unchanged, e.g. don't explictly zero it) + // unchanged, e.g. don't explictly zero it): SetXXX() functions modify the + // object itself, GetXXX() ones return a new object. // ------------------------------------------------------------------------ // set to the given week day in the same week as this one wxDateTime& SetToWeekDayInSameWeek(WeekDay weekday); + inline wxDateTime GetWeekDayInSameWeek(WeekDay weekday) const; // set to the next week day following this one wxDateTime& SetToNextWeekDay(WeekDay weekday); + inline wxDateTime GetNextWeekDay(WeekDay weekday) const; // set to the previous week day before this one wxDateTime& SetToPrevWeekDay(WeekDay weekday); + inline wxDateTime GetPrevWeekDay(WeekDay weekday) const; // set to Nth occurence of given weekday in the given month of the // given year (time is set to 0), return TRUE on success and FALSE on @@ -638,24 +643,35 @@ public: int n = 1, Month month = Inv_Month, int year = Inv_Year); + inline wxDateTime GetWeekDay(WeekDay weekday, + int n = 1, + Month month = Inv_Month, + int year = Inv_Year) const; // sets to the last weekday in the given month, year inline bool SetToLastWeekDay(WeekDay weekday, Month month = Inv_Month, int year = Inv_Year); + inline wxDateTime GetLastWeekDay(WeekDay weekday, + Month month = Inv_Month, + int year = Inv_Year); // sets the date to the given day of the given week in the year, // returns TRUE on success and FALSE if given date doesn't exist (e.g. // numWeek is > 53) bool SetToTheWeek(wxDateTime_t numWeek, WeekDay weekday = Mon); + inline wxDateTime GetWeek(wxDateTime_t numWeek, WeekDay weekday = Mon) const; // sets the date to the last day of the given (or current) month or the // given (or current) year wxDateTime& SetToLastMonthDay(Month month = Inv_Month, int year = Inv_Year); + inline wxDateTime GetLastMonthDay(Month month = Inv_Month, + int year = Inv_Year) const; // sets to the given year day (1..365 or 366) wxDateTime& SetToYearDay(wxDateTime_t yday); + inline wxDateTime GetYearDay(wxDateTime_t yday) const; // The definitions below were taken verbatim from // @@ -784,13 +800,12 @@ public: // is this date a work day? This depends on a country, of course, // because the holidays are different in different countries - bool IsWorkDay(Country country = Country_Default, - const TimeZone& tz = Local) const; + bool IsWorkDay(Country country = Country_Default) const; // is this date later than Gregorian calendar introduction for the // given country (see enum GregorianAdoption)? // - // NB: this function shouldn't be considered as absolute authoiruty in + // NB: this function shouldn't be considered as absolute authority in // the matter. Besides, for some countries the exact date of // adoption of the Gregorian calendar is simply unknown. bool IsGregorianDate(GregorianAdoption country = Gr_Standard) const; @@ -891,9 +906,12 @@ public: wxString FormatDate() const { return Format(_T("%x")); } // preferred time representation for the current locale wxString FormatTime() const { return Format(_T("%X")); } - // return the string representing the date in ISO 8601 format + // returns the string representing the date in ISO 8601 format // (YYYY-MM-DD) wxString FormatISODate() const { return Format(_T("%Y-%m-%d")); } + // returns the string representing the time in ISO 8601 format + // (HH:MM:SS) + wxString FormatISOTime() const { return Format(_T("%H:%M:%S")); } // implementation // ------------------------------------------------------------------------ @@ -1098,7 +1116,12 @@ private: // one month to Feb, 15 - we want to get Mar, 15, of course). // // When adding a month to the date, all lesser components (days, hours, ...) -// won't be changed. +// won't be changed unless the resulting date would be invalid: for example, +// Jan 31 + 1 month will be Feb 28, not (non existing) Feb 31. +// +// Because of this feature, adding and substracting back again the same +// wxDateSpan will *not*, in general give back the original date: Feb 28 - 1 +// month will be Jan 28, not Jan 31! // // wxDateSpan can be either positive or negative. They may be // multiplied by scalars which multiply all deltas by the scalar: i.e. 2*(1 @@ -1210,10 +1233,75 @@ private: m_days; }; -WXDLLEXPORT_DATA(extern wxDateSpan) wxYear; -WXDLLEXPORT_DATA(extern wxDateSpan) wxMonth; -WXDLLEXPORT_DATA(extern wxDateSpan) wxWeek; -WXDLLEXPORT_DATA(extern wxDateSpan) wxDay; +// ---------------------------------------------------------------------------- +// wxDateTimeArray: array of dates. +// ---------------------------------------------------------------------------- + +#include "wx/dynarray.h" + +WX_DECLARE_OBJARRAY(wxDateTime, wxDateTimeArray); + +// ---------------------------------------------------------------------------- +// wxDateTimeHolidayAuthority: an object of this class will decide whether a +// given date is a holiday and is used by all functions working with "work +// days". +// +// NB: the base class is an ABC, derived classes must implement the pure +// virtual methods to work with the holidays they correspond to. +// ---------------------------------------------------------------------------- + +class WXDLLEXPORT wxDateTimeHolidayAuthority; +WX_DEFINE_ARRAY(wxDateTimeHolidayAuthority *, wxHolidayAuthoritiesArray); + +class WXDLLEXPORT wxDateTimeHolidayAuthority +{ +public: + // returns TRUE if the given date is a holiday + static bool IsHoliday(const wxDateTime& dt); + + // fills the provided array with all holidays in the given range, returns + // the number of them + static size_t GetHolidaysInRange(const wxDateTime& dtStart, + const wxDateTime& dtEnd, + wxDateTimeArray& holidays); + + // clear the list of holiday authorities + static void ClearAllAuthorities(); + + // add a new holiday authority (the pointer will be deleted by + // wxDateTimeHolidayAuthority) + static void AddAuthority(wxDateTimeHolidayAuthority *auth); + +protected: + // this function is called to determine whether a given day is a holiday + virtual bool DoIsHoliday(const wxDateTime& dt) const = 0; + + // this function should fill the array with all holidays between the two + // given dates - it is implemented in the base class, but in a very + // inefficient way (it just iterates over all days and uses IsHoliday() for + // each of them), so it must be overridden in the derived class where the + // base class version may be explicitly used if needed + // + // returns the number of holidays in the given range and fills holidays + // array + virtual size_t DoGetHolidaysInRange(const wxDateTime& dtStart, + const wxDateTime& dtEnd, + wxDateTimeArray& holidays) const = 0; + +private: + // all holiday authorities + static wxHolidayAuthoritiesArray ms_authorities; +}; + +// the holidays for this class are all Saturdays and Sundays +class WXDLLEXPORT wxDateTimeWorkDays : public wxDateTimeHolidayAuthority +{ +protected: + virtual bool DoIsHoliday(const wxDateTime& dt) const; + virtual size_t DoGetHolidaysInRange(const wxDateTime& dtStart, + const wxDateTime& dtEnd, + wxDateTimeArray& holidays) const; +}; // ============================================================================ // inline functions implementation diff --git a/include/wx/datetime.inl b/include/wx/datetime.inl index 41dcc9fe95..f86b7ff956 100644 --- a/include/wx/datetime.inl +++ b/include/wx/datetime.inl @@ -16,8 +16,21 @@ #error "This file is only included by wx/datetime.h, don't include it manually!" #endif +// ---------------------------------------------------------------------------- +// private macros +// ---------------------------------------------------------------------------- + #define MILLISECONDS_PER_DAY 86400000l +// some broken compilers (HP-UX CC) refuse to compile the "normal" version, but +// using a temp variable always might prevent other compilers from optimising +// it away - hence use of this ugly macro +#ifndef __HPUX__ + #define MODIFY_AND_RETURN(op) return wxDateTime(*this).op +#else + #define MODIFY_AND_RETURN(op) wxDateTime dt(*this); dt.op; return dt +#endif + // ---------------------------------------------------------------------------- // wxDateTime construction // ---------------------------------------------------------------------------- @@ -138,6 +151,57 @@ bool wxDateTime::SetToLastWeekDay(WeekDay weekday, return SetToWeekDay(weekday, -1, month, year); } +wxDateTime wxDateTime::GetWeekDayInSameWeek(WeekDay weekday) const +{ + MODIFY_AND_RETURN( SetToWeekDayInSameWeek(weekday) ); +} + +wxDateTime wxDateTime::GetNextWeekDay(WeekDay weekday) const +{ + MODIFY_AND_RETURN( SetToNextWeekDay(weekday) ); +} + +wxDateTime wxDateTime::GetPrevWeekDay(WeekDay weekday) const +{ + MODIFY_AND_RETURN( SetToPrevWeekDay(weekday) ); +} + +wxDateTime wxDateTime::GetWeekDay(WeekDay weekday, + int n, + Month month, + int year) const +{ + wxDateTime dt(*this); + + return dt.SetToWeekDay(weekday, n, month, year) ? dt : wxInvalidDateTime; +} + +wxDateTime wxDateTime::GetLastWeekDay(WeekDay weekday, + Month month, + int year) +{ + wxDateTime dt(*this); + + return dt.SetToLastWeekDay(weekday, month, year) ? dt : wxInvalidDateTime; +} + +wxDateTime wxDateTime::GetWeek(wxDateTime_t numWeek, WeekDay weekday) const +{ + wxDateTime dt(*this); + + return dt.SetToTheWeek(numWeek, weekday) ? dt : wxInvalidDateTime; +} + +wxDateTime wxDateTime::GetLastMonthDay(Month month, int year) const +{ + MODIFY_AND_RETURN( SetToLastMonthDay(month, year) ); +} + +wxDateTime wxDateTime::GetYearDay(wxDateTime_t yday) const +{ + MODIFY_AND_RETURN( SetToYearDay(yday) ); +} + // ---------------------------------------------------------------------------- // wxDateTime comparison // ---------------------------------------------------------------------------- @@ -287,8 +351,7 @@ wxDateTime& wxDateTime::operator+=(const wxDateSpan& diff) wxDateTime wxDateTime::ToTimezone(const wxDateTime::TimeZone& tz, bool noDST) const { - wxDateTime dt(*this); - return dt.MakeTimezone(tz, noDST); + MODIFY_AND_RETURN( MakeTimezone(tz, noDST) ); } // ---------------------------------------------------------------------------- @@ -437,3 +500,5 @@ wxDateSpan& wxDateSpan::Neg() } #undef MILLISECONDS_PER_DAY + +#undef MODIFY_AND_RETURN diff --git a/include/wx/dynarray.h b/include/wx/dynarray.h index fc49a0f260..109a89437c 100644 --- a/include/wx/dynarray.h +++ b/include/wx/dynarray.h @@ -448,6 +448,16 @@ WX_DEFINE_EXPORTED_ARRAY(void *, wxArrayPtrVoid); // convinience macros // ----------------------------------------------------------------------------- +// append all element of one array to another one +#define WX_APPEND_ARRAY(array, other) \ + { \ + size_t count = other.Count(); \ + for ( size_t n = 0; n < count; n++ ) \ + { \ + array.Add(other[n]); \ + } \ + } + // delete all array elements // // NB: the class declaration of the array elements must be visible from the diff --git a/include/wx/generic/calctrl.h b/include/wx/generic/calctrl.h index a3c4617964..78a9385d3b 100644 --- a/include/wx/generic/calctrl.h +++ b/include/wx/generic/calctrl.h @@ -18,9 +18,9 @@ #include "wx/control.h" // the base class -#include "wx/datetime.h" // for m_date -#include "wx/combobox.h" // for m_comboMonth -#include "wx/spinctrl.h" // for m_spinYear +#include "wx/spinctrl.h" // for wxSpinEvent + +class WXDLLEXPORT wxComboBox; #define wxCalendarNameStr _T("CalendarCtrl") @@ -41,7 +41,7 @@ public: const wxDateTime& date = wxDefaultDateTime, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = 0, + long style = wxCAL_SHOW_HOLIDAYS, const wxString& name = wxCalendarNameStr) : wxControl(parent, id, pos, size, style | wxWANTS_CHARS, wxDefaultValidator, name) @@ -56,15 +56,81 @@ public: const wxDateTime& date = wxDefaultDateTime, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, - long style = 0, + long style = wxCAL_SHOW_HOLIDAYS, const wxString& name = wxCalendarNameStr); virtual ~wxCalendarCtrl(); // set/get the current date + // ------------------------ + void SetDate(const wxDateTime& date); const wxDateTime& GetDate() const { return m_date; } + // customization + // ------------- + + // header colours are used for painting the weekdays at the top + void SetHeaderColours(const wxColour& colFg, const wxColour& colBg) + { + m_colHeaderFg = colFg; + m_colHeaderBg = colBg; + } + + const wxColour& GetHeaderColourFg() const { return m_colHeaderFg; } + const wxColour& GetHeaderColourBg() const { return m_colHeaderBg; } + + // highlight colour is used for the currently selected date + void SetHighlightColours(const wxColour& colFg, const wxColour& colBg) + { + m_colHighlightFg = colFg; + m_colHighlightBg = colBg; + } + + const wxColour& GetHighlightColourFg() const { return m_colHighlightFg; } + const wxColour& GetHighlightColourBg() const { return m_colHighlightBg; } + + // holiday colour is used for the holidays (if style & wxCAL_SHOW_HOLIDAYS) + void SetHolidayColours(const wxColour& colFg, const wxColour& colBg) + { + m_colHolidayFg = colFg; + m_colHolidayBg = colBg; + } + + const wxColour& GetHolidayColourFg() const { return m_colHolidayFg; } + const wxColour& GetHolidayColourBg() const { return m_colHolidayBg; } + + // this function should be called instead of directly changing the + // wxCAL_SHOW_HOLIDAYS bit in the control style after the control creation + // (this won't work) + void EnableHolidayDisplay(bool display = TRUE); + + // an item without custom attributes is drawn with the default colours and + // font and without border, setting custom attributes allows to modify this + // + // the day parameter should be in 1..31 range, for days 29, 30, 31 the + // corresponding attribute is just unused if there is no such day in the + // current month + + wxCalendarDateAttr *GetAttr(size_t day) const + { + wxCHECK_MSG( day > 0 && day < 32, NULL, _T("invalid day") ); + + return m_attrs[day - 1]; + } + + void SetAttr(size_t day, wxCalendarDateAttr *attr) + { + wxCHECK_RET( day > 0 && day < 32, _T("invalid day") ); + + delete m_attrs[day - 1]; + m_attrs[day - 1] = attr; + } + + void SetHoliday(size_t day); + + void ResetAttr(size_t day) { SetAttr(day, (wxCalendarDateAttr *)NULL); } + // returns one of wxCAL_HITTEST_XXX constants and fills either date or wd // with the corresponding value (none for NOWHERE, the date for DAY and wd // for HEADER) @@ -90,6 +156,7 @@ private: void OnChar(wxKeyEvent& event); void OnMonthChange(wxCommandEvent& event); void OnYearChange(wxSpinEvent& event); + void OnCalMonthChange(wxCalendarEvent& event); // override some base class virtuals virtual wxSize DoGetBestSize() const; @@ -119,16 +186,43 @@ private: // change the date inside the same month/year void ChangeDay(const wxDateTime& date); - // generate the given calendar event and a "selection changed" one if - // selChanged is TRUE - void GenerateEvent(wxEventType type, bool selChanged = TRUE); + // set the attributes for the holidays if needed + void SetHolidayAttrs(); + + // reset all holidays + void ResetHolidayAttrs(); + + // generate the given calendar event(s) + void GenerateEvent(wxEventType type) + { + wxCalendarEvent event(this, type); + (void)GetEventHandler()->ProcessEvent(event); + } + + void GenerateEvents(wxEventType type1, wxEventType type2) + { + GenerateEvent(type1); + GenerateEvent(type2); + } // the subcontrols wxComboBox *m_comboMonth; wxSpinCtrl *m_spinYear; + // the current selection wxDateTime m_date; + // default attributes + wxColour m_colHighlightFg, + m_colHighlightBg, + m_colHolidayFg, + m_colHolidayBg, + m_colHeaderFg, + m_colHeaderBg; + + // the attributes for each of the month days + wxCalendarDateAttr *m_attrs[31]; + // the width and height of one column/row in the calendar wxCoord m_widthCol, m_heightRow; diff --git a/src/common/datetime.cpp b/src/common/datetime.cpp index eae9bc33bf..7fed200c42 100644 --- a/src/common/datetime.cpp +++ b/src/common/datetime.cpp @@ -67,6 +67,7 @@ #include "wx/string.h" #include "wx/intl.h" #include "wx/log.h" + #include "wx/module.h" #endif // WX_PRECOMP #include "wx/thread.h" @@ -99,6 +100,31 @@ #endif #endif // !WX_TIMEZONE +// ---------------------------------------------------------------------------- +// private classes +// ---------------------------------------------------------------------------- + +class wxDateTimeHolidaysModule : public wxModule +{ +public: + virtual bool OnInit() + { + wxDateTimeHolidayAuthority::AddAuthority(new wxDateTimeWorkDays); + + return TRUE; + } + + virtual void OnExit() + { + wxDateTimeHolidayAuthority::ClearAllAuthorities(); + } + +private: + DECLARE_DYNAMIC_CLASS(wxDateTimeHolidaysModule) +}; + +IMPLEMENT_DYNAMIC_CLASS(wxDateTimeHolidaysModule, wxModule) + // ---------------------------------------------------------------------------- // constants // ---------------------------------------------------------------------------- @@ -160,6 +186,18 @@ wxDateTime::Country wxDateTime::ms_country = wxDateTime::Country_Unknown; // private functions // ---------------------------------------------------------------------------- +// debugger helper: shows what the date really is +#ifdef __WXDEBUG__ +extern const wxChar *wxDumpDate(const wxDateTime* dt) +{ + static wxChar buf[20]; + + wxStrcpy(buf, dt->Format(_T("%Y-%m-%d (%a) %H:%M:%S"))); + + return buf; +} +#endif // Debug + // get the number of days in the given month of the given year static inline wxDateTime::wxDateTime_t GetNumOfDaysInMonth(int year, wxDateTime::Month month) @@ -437,6 +475,9 @@ void wxDateTime::Tm::AddMonths(int monDiff) mon = (wxDateTime::Month)(mon + monDiff); wxASSERT_MSG( mon >= 0 && mon < MONTHS_IN_YEAR, _T("logic error") ); + + // NB: we don't check here that the resulting date is valid, this function + // is private and the caller must check it if needed } void wxDateTime::Tm::AddDays(int dayDiff) @@ -1358,6 +1399,20 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff) tm.year += diff.GetYears(); tm.AddMonths(diff.GetMonths()); + + // check that the resulting date is valid + if ( tm.mday > GetNumOfDaysInMonth(tm.year, tm.mon) ) + { + // We suppose that when adding one month to Jan 31 we want to get Feb + // 28 (or 29), i.e. adding a month to the last day of the month should + // give the last day of the next month which is quite logical. + // + // Unfortunately, there is no logic way to understand what should + // Jan 30 + 1 month be - Feb 28 too or Feb 27 (assuming non leap year)? + // We make it Feb 28 (last day too), but it is highly questionable. + tm.mday = GetNumOfDaysInMonth(tm.year, tm.mon); + } + tm.AddDays(diff.GetTotalDays()); Set(tm); @@ -1372,6 +1427,23 @@ wxDateTime& wxDateTime::Add(const wxDateSpan& diff) // Weekday and monthday stuff // ---------------------------------------------------------------------------- +bool wxDateTime::SetToTheWeek(wxDateTime_t numWeek, WeekDay weekday) +{ + int year = GetYear(); + + // Jan 4 always lies in the 1st week of the year + Set(4, Jan, year); + SetToWeekDayInSameWeek(weekday) += wxDateSpan::Weeks(numWeek); + + if ( GetYear() != year ) + { + // oops... numWeek was too big + return FALSE; + } + + return TRUE; +} + wxDateTime& wxDateTime::SetToLastMonthDay(Month month, int year) { @@ -1396,11 +1468,11 @@ wxDateTime& wxDateTime::SetToWeekDayInSameWeek(WeekDay weekday) } else if ( weekday < wdayThis ) { - return Substract(wxTimeSpan::Days(wdayThis - weekday)); + return Substract(wxDateSpan::Days(wdayThis - weekday)); } else // weekday > wdayThis { - return Add(wxTimeSpan::Days(weekday - wdayThis)); + return Add(wxDateSpan::Days(weekday - wdayThis)); } } @@ -1425,7 +1497,7 @@ wxDateTime& wxDateTime::SetToNextWeekDay(WeekDay weekday) diff = weekday - wdayThis; } - return Add(wxTimeSpan::Days(diff)); + return Add(wxDateSpan::Days(diff)); } wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday) @@ -1449,7 +1521,7 @@ wxDateTime& wxDateTime::SetToPrevWeekDay(WeekDay weekday) diff = wdayThis - weekday; } - return Substract(wxTimeSpan::Days(diff)); + return Substract(wxDateSpan::Days(diff)); } bool wxDateTime::SetToWeekDay(WeekDay weekday, @@ -3240,6 +3312,15 @@ const wxChar *wxDateTime::ParseTime(const wxChar *time) return result; } +// ---------------------------------------------------------------------------- +// Workdays and holidays support +// ---------------------------------------------------------------------------- + +bool wxDateTime::IsWorkDay(Country WXUNUSED(country)) const +{ + return !wxDateTimeHolidayAuthority::IsHoliday(*this); +} + // ============================================================================ // wxTimeSpan // ============================================================================ @@ -3325,3 +3406,121 @@ wxString wxTimeSpan::Format(const wxChar *format) const return str; } + +// ============================================================================ +// wxDateTimeHolidayAuthority and related classes +// ============================================================================ + +#include "wx/arrimpl.cpp" + +WX_DEFINE_OBJARRAY(wxDateTimeArray) + +static int wxCMPFUNC_CONV +wxDateTimeCompareFunc(wxDateTime **first, wxDateTime **second) +{ + wxDateTime dt1 = **first, + dt2 = **second; + + return dt1 == dt2 ? 0 : dt1 < dt2 ? -1 : +1; +} + +// ---------------------------------------------------------------------------- +// wxDateTimeHolidayAuthority +// ---------------------------------------------------------------------------- + +wxHolidayAuthoritiesArray wxDateTimeHolidayAuthority::ms_authorities; + +/* static */ +bool wxDateTimeHolidayAuthority::IsHoliday(const wxDateTime& dt) +{ + size_t count = ms_authorities.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + if ( ms_authorities[n]->DoIsHoliday(dt) ) + { + return TRUE; + } + } + + return FALSE; +} + +/* static */ +size_t +wxDateTimeHolidayAuthority::GetHolidaysInRange(const wxDateTime& dtStart, + const wxDateTime& dtEnd, + wxDateTimeArray& holidays) +{ + wxDateTimeArray hol; + + holidays.Empty(); + + size_t count = ms_authorities.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + ms_authorities[n]->DoGetHolidaysInRange(dtStart, dtEnd, hol); + + WX_APPEND_ARRAY(holidays, hol); + } + + holidays.Sort(wxDateTimeCompareFunc); + + return holidays.GetCount(); +} + +/* static */ +void wxDateTimeHolidayAuthority::ClearAllAuthorities() +{ + WX_CLEAR_ARRAY(ms_authorities); +} + +/* static */ +void wxDateTimeHolidayAuthority::AddAuthority(wxDateTimeHolidayAuthority *auth) +{ + ms_authorities.Add(auth); +} + +// ---------------------------------------------------------------------------- +// wxDateTimeWorkDays +// ---------------------------------------------------------------------------- + +bool wxDateTimeWorkDays::DoIsHoliday(const wxDateTime& dt) const +{ + wxDateTime::WeekDay wd = dt.GetWeekDay(); + + return (wd == wxDateTime::Sun) || (wd == wxDateTime::Sat); +} + +size_t wxDateTimeWorkDays::DoGetHolidaysInRange(const wxDateTime& dtStart, + const wxDateTime& dtEnd, + wxDateTimeArray& holidays) const +{ + if ( dtStart > dtEnd ) + { + wxFAIL_MSG( _T("invalid date range in GetHolidaysInRange") ); + + return 0u; + } + + holidays.Empty(); + + // instead of checking all days, start with the first Sat after dtStart and + // end with the last Sun before dtEnd + wxDateTime dtSatFirst = dtStart.GetNextWeekDay(wxDateTime::Sat), + dtSatLast = dtEnd.GetPrevWeekDay(wxDateTime::Sat), + dtSunFirst = dtStart.GetNextWeekDay(wxDateTime::Sun), + dtSunLast = dtEnd.GetPrevWeekDay(wxDateTime::Sun), + dt; + + for ( dt = dtSatFirst; dt <= dtSatLast; dt += wxDateSpan::Week() ) + { + holidays.Add(dt); + } + + for ( dt = dtSunFirst; dt <= dtSunLast; dt += wxDateSpan::Week() ) + { + holidays.Add(dt); + } + + return holidays.GetCount(); +} diff --git a/src/generic/calctrl.cpp b/src/generic/calctrl.cpp index 78b6c57f49..73f74042c9 100644 --- a/src/generic/calctrl.cpp +++ b/src/generic/calctrl.cpp @@ -32,6 +32,7 @@ #include "wx/dcclient.h" #include "wx/settings.h" #include "wx/brush.h" + #include "wx/combobox.h" #endif //WX_PRECOMP #include "wx/calctrl.h" @@ -79,6 +80,9 @@ BEGIN_EVENT_TABLE(wxCalendarCtrl, wxControl) EVT_LEFT_DOWN(wxCalendarCtrl::OnClick) EVT_LEFT_DCLICK(wxCalendarCtrl::OnDClick) + + EVT_CALENDAR_MONTH(-1, wxCalendarCtrl::OnCalMonthChange) + EVT_CALENDAR_YEAR(-1, wxCalendarCtrl::OnCalMonthChange) END_EVENT_TABLE() BEGIN_EVENT_TABLE(wxMonthComboBox, wxComboBox) @@ -146,6 +150,21 @@ void wxCalendarCtrl::Init() { m_weekdays[wd] = wxDateTime::GetWeekDayName(wd, wxDateTime::Name_Abbr); } + + for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ ) + { + m_attrs[n] = NULL; + } + + wxSystemSettings ss; + m_colHighlightFg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT); + m_colHighlightBg = ss.GetSystemColour(wxSYS_COLOUR_HIGHLIGHT); + + m_colHolidayFg = *wxRED; + // don't set m_colHolidayBg - by default, same as our bg colour + + m_colHeaderFg = *wxBLUE; + m_colHeaderBg = *wxLIGHT_GREY; } bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent), @@ -182,15 +201,17 @@ bool wxCalendarCtrl::Create(wxWindow * WXUNUSED(parent), SetBackgroundColour(*wxWHITE); SetFont(*wxSWISS_FONT); + SetHolidayAttrs(); + return TRUE; } wxCalendarCtrl::~wxCalendarCtrl() { -#if 0 - m_comboMonth->PopEventHandler(); - m_spinYear->PopEventHandler(); -#endif // 0 + for ( size_t n = 0; n < WXSIZEOF(m_attrs); n++ ) + { + delete m_attrs[n]; + } } // ---------------------------------------------------------------------------- @@ -285,7 +306,7 @@ void wxCalendarCtrl::SetDateAndNotify(const wxDateTime& date) SetDate(date); - GenerateEvent(type); + GenerateEvents(type, wxEVT_CALENDAR_SEL_CHANGED); } // ---------------------------------------------------------------------------- @@ -302,9 +323,6 @@ wxDateTime wxCalendarCtrl::GetStartDate() const date.SetToPrevWeekDay(GetWindowStyle() & wxCAL_MONDAY_FIRST ? wxDateTime::Mon : wxDateTime::Sun); - // be sure to do it or it might gain 1 hour if DST changed in between - date.ResetTime(); - return date; } @@ -444,10 +462,10 @@ void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) puts("painting the header"); #endif - dc.SetTextForeground(*wxBLUE); - dc.SetBrush(wxBrush(*wxLIGHT_GREY, wxSOLID)); dc.SetBackgroundMode(wxTRANSPARENT); - dc.SetPen(*wxLIGHT_GREY_PEN); + dc.SetTextForeground(m_colHeaderFg); + dc.SetBrush(wxBrush(m_colHeaderBg, wxSOLID)); + dc.SetPen(wxPen(m_colHeaderBg, 1, wxSOLID)); dc.DrawRectangle(0, 0, 7*m_widthCol, m_heightRow); bool startOnMonday = (GetWindowStyle() & wxCAL_MONDAY_FIRST) != 0; @@ -495,24 +513,104 @@ void wxCalendarCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) if ( IsDateShown(date) ) { // don't use wxDate::Format() which prepends 0s - wxString day = wxString::Format(_T("%u"), date.GetDay()); + unsigned int day = date.GetDay(); + wxString dayStr = wxString::Format(_T("%u"), day); wxCoord width; - dc.GetTextExtent(day, &width, (wxCoord *)NULL); + dc.GetTextExtent(dayStr, &width, (wxCoord *)NULL); + + bool changedColours = FALSE, + changedFont = FALSE; + + wxCalendarDateAttr *attr = m_attrs[day - 1]; bool isSel = m_date == date; if ( isSel ) { - dc.SetTextForeground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHTTEXT)); - dc.SetTextBackground(wxSystemSettings::GetSystemColour(wxSYS_COLOUR_HIGHLIGHT)); + dc.SetTextForeground(m_colHighlightFg); + dc.SetTextBackground(m_colHighlightBg); + + changedColours = TRUE; + } + else if ( attr ) + { + wxColour colFg, colBg; + + if ( attr->IsHoliday() ) + { + colFg = m_colHolidayFg; + colBg = m_colHolidayBg; + } + else + { + colFg = attr->GetTextColour(); + colBg = attr->GetBackgroundColour(); + } + + if ( colFg.Ok() ) + { + dc.SetTextForeground(colFg); + changedColours = TRUE; + } + + if ( colBg.Ok() ) + { + dc.SetTextBackground(colBg); + changedColours = TRUE; + } + + if ( attr->HasFont() ) + { + dc.SetFont(attr->GetFont()); + changedFont = TRUE; + } } - dc.DrawText(day, wd*m_widthCol + (m_widthCol - width) / 2, y); + wxCoord x = wd*m_widthCol + (m_widthCol - width) / 2; + dc.DrawText(dayStr, x, y + 1); - if ( isSel ) + if ( !isSel && attr && attr->HasBorder() ) + { + wxColour colBorder; + if ( attr->HasBorderColour() ) + { + colBorder = attr->GetBorderColour(); + } + else + { + colBorder = m_foregroundColour; + } + + wxPen pen(colBorder, 1, wxSOLID); + dc.SetPen(pen); + dc.SetBrush(*wxTRANSPARENT_BRUSH); + + switch ( attr->GetBorder() ) + { + case wxCAL_BORDER_SQUARE: + dc.DrawRectangle(x - 2, y, + width + 4, m_heightRow); + break; + + case wxCAL_BORDER_ROUND: + dc.DrawEllipse(x - 2, y, + width + 4, m_heightRow); + break; + + default: + wxFAIL_MSG(_T("unknown border type")); + } + } + + if ( changedColours ) { dc.SetTextForeground(m_foregroundColour); dc.SetTextBackground(m_backgroundColour); } + + if ( changedFont ) + { + dc.SetFont(m_font); + } } //else: just don't draw it @@ -560,7 +658,7 @@ void wxCalendarCtrl::OnDClick(wxMouseEvent& event) } else { - GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED, FALSE); + GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED); } } @@ -573,7 +671,8 @@ void wxCalendarCtrl::OnClick(wxMouseEvent& event) case wxCAL_HITTEST_DAY: ChangeDay(date); - GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED); + GenerateEvents(wxEVT_CALENDAR_DAY_CHANGED, + wxEVT_CALENDAR_SEL_CHANGED); break; case wxCAL_HITTEST_HEADER: @@ -655,7 +754,7 @@ void wxCalendarCtrl::OnMonthChange(wxCommandEvent& event) SetDate(wxDateTime(tm.mday, mon, tm.year)); - GenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED); + GenerateEvents(wxEVT_CALENDAR_MONTH_CHANGED, wxEVT_CALENDAR_SEL_CHANGED); } void wxCalendarCtrl::OnYearChange(wxSpinEvent& event) @@ -670,7 +769,7 @@ void wxCalendarCtrl::OnYearChange(wxSpinEvent& event) SetDate(wxDateTime(tm.mday, tm.mon, year)); - GenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED); + GenerateEvents(wxEVT_CALENDAR_YEAR_CHANGED, wxEVT_CALENDAR_SEL_CHANGED); } // ---------------------------------------------------------------------------- @@ -736,30 +835,96 @@ void wxCalendarCtrl::OnChar(wxKeyEvent& event) SetDateAndNotify(wxDateTime(m_date).SetToLastMonthDay()); break; + case WXK_RETURN: + GenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED); + break; + default: event.Skip(); } } // ---------------------------------------------------------------------------- -// wxCalendarEvent +// holidays handling // ---------------------------------------------------------------------------- -void wxCalendarCtrl::GenerateEvent(wxEventType type, bool selChanged) +void wxCalendarCtrl::OnCalMonthChange(wxCalendarEvent& event) +{ + SetHolidayAttrs(); + + event.Skip(); +} + +void wxCalendarCtrl::EnableHolidayDisplay(bool display) { - // we're called for a change in some particular date field but we always - // also generate a generic "changed" event - wxCalendarEvent event(this, type); - (void)GetEventHandler()->ProcessEvent(event); + long style = GetWindowStyle(); + if ( display ) + style |= wxCAL_SHOW_HOLIDAYS; + else + style &= ~wxCAL_SHOW_HOLIDAYS; + + SetWindowStyle(style); + + if ( display ) + SetHolidayAttrs(); + else + ResetHolidayAttrs(); + + Refresh(); +} + +void wxCalendarCtrl::SetHolidayAttrs() +{ + if ( GetWindowStyle() & wxCAL_SHOW_HOLIDAYS ) + { + ResetHolidayAttrs(); + + wxDateTime::Tm tm = m_date.GetTm(); + wxDateTime dtStart(1, tm.mon, tm.year), + dtEnd = dtStart.GetLastMonthDay(); + + wxDateTimeArray hol; + wxDateTimeHolidayAuthority::GetHolidaysInRange(dtStart, dtEnd, hol); + + size_t count = hol.GetCount(); + for ( size_t n = 0; n < count; n++ ) + { + SetHoliday(hol[n].GetDay()); + } + } +} + +void wxCalendarCtrl::SetHoliday(size_t day) +{ + wxCHECK_RET( day > 0 && day < 32, _T("invalid day in SetHoliday") ); - if ( selChanged ) + wxCalendarDateAttr *attr = GetAttr(day); + if ( !attr ) { - wxCalendarEvent event2(this, wxEVT_CALENDAR_SEL_CHANGED); + attr = new wxCalendarDateAttr; + } - (void)GetEventHandler()->ProcessEvent(event2); + attr->SetHoliday(TRUE); + + // can't use SetAttr() because it would delete this pointer + m_attrs[day - 1] = attr; +} + +void wxCalendarCtrl::ResetHolidayAttrs() +{ + for ( size_t day = 0; day < 31; day++ ) + { + if ( m_attrs[day] ) + { + m_attrs[day]->SetHoliday(FALSE); + } } } +// ---------------------------------------------------------------------------- +// wxCalendarEvent +// ---------------------------------------------------------------------------- + void wxCalendarEvent::Init() { m_wday = wxDateTime::Inv_WeekDay; -- 2.45.2