Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-cell-date-edit-text.c
blob2c7043425c85d11150fa95c9d0efc12fd73dbf15
1 /*
2 * ECellDateEditText - a subclass of ECellText used to show and edit the text
3 * representation of the date, from a ECalComponentDateTime* model value.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Authors:
18 * Damon Chaplin <damon@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
23 #include "evolution-config.h"
25 #include <sys/time.h>
26 #include <time.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <glib/gi18n.h>
30 #include <libecal/libecal.h>
32 #include "e-cell-date-edit-text.h"
34 #define E_CELL_DATE_EDIT_TEXT_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), E_TYPE_CELL_DATE_EDIT_TEXT, ECellDateEditTextPrivate))
38 struct _ECellDateEditTextPrivate {
40 /* The timezone to display the date in. */
41 icaltimezone *timezone;
43 /* Whether to display in 24-hour format. */
44 gboolean use_24_hour_format;
47 enum {
48 PROP_0,
49 PROP_TIMEZONE,
50 PROP_USE_24_HOUR_FORMAT
53 G_DEFINE_TYPE (
54 ECellDateEditText,
55 e_cell_date_edit_text,
56 E_TYPE_CELL_TEXT)
58 static void
59 cell_date_edit_text_set_property (GObject *object,
60 guint property_id,
61 const GValue *value,
62 GParamSpec *pspec)
64 switch (property_id) {
65 case PROP_TIMEZONE:
66 e_cell_date_edit_text_set_timezone (
67 E_CELL_DATE_EDIT_TEXT (object),
68 g_value_get_pointer (value));
69 return;
71 case PROP_USE_24_HOUR_FORMAT:
72 e_cell_date_edit_text_set_use_24_hour_format (
73 E_CELL_DATE_EDIT_TEXT (object),
74 g_value_get_boolean (value));
75 return;
78 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
81 static void
82 cell_date_edit_text_get_property (GObject *object,
83 guint property_id,
84 GValue *value,
85 GParamSpec *pspec)
87 switch (property_id) {
88 case PROP_TIMEZONE:
89 g_value_set_pointer (
90 value,
91 e_cell_date_edit_text_get_timezone (
92 E_CELL_DATE_EDIT_TEXT (object)));
93 return;
95 case PROP_USE_24_HOUR_FORMAT:
96 g_value_set_boolean (
97 value,
98 e_cell_date_edit_text_get_use_24_hour_format (
99 E_CELL_DATE_EDIT_TEXT (object)));
100 return;
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
106 static gchar *
107 cell_date_edit_text_get_text (ECellText *cell,
108 ETableModel *model,
109 gint col,
110 gint row)
112 ECellDateEditText *ecd = E_CELL_DATE_EDIT_TEXT (cell);
113 ECellDateEditValue *dv = e_table_model_value_at (model, col, row);
114 icaltimezone *timezone;
115 struct tm tmp_tm;
116 gchar *res;
118 if (!dv)
119 return g_strdup ("");
121 timezone = e_cell_date_edit_text_get_timezone (ecd);
123 /* Note that although the property may be in a different
124 * timezone, we convert it to the current timezone to display
125 * it in the table. If the user actually edits the value,
126 * it will be set to the current timezone. See set_value (). */
127 tmp_tm = icaltimetype_to_tm_with_zone (&dv->tt, dv->zone, timezone);
129 res = e_datetime_format_format_tm (
130 "calendar", "table", dv->tt.is_date ?
131 DTFormatKindDate : DTFormatKindDateTime, &tmp_tm);
133 e_table_model_free_value (model, col, dv);
135 return res;
138 static void
139 cell_date_edit_text_free_text (ECellText *cell,
140 ETableModel *model,
141 gint col,
142 gchar *text)
144 g_free (text);
147 /* FIXME: We need to set the "transient_for" property for the dialog. */
148 static void
149 show_date_warning (ECellDateEditText *ecd)
151 GtkWidget *dialog;
152 gchar buffer[64], *format;
153 time_t t;
154 struct tm *tmp_tm;
156 t = time (NULL);
157 /* We are only using this as an example, so the timezone doesn't
158 * matter. */
159 tmp_tm = localtime (&t);
161 if (e_cell_date_edit_text_get_use_24_hour_format (ecd))
162 /* strftime format of a weekday, a date and a time, 24-hour. */
163 format = _("%a %m/%d/%Y %H:%M:%S");
164 else
165 /* strftime format of a weekday, a date and a time, 12-hour. */
166 format = _("%a %m/%d/%Y %I:%M:%S %p");
168 e_utf8_strftime (buffer, sizeof (buffer), format, tmp_tm);
170 dialog = gtk_message_dialog_new (
171 NULL, 0,
172 GTK_MESSAGE_ERROR,
173 GTK_BUTTONS_OK,
174 _("The date must be entered in the format: \n%s"),
175 buffer);
176 gtk_dialog_run (GTK_DIALOG (dialog));
177 gtk_widget_destroy (dialog);
180 static void
181 cell_date_edit_text_set_value (ECellText *cell,
182 ETableModel *model,
183 gint col,
184 gint row,
185 const gchar *text)
187 ECellDateEditText *ecd = E_CELL_DATE_EDIT_TEXT (cell);
188 ETimeParseStatus status;
189 struct tm tmp_tm;
190 ECellDateEditValue dv;
191 ECellDateEditValue *value;
192 gboolean is_date = TRUE;
194 /* Try to parse just a date first. If the value is only a date, we
195 * use a DATE value. */
196 status = e_time_parse_date (text, &tmp_tm);
197 if (status == E_TIME_PARSE_INVALID) {
198 is_date = FALSE;
199 status = e_time_parse_date_and_time (text, &tmp_tm);
201 if (status == E_TIME_PARSE_INVALID) {
202 show_date_warning (ecd);
203 return;
207 if (status == E_TIME_PARSE_NONE) {
208 value = NULL;
209 } else {
210 dv.tt = icaltime_null_time ();
212 dv.tt.year = tmp_tm.tm_year + 1900;
213 dv.tt.month = tmp_tm.tm_mon + 1;
214 dv.tt.day = tmp_tm.tm_mday;
215 dv.tt.hour = tmp_tm.tm_hour;
216 dv.tt.minute = tmp_tm.tm_min;
217 dv.tt.second = tmp_tm.tm_sec;
218 dv.tt.is_date = is_date;
220 /* FIXME: We assume it is being set to the current timezone.
221 * Is that OK? */
222 if (is_date) {
223 dv.zone = NULL;
224 } else {
225 dv.zone = e_cell_date_edit_text_get_timezone (ecd);
228 value = &dv;
231 e_table_model_set_value_at (model, col, row, value);
234 static void
235 e_cell_date_edit_text_class_init (ECellDateEditTextClass *class)
237 GObjectClass *object_class;
238 ECellTextClass *cell_text_class;
240 g_type_class_add_private (class, sizeof (ECellDateEditTextPrivate));
242 object_class = G_OBJECT_CLASS (class);
243 object_class->set_property = cell_date_edit_text_set_property;
244 object_class->get_property = cell_date_edit_text_get_property;
246 cell_text_class = E_CELL_TEXT_CLASS (class);
247 cell_text_class->get_text = cell_date_edit_text_get_text;
248 cell_text_class->free_text = cell_date_edit_text_free_text;
249 cell_text_class->set_value = cell_date_edit_text_set_value;
251 g_object_class_install_property (
252 object_class,
253 PROP_TIMEZONE,
254 g_param_spec_pointer (
255 "timezone",
256 "Time Zone",
257 NULL,
258 G_PARAM_READWRITE));
260 g_object_class_install_property (
261 object_class,
262 PROP_USE_24_HOUR_FORMAT,
263 g_param_spec_boolean (
264 "use-24-hour-format",
265 "Use 24-Hour Format",
266 NULL,
267 TRUE,
268 G_PARAM_READWRITE));
271 static void
272 e_cell_date_edit_text_init (ECellDateEditText *ecd)
274 ecd->priv = E_CELL_DATE_EDIT_TEXT_GET_PRIVATE (ecd);
276 ecd->priv->timezone = icaltimezone_get_utc_timezone ();
277 ecd->priv->use_24_hour_format = TRUE;
281 * e_cell_date_edit_text_new:
283 * Creates a new ECell renderer that can be used to render and edit dates that
284 * that come from the model. The value returned from the model is
285 * interpreted as being a ECalComponentDateTime*.
287 * Returns: an ECell object that can be used to render dates.
289 ECell *
290 e_cell_date_edit_text_new (const gchar *fontname,
291 GtkJustification justify)
293 ECell *cell;
295 cell = g_object_new (E_TYPE_CELL_DATE_EDIT_TEXT, NULL);
296 e_cell_text_construct (E_CELL_TEXT (cell), fontname, justify);
298 return cell;
301 icaltimezone *
302 e_cell_date_edit_text_get_timezone (ECellDateEditText *ecd)
304 g_return_val_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd), NULL);
306 return ecd->priv->timezone;
309 void
310 e_cell_date_edit_text_set_timezone (ECellDateEditText *ecd,
311 icaltimezone *timezone)
313 g_return_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd));
315 if (ecd->priv->timezone == timezone)
316 return;
318 ecd->priv->timezone = timezone;
320 g_object_notify (G_OBJECT (ecd), "timezone");
323 gboolean
324 e_cell_date_edit_text_get_use_24_hour_format (ECellDateEditText *ecd)
326 g_return_val_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd), FALSE);
328 return ecd->priv->use_24_hour_format;
331 void
332 e_cell_date_edit_text_set_use_24_hour_format (ECellDateEditText *ecd,
333 gboolean use_24_hour)
335 g_return_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd));
337 if (ecd->priv->use_24_hour_format == use_24_hour)
338 return;
340 ecd->priv->use_24_hour_format = use_24_hour;
342 g_object_notify (G_OBJECT (ecd), "use-24-hour-format");
345 gint
346 e_cell_date_edit_compare_cb (gconstpointer a,
347 gconstpointer b,
348 gpointer cmp_cache)
350 ECellDateEditValue *dv1 = (ECellDateEditValue *) a;
351 ECellDateEditValue *dv2 = (ECellDateEditValue *) b;
352 struct icaltimetype tt;
354 /* First check if either is NULL. NULL dates sort last. */
355 if (!dv1 || !dv2) {
356 if (dv1 == dv2)
357 return 0;
358 else if (dv1)
359 return -1;
360 else
361 return 1;
364 /* Copy the 2nd value and convert it to the same timezone as the first. */
365 tt = dv2->tt;
367 icaltimezone_convert_time (&tt, dv2->zone, dv1->zone);
369 /* Now we can compare them. */
370 return icaltime_compare (dv1->tt, tt);