No issues found
1 /*
2 * EDateTimeList - list of calendar dates/times with GtkTreeModel interface.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
16 *
17 *
18 * Authors:
19 * Hans Petter Jansson <hpj@ximian.com>
20 *
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 *
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include "e-date-time-list.h"
30
31 #include <string.h>
32 #include <libecal/libecal.h>
33
34 /* XXX Was it really necessary to implement a custom GtkTreeModel for a
35 * one-column list store? There's no mention of why this was done. */
36
37 #define G_LIST(x) ((GList *) x)
38 #define E_DATE_TIME_LIST_IS_SORTED(list) \
39 (E_DATE_TIME_LIST (list)->sort_column_id != -2)
40 #define IS_VALID_ITER(dt_list, iter) \
41 (iter != NULL && iter->user_data != NULL && \
42 dt_list->stamp == iter->stamp)
43
44 enum {
45 PROP_0,
46 PROP_USE_24_HOUR_FORMAT
47 };
48
49 static GType column_types[E_DATE_TIME_LIST_NUM_COLUMNS];
50
51 static void e_date_time_list_tree_model_init (GtkTreeModelIface *iface);
52
53 G_DEFINE_TYPE_WITH_CODE (
54 EDateTimeList, e_date_time_list, G_TYPE_OBJECT,
55 G_IMPLEMENT_INTERFACE (
56 GTK_TYPE_TREE_MODEL, e_date_time_list_tree_model_init))
57
58 static void
59 free_datetime (ECalComponentDateTime *datetime)
60 {
61 g_free (datetime->value);
62 if (datetime->tzid)
63 g_free ((gchar *) datetime->tzid);
64 g_free (datetime);
65 }
66
67 static ECalComponentDateTime *
68 copy_datetime (const ECalComponentDateTime *datetime)
69 {
70 ECalComponentDateTime *datetime_copy;
71
72 datetime_copy = g_new0 (ECalComponentDateTime, 1);
73 datetime_copy->value = g_new (struct icaltimetype, 1);
74 *datetime_copy->value = *datetime->value;
75
76 if (datetime->tzid)
77 datetime_copy->tzid = g_strdup (datetime->tzid);
78
79 return datetime_copy;
80 }
81
82 static gint
83 compare_datetime (const ECalComponentDateTime *datetime1,
84 const ECalComponentDateTime *datetime2)
85 {
86 return icaltime_compare (*datetime1->value, *datetime2->value);
87 }
88
89 static void
90 all_rows_deleted (EDateTimeList *date_time_list)
91 {
92 GtkTreePath *path;
93 gint i;
94
95 if (!date_time_list->list)
96 return;
97
98 path = gtk_tree_path_new ();
99 i = g_list_length (date_time_list->list);
100 gtk_tree_path_append_index (path, i);
101
102 for (; i >= 0; i--) {
103 gtk_tree_model_row_deleted (GTK_TREE_MODEL (date_time_list), path);
104 gtk_tree_path_prev (path);
105 }
106
107 gtk_tree_path_free (path);
108 }
109
110 static void
111 row_deleted (EDateTimeList *date_time_list,
112 gint n)
113 {
114 GtkTreePath *path;
115
116 path = gtk_tree_path_new ();
117 gtk_tree_path_append_index (path, n);
118 gtk_tree_model_row_deleted (GTK_TREE_MODEL (date_time_list), path);
119 gtk_tree_path_free (path);
120 }
121
122 static void
123 row_added (EDateTimeList *date_time_list,
124 gint n)
125 {
126 GtkTreePath *path;
127 GtkTreeIter iter;
128
129 path = gtk_tree_path_new ();
130 gtk_tree_path_append_index (path, n);
131
132 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (date_time_list), &iter, path))
133 gtk_tree_model_row_inserted (GTK_TREE_MODEL (date_time_list), path, &iter);
134
135 gtk_tree_path_free (path);
136 }
137
138 static void
139 row_updated (EDateTimeList *date_time_list,
140 gint n)
141 {
142 GtkTreePath *path;
143 GtkTreeIter iter;
144
145 path = gtk_tree_path_new ();
146 gtk_tree_path_append_index (path, n);
147
148 if (gtk_tree_model_get_iter (GTK_TREE_MODEL (date_time_list), &iter, path))
149 gtk_tree_model_row_changed (GTK_TREE_MODEL (date_time_list), path, &iter);
150
151 gtk_tree_path_free (path);
152 }
153
154 /* Builds a static string out of an exception date */
155 static gchar *
156 get_exception_string (EDateTimeList *date_time_list,
157 ECalComponentDateTime *dt)
158 {
159 static gchar buf[256];
160 struct tm tmp_tm;
161 gboolean use_24_hour_format;
162
163 use_24_hour_format =
164 e_date_time_list_get_use_24_hour_format (date_time_list);
165
166 tmp_tm.tm_year = dt->value->year - 1900;
167 tmp_tm.tm_mon = dt->value->month - 1;
168 tmp_tm.tm_mday = dt->value->day;
169 tmp_tm.tm_hour = dt->value->hour;
170 tmp_tm.tm_min = dt->value->minute;
171 tmp_tm.tm_sec = dt->value->second;
172 tmp_tm.tm_isdst = -1;
173
174 tmp_tm.tm_wday = time_day_of_week (
175 dt->value->day,
176 dt->value->month - 1,
177 dt->value->year);
178
179 e_time_format_date_and_time (
180 &tmp_tm, use_24_hour_format,
181 FALSE, FALSE, buf, sizeof (buf));
182
183 return buf;
184 }
185
186 static void
187 date_time_list_set_property (GObject *object,
188 guint property_id,
189 const GValue *value,
190 GParamSpec *pspec)
191 {
192 switch (property_id) {
193 case PROP_USE_24_HOUR_FORMAT:
194 e_date_time_list_set_use_24_hour_format (
195 E_DATE_TIME_LIST (object),
196 g_value_get_boolean (value));
197 return;
198 }
199
200 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
201 }
202
203 static void
204 date_time_list_get_property (GObject *object,
205 guint property_id,
206 GValue *value,
207 GParamSpec *pspec)
208 {
209 switch (property_id) {
210 case PROP_USE_24_HOUR_FORMAT:
211 g_value_set_boolean (
212 value,
213 e_date_time_list_get_use_24_hour_format (
214 E_DATE_TIME_LIST (object)));
215 return;
216 }
217
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
219 }
220
221 static GtkTreeModelFlags
222 date_time_list_get_flags (GtkTreeModel *tree_model)
223 {
224 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), 0);
225
226 return GTK_TREE_MODEL_LIST_ONLY;
227 }
228
229 static gint
230 date_time_list_get_n_columns (GtkTreeModel *tree_model)
231 {
232 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
233
234 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), 0);
235
236 date_time_list->columns_dirty = TRUE;
237 return E_DATE_TIME_LIST_NUM_COLUMNS;
238 }
239
240 static GType
241 date_time_list_get_column_type (GtkTreeModel *tree_model,
242 gint index)
243 {
244 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
245
246 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), G_TYPE_INVALID);
247 g_return_val_if_fail (index < E_DATE_TIME_LIST_NUM_COLUMNS &&
248 index >= 0, G_TYPE_INVALID);
249
250 date_time_list->columns_dirty = TRUE;
251 return column_types[index];
252 }
253
254 static gboolean
255 date_time_list_get_iter (GtkTreeModel *tree_model,
256 GtkTreeIter *iter,
257 GtkTreePath *path)
258 {
259 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
260 GList *l;
261 gint i;
262
263 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
264 g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
265
266 if (!date_time_list->list)
267 return FALSE;
268
269 date_time_list->columns_dirty = TRUE;
270
271 i = gtk_tree_path_get_indices (path)[0];
272 l = g_list_nth (date_time_list->list, i);
273 if (!l)
274 return FALSE;
275
276 iter->user_data = l;
277 iter->stamp = date_time_list->stamp;
278 return TRUE;
279 }
280
281 static GtkTreePath *
282 date_time_list_get_path (GtkTreeModel *tree_model,
283 GtkTreeIter *iter)
284 {
285 EDateTimeList *date_time_list = (EDateTimeList *) tree_model;
286 GtkTreePath *retval;
287 GList *l;
288
289 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), NULL);
290 g_return_val_if_fail (iter->stamp == E_DATE_TIME_LIST (tree_model)->stamp, NULL);
291
292 l = iter->user_data;
293 retval = gtk_tree_path_new ();
294 gtk_tree_path_append_index (retval, g_list_position (date_time_list->list, l));
295 return retval;
296 }
297
298 static void
299 date_time_list_get_value (GtkTreeModel *tree_model,
300 GtkTreeIter *iter,
301 gint column,
302 GValue *value)
303 {
304 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
305 ECalComponentDateTime *datetime;
306 GList *l;
307 const gchar *str;
308
309 g_return_if_fail (E_IS_DATE_TIME_LIST (tree_model));
310 g_return_if_fail (column < E_DATE_TIME_LIST_NUM_COLUMNS);
311 g_return_if_fail (E_DATE_TIME_LIST (tree_model)->stamp == iter->stamp);
312 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
313
314 g_value_init (value, column_types[column]);
315
316 if (!date_time_list->list)
317 return;
318
319 l = iter->user_data;
320 datetime = l->data;
321
322 if (!datetime)
323 return;
324
325 switch (column) {
326 case E_DATE_TIME_LIST_COLUMN_DESCRIPTION:
327 str = get_exception_string (date_time_list, datetime);
328 g_value_set_string (value, str);
329 break;
330 }
331 }
332
333 static gboolean
334 date_time_list_iter_next (GtkTreeModel *tree_model,
335 GtkTreeIter *iter)
336 {
337 GList *l;
338
339 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
340 g_return_val_if_fail (IS_VALID_ITER (E_DATE_TIME_LIST (tree_model), iter), FALSE);
341
342 if (!E_DATE_TIME_LIST (tree_model)->list)
343 return FALSE;
344
345 l = iter->user_data;
346 l = g_list_next (l);
347 if (l) {
348 iter->user_data = l;
349 return TRUE;
350 }
351
352 return FALSE;
353 }
354
355 static gboolean
356 date_time_list_iter_children (GtkTreeModel *tree_model,
357 GtkTreeIter *iter,
358 GtkTreeIter *parent)
359 {
360 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
361
362 /* this is a list, nodes have no children */
363 if (parent)
364 return FALSE;
365
366 /* but if parent == NULL we return the list itself as children of the
367 * "root" */
368
369 if (!date_time_list->list)
370 return FALSE;
371
372 iter->stamp = E_DATE_TIME_LIST (tree_model)->stamp;
373 iter->user_data = date_time_list->list;
374 return TRUE;
375 }
376
377 static gboolean
378 date_time_list_iter_has_child (GtkTreeModel *tree_model,
379 GtkTreeIter *iter)
380 {
381 g_return_val_if_fail (IS_VALID_ITER (E_DATE_TIME_LIST (tree_model), iter), FALSE);
382 return FALSE;
383 }
384
385 static gint
386 date_time_list_iter_n_children (GtkTreeModel *tree_model,
387 GtkTreeIter *iter)
388 {
389 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
390
391 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), -1);
392
393 if (iter == NULL)
394 return g_list_length (date_time_list->list);
395
396 g_return_val_if_fail (E_DATE_TIME_LIST (tree_model)->stamp == iter->stamp, -1);
397 return 0;
398 }
399
400 static gboolean
401 date_time_list_iter_nth_child (GtkTreeModel *tree_model,
402 GtkTreeIter *iter,
403 GtkTreeIter *parent,
404 gint n)
405 {
406 EDateTimeList *date_time_list = E_DATE_TIME_LIST (tree_model);
407
408 g_return_val_if_fail (E_IS_DATE_TIME_LIST (tree_model), FALSE);
409
410 if (parent)
411 return FALSE;
412
413 if (date_time_list->list) {
414 GList *l;
415
416 l = g_list_nth (date_time_list->list, n);
417 if (!l)
418 return FALSE;
419
420 iter->stamp = date_time_list->stamp;
421 iter->user_data = l;
422 return TRUE;
423 }
424
425 return FALSE;
426 }
427
428 static gboolean
429 date_time_list_iter_parent (GtkTreeModel *tree_model,
430 GtkTreeIter *iter,
431 GtkTreeIter *child)
432 {
433 return FALSE;
434 }
435
436 static void
437 e_date_time_list_class_init (EDateTimeListClass *class)
438 {
439 GObjectClass *object_class;
440
441 object_class = G_OBJECT_CLASS (class);
442 object_class->set_property = date_time_list_set_property;
443 object_class->get_property = date_time_list_get_property;
444
445 g_object_class_install_property (
446 object_class,
447 PROP_USE_24_HOUR_FORMAT,
448 g_param_spec_boolean (
449 "use-24-hour-format",
450 "Use 24-hour Format",
451 NULL,
452 FALSE,
453 G_PARAM_READWRITE));
454
455 column_types[E_DATE_TIME_LIST_COLUMN_DESCRIPTION] = G_TYPE_STRING;
456 }
457
458 static void
459 e_date_time_list_init (EDateTimeList *date_time_list)
460 {
461 date_time_list->stamp = g_random_int ();
462 date_time_list->columns_dirty = FALSE;
463 date_time_list->list = NULL;
464 }
465
466 static void
467 e_date_time_list_tree_model_init (GtkTreeModelIface *iface)
468 {
469 iface->get_flags = date_time_list_get_flags;
470 iface->get_n_columns = date_time_list_get_n_columns;
471 iface->get_column_type = date_time_list_get_column_type;
472 iface->get_iter = date_time_list_get_iter;
473 iface->get_path = date_time_list_get_path;
474 iface->get_value = date_time_list_get_value;
475 iface->iter_next = date_time_list_iter_next;
476 iface->iter_children = date_time_list_iter_children;
477 iface->iter_has_child = date_time_list_iter_has_child;
478 iface->iter_n_children = date_time_list_iter_n_children;
479 iface->iter_nth_child = date_time_list_iter_nth_child;
480 iface->iter_parent = date_time_list_iter_parent;
481 }
482
483 EDateTimeList *
484 e_date_time_list_new (void)
485 {
486 return g_object_new (E_TYPE_DATE_TIME_LIST, NULL);
487 }
488
489 const ECalComponentDateTime *
490 e_date_time_list_get_date_time (EDateTimeList *date_time_list,
491 GtkTreeIter *iter)
492 {
493 g_return_val_if_fail (IS_VALID_ITER (date_time_list, iter), NULL);
494
495 return G_LIST (iter->user_data)->data;
496 }
497
498 void
499 e_date_time_list_set_date_time (EDateTimeList *date_time_list,
500 GtkTreeIter *iter,
501 const ECalComponentDateTime *datetime)
502 {
503 ECalComponentDateTime *datetime_old;
504
505 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
506
507 datetime_old = G_LIST (iter->user_data)->data;
508 free_datetime (datetime_old);
509 G_LIST (iter->user_data)->data = copy_datetime (datetime);
510 row_updated (
511 date_time_list, g_list_position (
512 date_time_list->list, G_LIST (iter->user_data)));
513 }
514
515 gboolean
516 e_date_time_list_get_use_24_hour_format (EDateTimeList *date_time_list)
517 {
518 g_return_val_if_fail (E_IS_DATE_TIME_LIST (date_time_list), FALSE);
519
520 return date_time_list->use_24_hour_format;
521 }
522
523 void
524 e_date_time_list_set_use_24_hour_format (EDateTimeList *date_time_list,
525 gboolean use_24_hour_format)
526 {
527 g_return_if_fail (E_IS_DATE_TIME_LIST (date_time_list));
528
529 if (date_time_list->use_24_hour_format == use_24_hour_format)
530 return;
531
532 date_time_list->use_24_hour_format = use_24_hour_format;
533
534 g_object_notify (G_OBJECT (date_time_list), "use-24-hour-format");
535 }
536
537 void
538 e_date_time_list_append (EDateTimeList *date_time_list,
539 GtkTreeIter *iter,
540 const ECalComponentDateTime *datetime)
541 {
542 g_return_if_fail (datetime != NULL);
543
544 if (g_list_find_custom (
545 date_time_list->list, datetime,
546 (GCompareFunc) compare_datetime) == NULL) {
547 date_time_list->list = g_list_append (
548 date_time_list->list, copy_datetime (datetime));
549 row_added (date_time_list, g_list_length (date_time_list->list) - 1);
550 }
551
552 if (iter) {
553 iter->user_data = g_list_last (date_time_list->list);
554 iter->stamp = date_time_list->stamp;
555 }
556 }
557
558 void
559 e_date_time_list_remove (EDateTimeList *date_time_list,
560 GtkTreeIter *iter)
561 {
562 gint n;
563
564 g_return_if_fail (IS_VALID_ITER (date_time_list, iter));
565
566 n = g_list_position (date_time_list->list, G_LIST (iter->user_data));
567 free_datetime ((ECalComponentDateTime *) G_LIST (iter->user_data)->data);
568 date_time_list->list = g_list_delete_link (
569 date_time_list->list, G_LIST (iter->user_data));
570 row_deleted (date_time_list, n);
571 }
572
573 void
574 e_date_time_list_clear (EDateTimeList *date_time_list)
575 {
576 GList *l;
577
578 all_rows_deleted (date_time_list);
579
580 for (l = date_time_list->list; l; l = g_list_next (l)) {
581 free_datetime ((ECalComponentDateTime *) l->data);
582 }
583
584 g_list_free (date_time_list->list);
585 date_time_list->list = NULL;
586 }