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
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/>.
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"
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
{
45 guint columns_dirty
: 1;
47 gboolean use_24_hour_format
;
53 PROP_USE_24_HOUR_FORMAT
,
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
))
67 free_datetime (struct icaltimetype
*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);
84 compare_datetime (const struct icaltimetype
*itt1
,
85 const struct icaltimetype
*itt2
)
87 return icaltime_compare (*itt1
, *itt2
);
91 all_rows_deleted (EDateTimeList
*date_time_list
)
96 if (!date_time_list
->priv
->list
)
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
);
112 row_deleted (EDateTimeList
*date_time_list
,
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
);
124 row_added (EDateTimeList
*date_time_list
,
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
);
140 row_updated (EDateTimeList
*date_time_list
,
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 */
157 get_exception_string (EDateTimeList
*date_time_list
,
158 struct icaltimetype
*itt
)
160 static gchar buf
[256];
161 struct icaltimetype tt
;
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
);
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 (
187 e_time_format_date_and_time (
188 &tmp_tm
, use_24_hour_format
,
189 FALSE
, FALSE
, buf
, sizeof (buf
));
195 date_time_list_set_property (GObject
*object
,
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
));
208 e_date_time_list_set_timezone (
209 E_DATE_TIME_LIST (object
),
210 g_value_get_pointer (value
));
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
218 date_time_list_get_property (GObject
*object
,
223 switch (property_id
) {
224 case PROP_USE_24_HOUR_FORMAT
:
225 g_value_set_boolean (
227 e_date_time_list_get_use_24_hour_format (
228 E_DATE_TIME_LIST (object
)));
232 g_value_set_pointer (
233 value
, e_date_time_list_get_timezone (
234 E_DATE_TIME_LIST (object
)));
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
;
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
;
261 date_time_list_get_column_type (GtkTreeModel
*tree_model
,
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
];
275 date_time_list_get_iter (GtkTreeModel
*tree_model
,
279 EDateTimeList
*date_time_list
= (EDateTimeList
*) tree_model
;
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
)
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
);
297 iter
->stamp
= date_time_list
->priv
->stamp
;
302 date_time_list_get_path (GtkTreeModel
*tree_model
,
305 EDateTimeList
*date_time_list
= (EDateTimeList
*) tree_model
;
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
);
313 retval
= gtk_tree_path_new ();
314 gtk_tree_path_append_index (retval
, g_list_position (date_time_list
->priv
->list
, l
));
319 date_time_list_get_value (GtkTreeModel
*tree_model
,
324 EDateTimeList
*date_time_list
= E_DATE_TIME_LIST (tree_model
);
325 struct icaltimetype
*itt
;
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
)
346 case E_DATE_TIME_LIST_COLUMN_DESCRIPTION
:
347 str
= get_exception_string (date_time_list
, itt
);
348 g_value_set_string (value
, str
);
354 date_time_list_iter_next (GtkTreeModel
*tree_model
,
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
)
376 date_time_list_iter_children (GtkTreeModel
*tree_model
,
380 EDateTimeList
*date_time_list
= E_DATE_TIME_LIST (tree_model
);
382 /* this is a list, nodes have no children */
386 /* but if parent == NULL we return the list itself as children of the
389 if (!date_time_list
->priv
->list
)
392 iter
->stamp
= E_DATE_TIME_LIST (tree_model
)->priv
->stamp
;
393 iter
->user_data
= date_time_list
->priv
->list
;
398 date_time_list_iter_has_child (GtkTreeModel
*tree_model
,
401 g_return_val_if_fail (IS_VALID_ITER (E_DATE_TIME_LIST (tree_model
), iter
), FALSE
);
406 date_time_list_iter_n_children (GtkTreeModel
*tree_model
,
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);
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);
421 date_time_list_iter_nth_child (GtkTreeModel
*tree_model
,
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
);
433 if (date_time_list
->priv
->list
) {
436 l
= g_list_nth (date_time_list
->priv
->list
, n
);
440 iter
->stamp
= date_time_list
->priv
->stamp
;
449 date_time_list_iter_parent (GtkTreeModel
*tree_model
,
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 (
469 PROP_USE_24_HOUR_FORMAT
,
470 g_param_spec_boolean (
471 "use-24-hour-format",
472 "Use 24-hour Format",
477 g_object_class_install_property (
480 g_param_spec_pointer (
486 column_types
[E_DATE_TIME_LIST_COLUMN_DESCRIPTION
] = G_TYPE_STRING
;
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
;
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
;
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
,
526 g_return_val_if_fail (IS_VALID_ITER (date_time_list
, iter
), NULL
);
528 return G_LIST (iter
->user_data
)->data
;
532 e_date_time_list_set_date_time (EDateTimeList
*date_time_list
,
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
)));
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
;
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
)
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");
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
;
578 e_date_time_list_set_timezone (EDateTimeList
*date_time_list
,
581 g_return_if_fail (E_IS_DATE_TIME_LIST (date_time_list
));
583 if (date_time_list
->priv
->zone
== zone
)
586 date_time_list
->priv
->zone
= zone
;
588 g_object_notify (G_OBJECT (date_time_list
), "timezone");
592 e_date_time_list_append (EDateTimeList
*date_time_list
,
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);
604 iter
->user_data
= g_list_last (date_time_list
->priv
->list
);
605 iter
->stamp
= date_time_list
->priv
->stamp
;
610 e_date_time_list_remove (EDateTimeList
*date_time_list
,
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
);
625 e_date_time_list_clear (EDateTimeList
*date_time_list
)
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
;