1 /*
2 * ECellDateEditText - a subclass of ECellText used to show and edit the text
3 * representation of the date, from a ECalComponentDateTime* model value.
4 *
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.
8 *
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.
13 *
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/>.
16 *
17 * Authors:
18 * Damon Chaplin <damon@ximian.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 */
22
23 #include "evolution-config.h"
24
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>
31
32 #include "e-cell-date-edit-text.h"
33
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))
37
38 struct _ECellDateEditTextPrivate {
39
40 /* The timezone to display the date in. */
41 ICalTimezone *timezone;
42
43 /* Whether to display in 24-hour format. */
44 gboolean use_24_hour_format;
45 };
46
47 enum {
48 PROP_0,
49 PROP_TIMEZONE,
50 PROP_USE_24_HOUR_FORMAT
51 };
52
G_DEFINE_TYPE(ECellDateEditText,e_cell_date_edit_text,E_TYPE_CELL_TEXT)53 G_DEFINE_TYPE (
54 ECellDateEditText,
55 e_cell_date_edit_text,
56 E_TYPE_CELL_TEXT)
57
58 static void
59 cell_date_edit_text_set_property (GObject *object,
60 guint property_id,
61 const GValue *value,
62 GParamSpec *pspec)
63 {
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_object (value));
69 return;
70
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;
76 }
77
78 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
79 }
80
81 static void
cell_date_edit_text_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)82 cell_date_edit_text_get_property (GObject *object,
83 guint property_id,
84 GValue *value,
85 GParamSpec *pspec)
86 {
87 switch (property_id) {
88 case PROP_TIMEZONE:
89 g_value_set_object (
90 value,
91 e_cell_date_edit_text_get_timezone (
92 E_CELL_DATE_EDIT_TEXT (object)));
93 return;
94
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;
101 }
102
103 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104 }
105
106 static void
cell_date_edit_text_finalize(GObject * object)107 cell_date_edit_text_finalize (GObject *object)
108 {
109 ECellDateEditText *ecd = E_CELL_DATE_EDIT_TEXT (object);
110
111 g_clear_object (&ecd->priv->timezone);
112
113 /* Chain up to parent's method. */
114 G_OBJECT_CLASS (e_cell_date_edit_text_parent_class)->finalize (object);
115 }
116
117 static gchar *
cell_date_edit_text_get_text(ECellText * cell,ETableModel * model,gint col,gint row)118 cell_date_edit_text_get_text (ECellText *cell,
119 ETableModel *model,
120 gint col,
121 gint row)
122 {
123 ECellDateEditText *ecd = E_CELL_DATE_EDIT_TEXT (cell);
124 ECellDateEditValue *dv = e_table_model_value_at (model, col, row);
125 ICalTimezone *timezone;
126 ICalTime *tt;
127 struct tm tmp_tm;
128 gchar *res;
129
130 if (!dv)
131 return g_strdup ("");
132
133 timezone = e_cell_date_edit_text_get_timezone (ecd);
134
135 tt = e_cell_date_edit_value_get_time (dv);
136
137 /* Note that although the property may be in a different
138 * timezone, we convert it to the current timezone to display
139 * it in the table. If the user actually edits the value,
140 * it will be set to the current timezone. See set_value (). */
141 tmp_tm = e_cal_util_icaltime_to_tm_with_zone (tt, e_cell_date_edit_value_get_zone (dv), timezone);
142
143 res = e_datetime_format_format_tm (
144 "calendar", "table", i_cal_time_is_date (tt) ?
145 DTFormatKindDate : DTFormatKindDateTime, &tmp_tm);
146
147 e_table_model_free_value (model, col, dv);
148
149 return res;
150 }
151
152 static void
cell_date_edit_text_free_text(ECellText * cell,ETableModel * model,gint col,gchar * text)153 cell_date_edit_text_free_text (ECellText *cell,
154 ETableModel *model,
155 gint col,
156 gchar *text)
157 {
158 g_free (text);
159 }
160
161 /* FIXME: We need to set the "transient_for" property for the dialog. */
162 static void
show_date_warning(ECellDateEditText * ecd)163 show_date_warning (ECellDateEditText *ecd)
164 {
165 GtkWidget *dialog;
166 gchar buffer[64], *format;
167 time_t t;
168 struct tm *tmp_tm;
169
170 t = time (NULL);
171 /* We are only using this as an example, so the timezone doesn't
172 * matter. */
173 tmp_tm = localtime (&t);
174
175 if (e_cell_date_edit_text_get_use_24_hour_format (ecd))
176 /* strftime format of a weekday, a date and a time, 24-hour. */
177 format = _("%a %m/%d/%Y %H:%M:%S");
178 else
179 /* strftime format of a weekday, a date and a time, 12-hour. */
180 format = _("%a %m/%d/%Y %I:%M:%S %p");
181
182 e_utf8_strftime (buffer, sizeof (buffer), format, tmp_tm);
183
184 dialog = gtk_message_dialog_new (
185 NULL, 0,
186 GTK_MESSAGE_ERROR,
187 GTK_BUTTONS_OK,
188 _("The date must be entered in the format: \n%s"),
189 buffer);
190 gtk_dialog_run (GTK_DIALOG (dialog));
191 gtk_widget_destroy (dialog);
192 }
193
194 static void
cell_date_edit_text_set_value(ECellText * cell,ETableModel * model,gint col,gint row,const gchar * text)195 cell_date_edit_text_set_value (ECellText *cell,
196 ETableModel *model,
197 gint col,
198 gint row,
199 const gchar *text)
200 {
201 ECellDateEditText *ecd = E_CELL_DATE_EDIT_TEXT (cell);
202 ETimeParseStatus status;
203 struct tm tmp_tm;
204 ECellDateEditValue *dv = NULL;
205 ECellDateEditValue *value;
206 gboolean is_date = TRUE;
207
208 /* Try to parse just a date first. If the value is only a date, we
209 * use a DATE value. */
210 status = e_time_parse_date (text, &tmp_tm);
211 if (status == E_TIME_PARSE_INVALID) {
212 is_date = FALSE;
213 status = e_time_parse_date_and_time (text, &tmp_tm);
214
215 if (status == E_TIME_PARSE_INVALID) {
216 show_date_warning (ecd);
217 return;
218 }
219 }
220
221 if (status == E_TIME_PARSE_NONE) {
222 value = NULL;
223 } else {
224 ICalTime *tt;
225 ICalTimezone *zone;
226
227 tt = e_cal_util_tm_to_icaltime (&tmp_tm, is_date);
228
229 if (is_date) {
230 zone = NULL;
231 } else {
232 zone = e_cell_date_edit_text_get_timezone (ecd);
233 }
234
235 dv = e_cell_date_edit_value_new (tt, zone);
236 value = dv;
237
238 g_clear_object (&tt);
239 }
240
241 e_table_model_set_value_at (model, col, row, value);
242
243 e_cell_date_edit_value_free (dv);
244 }
245
246 static void
e_cell_date_edit_text_class_init(ECellDateEditTextClass * class)247 e_cell_date_edit_text_class_init (ECellDateEditTextClass *class)
248 {
249 GObjectClass *object_class;
250 ECellTextClass *cell_text_class;
251
252 g_type_class_add_private (class, sizeof (ECellDateEditTextPrivate));
253
254 object_class = G_OBJECT_CLASS (class);
255 object_class->set_property = cell_date_edit_text_set_property;
256 object_class->get_property = cell_date_edit_text_get_property;
257 object_class->finalize = cell_date_edit_text_finalize;
258
259 cell_text_class = E_CELL_TEXT_CLASS (class);
260 cell_text_class->get_text = cell_date_edit_text_get_text;
261 cell_text_class->free_text = cell_date_edit_text_free_text;
262 cell_text_class->set_value = cell_date_edit_text_set_value;
263
264 g_object_class_install_property (
265 object_class,
266 PROP_TIMEZONE,
267 g_param_spec_object (
268 "timezone",
269 "Time Zone",
270 NULL,
271 I_CAL_TYPE_TIMEZONE,
272 G_PARAM_READWRITE));
273
274 g_object_class_install_property (
275 object_class,
276 PROP_USE_24_HOUR_FORMAT,
277 g_param_spec_boolean (
278 "use-24-hour-format",
279 "Use 24-Hour Format",
280 NULL,
281 TRUE,
282 G_PARAM_READWRITE));
283 }
284
285 static void
e_cell_date_edit_text_init(ECellDateEditText * ecd)286 e_cell_date_edit_text_init (ECellDateEditText *ecd)
287 {
288 ecd->priv = E_CELL_DATE_EDIT_TEXT_GET_PRIVATE (ecd);
289
290 ecd->priv->timezone = e_cal_util_copy_timezone (i_cal_timezone_get_utc_timezone ());
291 ecd->priv->use_24_hour_format = TRUE;
292 g_object_set (ecd, "use-tabular-numbers", TRUE, NULL);
293 }
294
295 /**
296 * e_cell_date_edit_text_new:
297 *
298 * Creates a new ECell renderer that can be used to render and edit dates that
299 * that come from the model. The value returned from the model is
300 * interpreted as being a ECalComponentDateTime*.
301 *
302 * Returns: an ECell object that can be used to render dates.
303 */
304 ECell *
e_cell_date_edit_text_new(const gchar * fontname,GtkJustification justify)305 e_cell_date_edit_text_new (const gchar *fontname,
306 GtkJustification justify)
307 {
308 ECell *cell;
309
310 cell = g_object_new (E_TYPE_CELL_DATE_EDIT_TEXT, NULL);
311 e_cell_text_construct (E_CELL_TEXT (cell), fontname, justify);
312
313 return cell;
314 }
315
316 ICalTimezone *
e_cell_date_edit_text_get_timezone(ECellDateEditText * ecd)317 e_cell_date_edit_text_get_timezone (ECellDateEditText *ecd)
318 {
319 g_return_val_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd), NULL);
320
321 return ecd->priv->timezone;
322 }
323
324 void
e_cell_date_edit_text_set_timezone(ECellDateEditText * ecd,const ICalTimezone * timezone)325 e_cell_date_edit_text_set_timezone (ECellDateEditText *ecd,
326 const ICalTimezone *timezone)
327 {
328 g_return_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd));
329
330 if (ecd->priv->timezone == timezone)
331 return;
332
333 g_clear_object (&ecd->priv->timezone);
334 ecd->priv->timezone = timezone ? e_cal_util_copy_timezone (timezone) : NULL;
335
336 g_object_notify (G_OBJECT (ecd), "timezone");
337 }
338
339 gboolean
e_cell_date_edit_text_get_use_24_hour_format(ECellDateEditText * ecd)340 e_cell_date_edit_text_get_use_24_hour_format (ECellDateEditText *ecd)
341 {
342 g_return_val_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd), FALSE);
343
344 return ecd->priv->use_24_hour_format;
345 }
346
347 void
e_cell_date_edit_text_set_use_24_hour_format(ECellDateEditText * ecd,gboolean use_24_hour)348 e_cell_date_edit_text_set_use_24_hour_format (ECellDateEditText *ecd,
349 gboolean use_24_hour)
350 {
351 g_return_if_fail (E_IS_CELL_DATE_EDIT_TEXT (ecd));
352
353 if (ecd->priv->use_24_hour_format == use_24_hour)
354 return;
355
356 ecd->priv->use_24_hour_format = use_24_hour;
357
358 g_object_notify (G_OBJECT (ecd), "use-24-hour-format");
359 }
360
361 gint
e_cell_date_edit_compare_cb(gconstpointer a,gconstpointer b,gpointer cmp_cache)362 e_cell_date_edit_compare_cb (gconstpointer a,
363 gconstpointer b,
364 gpointer cmp_cache)
365 {
366 ECellDateEditValue *dv1 = (ECellDateEditValue *) a;
367 ECellDateEditValue *dv2 = (ECellDateEditValue *) b;
368 ICalTime *tt;
369 gint res;
370
371 /* First check if either is NULL. NULL dates sort last. */
372 if (!dv1 || !dv2) {
373 if (dv1 == dv2)
374 return 0;
375 else if (dv1)
376 return -1;
377 else
378 return 1;
379 }
380
381 /* Copy the 2nd value and convert it to the same timezone as the first. */
382 tt = i_cal_time_clone (e_cell_date_edit_value_get_time (dv2));
383 i_cal_time_convert_timezone (tt, e_cell_date_edit_value_get_zone (dv2), e_cell_date_edit_value_get_zone (dv1));
384
385 /* Now we can compare them. */
386 res = i_cal_time_compare (e_cell_date_edit_value_get_time (dv1), tt);
387
388 g_clear_object (&tt);
389
390 return res;
391 }
392
393 struct _ECellDateEditValue {
394 ICalTime *tt;
395 ICalTimezone *zone;
396 };
397
398 ECellDateEditValue *
e_cell_date_edit_value_new(const ICalTime * tt,const ICalTimezone * zone)399 e_cell_date_edit_value_new (const ICalTime *tt,
400 const ICalTimezone *zone)
401 {
402 g_return_val_if_fail (I_CAL_IS_TIME ((ICalTime *) tt), NULL);
403 if (zone)
404 g_return_val_if_fail (I_CAL_IS_TIMEZONE ((ICalTimezone *) zone), NULL);
405
406 return e_cell_date_edit_value_new_take (i_cal_time_clone (tt),
407 zone ? e_cal_util_copy_timezone (zone) : NULL);
408 }
409
410 ECellDateEditValue *
e_cell_date_edit_value_new_take(ICalTime * tt,ICalTimezone * zone)411 e_cell_date_edit_value_new_take (ICalTime *tt,
412 ICalTimezone *zone)
413 {
414 ECellDateEditValue *value;
415
416 g_return_val_if_fail (I_CAL_IS_TIME (tt), NULL);
417 if (zone)
418 g_return_val_if_fail (I_CAL_IS_TIMEZONE (zone), NULL);
419
420 value = g_new0 (ECellDateEditValue, 1);
421 value->tt = tt;
422 value->zone = zone;
423
424 return value;
425 }
426
427 ECellDateEditValue *
e_cell_date_edit_value_copy(const ECellDateEditValue * src)428 e_cell_date_edit_value_copy (const ECellDateEditValue *src)
429 {
430 if (!src)
431 return NULL;
432
433 return e_cell_date_edit_value_new (src->tt, src->zone);
434 }
435
436 void
e_cell_date_edit_value_free(ECellDateEditValue * value)437 e_cell_date_edit_value_free (ECellDateEditValue *value)
438 {
439 if (value) {
440 g_clear_object (&value->tt);
441 g_clear_object (&value->zone);
442 g_free (value);
443 }
444 }
445
446 ICalTime *
e_cell_date_edit_value_get_time(const ECellDateEditValue * value)447 e_cell_date_edit_value_get_time (const ECellDateEditValue *value)
448 {
449 g_return_val_if_fail (value != NULL, NULL);
450
451 return value->tt;
452 }
453
454 void
e_cell_date_edit_value_set_time(ECellDateEditValue * value,const ICalTime * tt)455 e_cell_date_edit_value_set_time (ECellDateEditValue *value,
456 const ICalTime *tt)
457 {
458 g_return_if_fail (value != NULL);
459 g_return_if_fail (I_CAL_IS_TIME ((ICalTime *) tt));
460
461 e_cell_date_edit_value_take_time (value, i_cal_time_clone (tt));
462 }
463
464 void
e_cell_date_edit_value_take_time(ECellDateEditValue * value,ICalTime * tt)465 e_cell_date_edit_value_take_time (ECellDateEditValue *value,
466 ICalTime *tt)
467 {
468 g_return_if_fail (value != NULL);
469 g_return_if_fail (I_CAL_IS_TIME (tt));
470
471 if (value->tt != tt) {
472 g_clear_object (&value->tt);
473 value->tt = tt;
474 } else {
475 g_clear_object (&tt);
476 }
477 }
478
479 ICalTimezone *
e_cell_date_edit_value_get_zone(const ECellDateEditValue * value)480 e_cell_date_edit_value_get_zone (const ECellDateEditValue *value)
481 {
482 g_return_val_if_fail (value != NULL, NULL);
483
484 return value->zone;
485 }
486
487 void
e_cell_date_edit_value_set_zone(ECellDateEditValue * value,const ICalTimezone * zone)488 e_cell_date_edit_value_set_zone (ECellDateEditValue *value,
489 const ICalTimezone *zone)
490 {
491 g_return_if_fail (value != NULL);
492 if (zone)
493 g_return_if_fail (I_CAL_IS_TIMEZONE ((ICalTimezone *) zone));
494
495 e_cell_date_edit_value_take_zone (value, zone ? e_cal_util_copy_timezone (zone) : NULL);
496 }
497
498 void
e_cell_date_edit_value_take_zone(ECellDateEditValue * value,ICalTimezone * zone)499 e_cell_date_edit_value_take_zone (ECellDateEditValue *value,
500 ICalTimezone *zone)
501 {
502 g_return_if_fail (value != NULL);
503 if (zone)
504 g_return_if_fail (I_CAL_IS_TIMEZONE (zone));
505
506 if (zone != value->zone) {
507 g_clear_object (&value->zone);
508 value->zone = zone;
509 } else {
510 g_clear_object (&zone);
511 }
512 }
513