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