1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/calctrl.cpp
3 // Purpose:     implementation of the wxGtkCalendarCtrl
4 // Author:      Marcin Wojdyr
5 // Copyright:   (c) 2008 Marcin Wojdyr
6 // Licence:     wxWindows licence
7 ///////////////////////////////////////////////////////////////////////////////
8 
9 #include "wx/wxprec.h"
10 
11 
12 #if wxUSE_CALENDARCTRL
13 
14 #ifndef WX_PRECOMP
15 #endif //WX_PRECOMP
16 
17 #include "wx/calctrl.h"
18 
19 #include "wx/gtk/private/wrapgtk.h"
20 
21 extern "C" {
22 
gtk_day_selected_callback(GtkWidget * WXUNUSED (widget),wxGtkCalendarCtrl * cal)23 static void gtk_day_selected_callback(GtkWidget *WXUNUSED(widget),
24                                       wxGtkCalendarCtrl *cal)
25 {
26     cal->GTKGenerateEvent(wxEVT_CALENDAR_SEL_CHANGED);
27 }
28 
gtk_day_selected_double_click_callback(GtkWidget * WXUNUSED (widget),wxGtkCalendarCtrl * cal)29 static void gtk_day_selected_double_click_callback(GtkWidget *WXUNUSED(widget),
30                                                    wxGtkCalendarCtrl *cal)
31 {
32     cal->GTKGenerateEvent(wxEVT_CALENDAR_DOUBLECLICKED);
33 }
34 
gtk_month_changed_callback(GtkWidget * WXUNUSED (widget),wxGtkCalendarCtrl * cal)35 static void gtk_month_changed_callback(GtkWidget *WXUNUSED(widget),
36                                        wxGtkCalendarCtrl *cal)
37 {
38     cal->GTKGenerateEvent(wxEVT_CALENDAR_PAGE_CHANGED);
39 }
40 
41 // callbacks that send deprecated events
42 
gtk_prev_month_callback(GtkWidget * WXUNUSED (widget),wxGtkCalendarCtrl * cal)43 static void gtk_prev_month_callback(GtkWidget *WXUNUSED(widget),
44                                     wxGtkCalendarCtrl *cal)
45 {
46     cal->GTKGenerateEvent(wxEVT_CALENDAR_MONTH_CHANGED);
47 }
48 
gtk_prev_year_callback(GtkWidget * WXUNUSED (widget),wxGtkCalendarCtrl * cal)49 static void gtk_prev_year_callback(GtkWidget *WXUNUSED(widget),
50                                     wxGtkCalendarCtrl *cal)
51 {
52     cal->GTKGenerateEvent(wxEVT_CALENDAR_YEAR_CHANGED);
53 }
54 
55 }
56 
57 // ----------------------------------------------------------------------------
58 // wxGtkCalendarCtrl
59 // ----------------------------------------------------------------------------
60 
61 
Create(wxWindow * parent,wxWindowID id,const wxDateTime & date,const wxPoint & pos,const wxSize & size,long style,const wxString & name)62 bool wxGtkCalendarCtrl::Create(wxWindow *parent,
63                                wxWindowID id,
64                                const wxDateTime& date,
65                                const wxPoint& pos,
66                                const wxSize& size,
67                                long style,
68                                const wxString& name)
69 {
70     if (!PreCreation(parent, pos, size) ||
71           !CreateBase(parent, id, pos, size, style, wxDefaultValidator, name))
72     {
73         wxFAIL_MSG(wxT("wxGtkCalendarCtrl creation failed"));
74         return false;
75     }
76 
77     m_widget = gtk_calendar_new();
78     g_object_ref(m_widget);
79     SetDate(date.IsValid() ? date : wxDateTime::Today());
80 
81     if (style & wxCAL_NO_MONTH_CHANGE)
82         g_object_set (G_OBJECT (m_widget), "no-month-change", true, NULL);
83     if (style & wxCAL_SHOW_WEEK_NUMBERS)
84         g_object_set (G_OBJECT (m_widget), "show-week-numbers", true, NULL);
85 
86     g_signal_connect_after(m_widget, "day-selected",
87                            G_CALLBACK (gtk_day_selected_callback),
88                            this);
89     g_signal_connect_after(m_widget, "day-selected-double-click",
90                            G_CALLBACK (gtk_day_selected_double_click_callback),
91                            this);
92     g_signal_connect_after(m_widget, "month-changed",
93                            G_CALLBACK (gtk_month_changed_callback),
94                            this);
95 
96     // connect callbacks that send deprecated events
97     g_signal_connect_after(m_widget, "prev-month",
98                            G_CALLBACK (gtk_prev_month_callback),
99                            this);
100     g_signal_connect_after(m_widget, "next-month",
101                            G_CALLBACK (gtk_prev_month_callback),
102                            this);
103     g_signal_connect_after(m_widget, "prev-year",
104                            G_CALLBACK (gtk_prev_year_callback),
105                            this);
106     g_signal_connect_after(m_widget, "next-year",
107                            G_CALLBACK (gtk_prev_year_callback),
108                            this);
109 
110     m_parent->DoAddChild(this);
111 
112     PostCreation(size);
113 
114     return true;
115 }
116 
GTKGenerateEvent(wxEventType type)117 void wxGtkCalendarCtrl::GTKGenerateEvent(wxEventType type)
118 {
119     // First check if the new date is in the specified range.
120     wxDateTime dt = GetDate();
121     if ( !IsInValidRange(dt) )
122     {
123         if ( m_validStart.IsValid() && dt < m_validStart )
124             dt = m_validStart;
125         else
126             dt = m_validEnd;
127 
128         SetDate(dt);
129 
130         return;
131     }
132 
133     if ( type == wxEVT_CALENDAR_SEL_CHANGED )
134     {
135         // Don't generate this event if the new date is the same as the old
136         // one.
137         if ( m_selectedDate == dt )
138             return;
139 
140         m_selectedDate = dt;
141 
142         GenerateEvent(type);
143 
144         // Also send the deprecated event together with the new one.
145         GenerateEvent(wxEVT_CALENDAR_DAY_CHANGED);
146     }
147     else
148     {
149         GenerateEvent(type);
150     }
151 }
152 
IsInValidRange(const wxDateTime & dt) const153 bool wxGtkCalendarCtrl::IsInValidRange(const wxDateTime& dt) const
154 {
155     return (!m_validStart.IsValid() || m_validStart <= dt) &&
156                 (!m_validEnd.IsValid() || dt <= m_validEnd);
157 }
158 
159 bool
SetDateRange(const wxDateTime & lowerdate,const wxDateTime & upperdate)160 wxGtkCalendarCtrl::SetDateRange(const wxDateTime& lowerdate,
161                                 const wxDateTime& upperdate)
162 {
163     if ( lowerdate.IsValid() && upperdate.IsValid() && lowerdate >= upperdate )
164         return false;
165 
166     m_validStart = lowerdate;
167     m_validEnd = upperdate;
168 
169     return true;
170 }
171 
172 bool
GetDateRange(wxDateTime * lowerdate,wxDateTime * upperdate) const173 wxGtkCalendarCtrl::GetDateRange(wxDateTime *lowerdate,
174                                 wxDateTime *upperdate) const
175 {
176     if ( lowerdate )
177         *lowerdate = m_validStart;
178     if ( upperdate )
179         *upperdate = m_validEnd;
180 
181     return m_validStart.IsValid() || m_validEnd.IsValid();
182 }
183 
184 
EnableMonthChange(bool enable)185 bool wxGtkCalendarCtrl::EnableMonthChange(bool enable)
186 {
187     if ( !wxCalendarCtrlBase::EnableMonthChange(enable) )
188         return false;
189 
190     g_object_set (G_OBJECT (m_widget), "no-month-change", !enable, NULL);
191 
192     return true;
193 }
194 
195 
SetDate(const wxDateTime & date)196 bool wxGtkCalendarCtrl::SetDate(const wxDateTime& date)
197 {
198     wxCHECK_MSG( date.IsValid(), false, "invalid date" );
199 
200     if ( !IsInValidRange(date) )
201         return false;
202 
203     g_signal_handlers_block_by_func(m_widget,
204         (gpointer) gtk_day_selected_callback, this);
205     g_signal_handlers_block_by_func(m_widget,
206         (gpointer) gtk_month_changed_callback, this);
207 
208     m_selectedDate = date;
209     int year = date.GetYear();
210     int month = date.GetMonth();
211     int day = date.GetDay();
212     gtk_calendar_select_month(GTK_CALENDAR(m_widget), month, year);
213     gtk_calendar_select_day(GTK_CALENDAR(m_widget), day);
214 
215     g_signal_handlers_unblock_by_func( m_widget,
216         (gpointer) gtk_month_changed_callback, this);
217     g_signal_handlers_unblock_by_func( m_widget,
218         (gpointer) gtk_day_selected_callback, this);
219 
220     return true;
221 }
222 
GetDate() const223 wxDateTime wxGtkCalendarCtrl::GetDate() const
224 {
225     guint year, monthGTK, day;
226     gtk_calendar_get_date(GTK_CALENDAR(m_widget), &year, &monthGTK, &day);
227 
228     // GTK may return an invalid date, this happens at least when switching the
229     // month (or the year in case of February in a leap year) and the new month
230     // has fewer days than the currently selected one making the currently
231     // selected day invalid, e.g. just choosing May 31 and going back a month
232     // results in the date being (non existent) April 31 when we're called from
233     // gtk_prev_month_callback(). We need to manually work around this to avoid
234     // asserts from wxDateTime ctor.
235     const wxDateTime::Month month = static_cast<wxDateTime::Month>(monthGTK);
236     const guint dayMax = wxDateTime::GetNumberOfDays(month, year);
237     if ( day > dayMax )
238         day = dayMax;
239 
240     return wxDateTime(day, month, year);
241 }
242 
Mark(size_t day,bool mark)243 void wxGtkCalendarCtrl::Mark(size_t day, bool mark)
244 {
245     if (mark)
246         gtk_calendar_mark_day(GTK_CALENDAR(m_widget), day);
247     else
248         gtk_calendar_unmark_day(GTK_CALENDAR(m_widget), day);
249 }
250 
251 #endif // wxUSE_CALENDARCTRL
252