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