Bug 793125 - Crash due to popup menus left attached too long
[evolution.git] / src / calendar / gui / e-date-time-list.c
blob681e0a2bcf3002a1e4a1583b81b33474fb8ce37d
1 /*
2 * EDateTimeList - list of calendar dates/times with GtkTreeModel interface.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 * Authors:
18 * Hans Petter Jansson <hpj@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 #include "evolution-config.h"
26 #include "e-date-time-list.h"
28 #include <string.h>
29 #include <libecal/libecal.h>
31 /* XXX Was it really necessary to implement a custom GtkTreeModel for a
32 * one-column list store? There's no mention of why this was done. */
34 #define G_LIST(x) ((GList *) x)
35 #define E_DATE_TIME_LIST_IS_SORTED(list) \
36 (E_DATE_TIME_LIST (list)->sort_column_id != -2)
37 #define IS_VALID_ITER(dt_list, iter) \
38 (iter != NULL && iter->user_data != NULL && \
39 dt_list->priv->stamp == iter->stamp)
41 struct _EDateTimeListPrivate {
42 gint stamp;
43 GList *list;
45 guint columns_dirty : 1;
47 gboolean use_24_hour_format;
48 icaltimezone *zone;
51 enum {
52 PROP_0,
53 PROP_USE_24_HOUR_FORMAT,
54 PROP_TIMEZONE
57 static GType column_types[E_DATE_TIME_LIST_NUM_COLUMNS];
59 static void e_date_time_list_tree_model_init (GtkTreeModelIface *iface);
61 G_DEFINE_TYPE_WITH_CODE (
62 EDateTimeList, e_date_time_list, G_TYPE_OBJECT,
63 G_IMPLEMENT_INTERFACE (
64 GTK_TYPE_TREE_MODEL, e_date_time_list_tree_model_init))
66 static void
67 free_datetime (struct icaltimetype *itt)
69 g_free (itt);
72 static struct icaltimetype *
73 copy_datetime (const struct icaltimetype itt)
75 struct icaltimetype *itt_copy;
77 itt_copy = g_new0 (struct icaltimetype, 1);
78 *itt_copy = itt;
80 return itt_copy;
83 static gint
84 compare_datetime (const struct icaltimetype *itt1,
85 const struct icaltimetype *itt2)
87 return icaltime_compare (*itt1, *itt2);
90 static void
91 all_rows_deleted (EDateTimeList *date_time_list)
93 GtkTreePath *path;
94 gint i;
96 if (!date_time_list->priv->list)
97 return;
99 path = gtk_tree_path_new ();
100 i = g_list_length (date_time_list->priv->list);
101 gtk_tree_path_append_index (path, i);
103 for (; i >= 0; i--) {
104 gtk_tree_model_row_deleted (GTK_TREE_MODEL (date_time_list), path);
105 gtk_tree_path_prev (path);
108 gtk_tree_path_free (path);
111 static void
112 row_deleted (EDateTimeList *date_time_list,
113 gint n)
115 GtkTreePath *path;
117 path = gtk_tree_path_new ();
118 gtk_tree_path_append_index (path, n);
119 gtk_tree_model_row_deleted (GTK_TREE_MODEL (date_time_list), path);
120 gtk_tree_path_free (path);
123 static void
124 row_added (EDateTimeList *date_time_list,
125 gint n)
127 GtkTreePath *path;
128 GtkTreeIter iter;
130 path = gtk_tree_path_new ();
131 gtk_tree_path_append_index (path, n);
133 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (date_time_list), &iter, path))
134 gtk_tree_model_row_inserted (GTK_TREE_MODEL (date_time_list), path, &iter);
136 gtk_tree_path_free (path);
139 static void
140 row_updated (EDateTimeList *date_time_list,
141 gint n)
143 GtkTreePath *path;
144 GtkTreeIter iter;
146 path = gtk_tree_path_new ();
147 gtk_tree_path_append_index (path, n);
149 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (date_time_list), &iter, path))
150 gtk_tree_model_row_changed (GTK_TREE_MODEL (date_time_list), path, &iter);
152 gtk_tree_path_free (path);
155 /* Builds a static string out of an exception date */
156 static gchar *
157 get_exception_string (EDateTimeList *date_time_list,
158 struct icaltimetype *itt)
160 static gchar buf[256];
161 struct icaltimetype tt;
162 struct tm tmp_tm;
163 icaltimezone *zone;
164 gboolean use_24_hour_format;
166 use_24_hour_format = e_date_time_list_get_use_24_hour_format (date_time_list);
167 zone = e_date_time_list_get_timezone (date_time_list);
169 tt = *itt;
171 if (zone)
172 tt = icaltime_convert_to_zone (tt, zone);
174 tmp_tm.tm_year = tt.year - 1900;
175 tmp_tm.tm_mon = tt.month - 1;
176 tmp_tm.tm_mday = tt.day;
177 tmp_tm.tm_hour = tt.hour;
178 tmp_tm.tm_min = tt.minute;
179 tmp_tm.tm_sec = tt.second;
180 tmp_tm.tm_isdst = -1;
182 tmp_tm.tm_wday = time_day_of_week (
183 tt.day,
184 tt.month - 1,
185 tt.year);
187 e_time_format_date_and_time (
188 &tmp_tm, use_24_hour_format,
189 FALSE, FALSE, buf, sizeof (buf));
191 return buf;
194 static void
195 date_time_list_set_property (GObject *object,
196 guint property_id,
197 const GValue *value,
198 GParamSpec *pspec)
200 switch (property_id) {
201 case PROP_USE_24_HOUR_FORMAT:
202 e_date_time_list_set_use_24_hour_format (
203 E_DATE_TIME_LIST (object),
204 g_value_get_boolean (value));
205 return;
207 case PROP_TIMEZONE:
208 e_date_time_list_set_timezone (
209 E_DATE_TIME_LIST (object),
210 g_value_get_pointer (value));
211 return;
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
217 static void
218 date_time_list_get_property (GObject *object,
219 guint property_id,
220 GValue *value,
221 GParamSpec *pspec)
223 switch (property_id) {
224 case PROP_USE_24_HOUR_FORMAT:
225 g_value_set_boolean (
226 value,
227 e_date_time_list_get_use_24_hour_format (
228 E_DATE_TIME_LIST (object)));
229 return;
231 case PROP_TIMEZONE:
232 g_value_set_pointer (
233 value, e_date_time_list_get_timezone (
234 E_DATE_TIME_LIST (object)));
235 return;
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
241 static GtkTreeModelFlags
242 date_time_list_get_flags (GtkTreeModel *tree_model)
244 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), 0);
246 return GTK_TREE_MODEL_LIST_ONLY;
249 static gint
250 date_time_list_get_n_columns (GtkTreeModel *tree_model)
252 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
254 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), 0);
256 date_time_list->priv->columns_dirty = TRUE;
257 return E_DATE_TIME_LIST_NUM_COLUMNS;
260 static GType
261 date_time_list_get_column_type (GtkTreeModel *tree_model,
262 gint index)
264 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
266 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), G_TYPE_INVALID);
267 g_return_val_if_fail (index < E_DATE_TIME_LIST_NUM_COLUMNS &&
268 index >= 0, G_TYPE_INVALID);
270 date_time_list->priv->columns_dirty = TRUE;
271 return column_types[index];
274 static gboolean
275 date_time_list_get_iter (GtkTreeModel *tree_model,
276 GtkTreeIter *iter,
277 GtkTreePath *path)
279 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
280 GList *l;
281 gint i;
283 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
284 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
286 if (!date_time_list->priv->list)
287 return FALSE;
289 date_time_list->priv->columns_dirty = TRUE;
291 i = gtk_tree_path_get_indices (path)[0];
292 l = g_list_nth (date_time_list->priv->list, i);
293 if (!l)
294 return FALSE;
296 iter->user_data = l;
297 iter->stamp = date_time_list->priv->stamp;
298 return TRUE;
301 static GtkTreePath *
302 date_time_list_get_path (GtkTreeModel *tree_model,
303 GtkTreeIter *iter)
305 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
306 GtkTreePath *retval;
307 GList *l;
309 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), NULL);
310 g_return_val_if_fail (iter->stamp == E_DATE_TIME_LIST (tree_model)->priv->stamp, NULL);
312 l = iter->user_data;
313 retval = gtk_tree_path_new ();
314 gtk_tree_path_append_index (retval, g_list_position (date_time_list->priv->list, l));
315 return retval;
318 static void
319 date_time_list_get_value (GtkTreeModel *tree_model,
320 GtkTreeIter *iter,
321 gint column,
322 GValue *value)
324 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
325 struct icaltimetype *itt;
326 GList *l;
327 const gchar *str;
329 g_return_if_fail (E_IS_DATE_TIME_LIST (tree_model));
330 g_return_if_fail (column < E_DATE_TIME_LIST_NUM_COLUMNS);
331 g_return_if_fail (E_DATE_TIME_LIST (tree_model)->priv->stamp == iter->stamp);
332 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
334 g_value_init (value, column_types[column]);
336 if (!date_time_list->priv->list)
337 return;
339 l = iter->user_data;
340 itt = l->data;
342 if (!itt)
343 return;
345 switch (column) {
346 case E_DATE_TIME_LIST_COLUMN_DESCRIPTION:
347 str = get_exception_string (date_time_list, itt);
348 g_value_set_string (value, str);
349 break;
353 static gboolean
354 date_time_list_iter_next (GtkTreeModel *tree_model,
355 GtkTreeIter *iter)
357 GList *l;
359 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
360 g_return_val_if_fail (IS_VALID_ITER (E_DATE_TIME_LIST (tree_model), iter), FALSE);
362 if (!E_DATE_TIME_LIST (tree_model)->priv->list)
363 return FALSE;
365 l = iter->user_data;
366 l = g_list_next (l);
367 if (l) {
368 iter->user_data = l;
369 return TRUE;
372 return FALSE;
375 static gboolean
376 date_time_list_iter_children (GtkTreeModel *tree_model,
377 GtkTreeIter *iter,
378 GtkTreeIter *parent)
380 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
382 /* this is a list, nodes have no children */
383 if (parent)
384 return FALSE;
386 /* but if parent == NULL we return the list itself as children of the
387 * "root" */
389 if (!date_time_list->priv->list)
390 return FALSE;
392 iter->stamp = E_DATE_TIME_LIST (tree_model)->priv->stamp;
393 iter->user_data = date_time_list->priv->list;
394 return TRUE;
397 static gboolean
398 date_time_list_iter_has_child (GtkTreeModel *tree_model,
399 GtkTreeIter *iter)
401 g_return_val_if_fail (IS_VALID_ITER (E_DATE_TIME_LIST (tree_model), iter), FALSE);
402 return FALSE;
405 static gint
406 date_time_list_iter_n_children (GtkTreeModel *tree_model,
407 GtkTreeIter *iter)
409 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
411 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), -1);
413 if (iter == NULL)
414 return g_list_length (date_time_list->priv->list);
416 g_return_val_if_fail (E_DATE_TIME_LIST (tree_model)->priv->stamp == iter->stamp, -1);
417 return 0;
420 static gboolean
421 date_time_list_iter_nth_child (GtkTreeModel *tree_model,
422 GtkTreeIter *iter,
423 GtkTreeIter *parent,
424 gint n)
426 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
428 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
430 if (parent)
431 return FALSE;
433 if (date_time_list->priv->list) {
434 GList *l;
436 l = g_list_nth (date_time_list->priv->list, n);
437 if (!l)
438 return FALSE;
440 iter->stamp = date_time_list->priv->stamp;
441 iter->user_data = l;
442 return TRUE;
445 return FALSE;
448 static gboolean
449 date_time_list_iter_parent (GtkTreeModel *tree_model,
450 GtkTreeIter *iter,
451 GtkTreeIter *child)
453 return FALSE;
456 static void
457 e_date_time_list_class_init (EDateTimeListClass *class)
459 GObjectClass *object_class;
461 g_type_class_add_private (class, sizeof (EDateTimeListPrivate));
463 object_class = G_OBJECT_CLASS (class);
464 object_class->set_property = date_time_list_set_property;
465 object_class->get_property = date_time_list_get_property;
467 g_object_class_install_property (
468 object_class,
469 PROP_USE_24_HOUR_FORMAT,
470 g_param_spec_boolean (
471 "use-24-hour-format",
472 "Use 24-hour Format",
473 NULL,
474 FALSE,
475 G_PARAM_READWRITE));
477 g_object_class_install_property (
478 object_class,
479 PROP_TIMEZONE,
480 g_param_spec_pointer (
481 "timezone",
482 "Time Zone",
483 NULL,
484 G_PARAM_READWRITE));
486 column_types[E_DATE_TIME_LIST_COLUMN_DESCRIPTION] = G_TYPE_STRING;
489 static void
490 e_date_time_list_init (EDateTimeList *date_time_list)
492 date_time_list->priv = G_TYPE_INSTANCE_GET_PRIVATE (date_time_list, E_TYPE_DATE_TIME_LIST, EDateTimeListPrivate);
494 date_time_list->priv->stamp = g_random_int ();
495 date_time_list->priv->columns_dirty = FALSE;
496 date_time_list->priv->list = NULL;
499 static void
500 e_date_time_list_tree_model_init (GtkTreeModelIface *iface)
502 iface->get_flags = date_time_list_get_flags;
503 iface->get_n_columns = date_time_list_get_n_columns;
504 iface->get_column_type = date_time_list_get_column_type;
505 iface->get_iter = date_time_list_get_iter;
506 iface->get_path = date_time_list_get_path;
507 iface->get_value = date_time_list_get_value;
508 iface->iter_next = date_time_list_iter_next;
509 iface->iter_children = date_time_list_iter_children;
510 iface->iter_has_child = date_time_list_iter_has_child;
511 iface->iter_n_children = date_time_list_iter_n_children;
512 iface->iter_nth_child = date_time_list_iter_nth_child;
513 iface->iter_parent = date_time_list_iter_parent;
516 EDateTimeList *
517 e_date_time_list_new (void)
519 return g_object_new (E_TYPE_DATE_TIME_LIST, NULL);
522 struct icaltimetype *
523 e_date_time_list_get_date_time (EDateTimeList *date_time_list,
524 GtkTreeIter *iter)
526 g_return_val_if_fail (IS_VALID_ITER (date_time_list, iter), NULL);
528 return G_LIST (iter->user_data)->data;
531 void
532 e_date_time_list_set_date_time (EDateTimeList *date_time_list,
533 GtkTreeIter *iter,
534 const struct icaltimetype itt)
536 struct icaltimetype *itt_old;
538 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
540 itt_old = G_LIST (iter->user_data)->data;
541 free_datetime (itt_old);
542 G_LIST (iter->user_data)->data = copy_datetime (itt);
543 row_updated (date_time_list,
544 g_list_position (date_time_list->priv->list, G_LIST (iter->user_data)));
547 gboolean
548 e_date_time_list_get_use_24_hour_format (EDateTimeList *date_time_list)
550 g_return_val_if_fail (E_IS_DATE_TIME_LIST (date_time_list), FALSE);
552 return date_time_list->priv->use_24_hour_format;
555 void
556 e_date_time_list_set_use_24_hour_format (EDateTimeList *date_time_list,
557 gboolean use_24_hour_format)
559 g_return_if_fail (E_IS_DATE_TIME_LIST (date_time_list));
561 if (date_time_list->priv->use_24_hour_format == use_24_hour_format)
562 return;
564 date_time_list->priv->use_24_hour_format = use_24_hour_format;
566 g_object_notify (G_OBJECT (date_time_list), "use-24-hour-format");
569 icaltimezone *
570 e_date_time_list_get_timezone (EDateTimeList *date_time_list)
572 g_return_val_if_fail (E_IS_DATE_TIME_LIST (date_time_list), NULL);
574 return date_time_list->priv->zone;
577 void
578 e_date_time_list_set_timezone (EDateTimeList *date_time_list,
579 icaltimezone *zone)
581 g_return_if_fail (E_IS_DATE_TIME_LIST (date_time_list));
583 if (date_time_list->priv->zone == zone)
584 return;
586 date_time_list->priv->zone = zone;
588 g_object_notify (G_OBJECT (date_time_list), "timezone");
591 void
592 e_date_time_list_append (EDateTimeList *date_time_list,
593 GtkTreeIter *iter,
594 const struct icaltimetype itt)
596 g_return_if_fail (icaltime_is_valid_time (itt));
598 if (g_list_find_custom (date_time_list->priv->list, &itt, (GCompareFunc) compare_datetime) == NULL) {
599 date_time_list->priv->list = g_list_append (date_time_list->priv->list, copy_datetime (itt));
600 row_added (date_time_list, g_list_length (date_time_list->priv->list) - 1);
603 if (iter) {
604 iter->user_data = g_list_last (date_time_list->priv->list);
605 iter->stamp = date_time_list->priv->stamp;
609 void
610 e_date_time_list_remove (EDateTimeList *date_time_list,
611 GtkTreeIter *iter)
613 gint n;
615 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
617 n = g_list_position (date_time_list->priv->list, G_LIST (iter->user_data));
618 free_datetime (G_LIST (iter->user_data)->data);
619 date_time_list->priv->list = g_list_delete_link (
620 date_time_list->priv->list, G_LIST (iter->user_data));
621 row_deleted (date_time_list, n);
624 void
625 e_date_time_list_clear (EDateTimeList *date_time_list)
627 GList *l;
629 all_rows_deleted (date_time_list);
631 for (l = date_time_list->priv->list; l; l = g_list_next (l)) {
632 free_datetime (l->data);
635 g_list_free (date_time_list->priv->list);
636 date_time_list->priv->list = NULL;