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