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