No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-label-list-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-label-list-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-label-list-store.c
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 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include "e-mail-label-list-store.h"
27
28 #include <glib/gi18n.h>
29 #include <camel/camel.h>
30
31 #define E_MAIL_LABEL_LIST_STORE_GET_PRIVATE(obj) \
32 (G_TYPE_INSTANCE_GET_PRIVATE \
33 ((obj), E_TYPE_MAIL_LABEL_LIST_STORE, EMailLabelListStorePrivate))
34
35 #define E_MAIL_LABEL_LIST_STORE_GET_PRIVATE(obj) \
36 (G_TYPE_INSTANCE_GET_PRIVATE \
37 ((obj), E_TYPE_MAIL_LABEL_LIST_STORE, EMailLabelListStorePrivate))
38
39 struct _EMailLabelListStorePrivate {
40 GHashTable *tag_index;
41 GSettings *mail_settings;
42 };
43
44 static struct {
45 const gchar *label_name;
46 const gchar *label_color;
47 const gchar *label_tag;
48 } label_defaults[] = {
49 { N_("I_mportant"), "#EF2929", "$Labelimportant" }, /* red */
50 { N_("_Work"), "#F57900", "$Labelwork" }, /* orange */
51 { N_("_Personal"), "#4E9A06", "$Labelpersonal" }, /* green */
52 { N_("_To Do"), "#3465A4", "$Labeltodo" }, /* blue */
53 { N_("_Later"), "#75507B", "$Labellater" } /* purple */
54 };
55
56 /* Forward Declarations */
57 static void e_mail_label_list_store_interface_init
58 (GtkTreeModelIface *interface);
59 static void labels_settings_changed_cb (GSettings *settings,
60 const gchar *key,
61 gpointer user_data);
62
63 G_DEFINE_TYPE_WITH_CODE (
64 EMailLabelListStore,
65 e_mail_label_list_store,
66 GTK_TYPE_LIST_STORE,
67 G_IMPLEMENT_INTERFACE (
68 GTK_TYPE_TREE_MODEL,
69 e_mail_label_list_store_interface_init))
70
71 static gchar *
72 mail_label_list_store_tag_from_name (const gchar *label_name)
73 {
74 gchar *label_tag;
75 gchar *temp;
76
77 /* Thunderbird compatible */
78 temp = g_ascii_strdown (label_name, -1);
79 g_strdelimit (temp, " ()/{%*<>\\\"", '_');
80 label_tag = camel_utf8_utf7 (temp);
81 g_free (temp);
82
83 return label_tag;
84 }
85
86 static gchar *
87 mail_label_list_store_encode_label (const gchar *label_name,
88 const gchar *label_color,
89 const gchar *label_tag)
90 {
91 GString *string;
92
93 /* Encoded Form: <name> ':' <color> [ '|' <tag> ] */
94
95 string = g_string_new (label_name);
96 g_string_append_printf (string, ":%s", label_color);
97
98 if (label_tag != NULL)
99 g_string_append_printf (string, "|%s", label_tag);
100
101 return g_string_free (string, FALSE);
102 }
103
104 static void
105 mail_label_list_store_ensure_defaults (EMailLabelListStore *store)
106 {
107 gint ii;
108
109 for (ii = 0; ii < G_N_ELEMENTS (label_defaults); ii++) {
110 GtkTreeIter iter;
111 const gchar *label_name;
112 const gchar *label_color;
113 const gchar *label_tag;
114 gchar *encoded;
115
116 label_name = gettext (label_defaults[ii].label_name);
117 label_color = label_defaults[ii].label_color;
118 label_tag = label_defaults[ii].label_tag;
119
120 if (e_mail_label_list_store_lookup (store, label_tag, &iter))
121 continue;
122
123 encoded = mail_label_list_store_encode_label (
124 label_name, label_color, label_tag);
125
126 gtk_list_store_insert_with_values (
127 GTK_LIST_STORE (store),
128 NULL, -1, 0, encoded, -1);
129
130 g_free (encoded);
131 }
132 }
133
134 static gchar *
135 mail_label_list_store_get_stock_id (EMailLabelListStore *store,
136 const gchar *color_spec)
137 {
138 EMailLabelListStoreClass *class;
139 GtkIconFactory *icon_factory;
140 GdkColor color;
141 gchar *stock_id;
142
143 class = E_MAIL_LABEL_LIST_STORE_GET_CLASS (store);
144 icon_factory = class->icon_factory;
145
146 if (!gdk_color_parse (color_spec, &color))
147 return NULL;
148
149 stock_id = g_strdup_printf ("evolution-label-%s", color_spec);
150
151 /* Themes need not be taken into account here.
152 * It's just a solid block of a user-chosen color. */
153 if (gtk_icon_factory_lookup (icon_factory, stock_id) == NULL) {
154 GtkIconSet *icon_set;
155 GdkPixbuf *pixbuf;
156 guint32 pixel;
157
158 pixel = ((color.red & 0xFF00) << 16) +
159 ((color.green & 0xFF00) << 8) +
160 (color.blue & 0xFF00);
161
162 pixbuf = gdk_pixbuf_new (
163 GDK_COLORSPACE_RGB, FALSE, 8, 16, 16);
164 gdk_pixbuf_fill (pixbuf, pixel);
165
166 icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
167 gtk_icon_factory_add (icon_factory, stock_id, icon_set);
168 gtk_icon_set_unref (icon_set);
169
170 g_object_unref (pixbuf);
171 }
172
173 return stock_id;
174 }
175
176 static void
177 mail_label_list_store_dispose (GObject *object)
178 {
179 EMailLabelListStorePrivate *priv;
180
181 priv = E_MAIL_LABEL_LIST_STORE_GET_PRIVATE (object);
182
183 if (priv->mail_settings != NULL) {
184 g_object_unref (priv->mail_settings);
185 priv->mail_settings = NULL;
186 }
187
188 /* Chain up to parent's dispose() method. */
189 G_OBJECT_CLASS (e_mail_label_list_store_parent_class)->
190 dispose (object);
191 }
192
193 static void
194 mail_label_list_store_finalize (GObject *object)
195 {
196 EMailLabelListStorePrivate *priv;
197
198 priv = E_MAIL_LABEL_LIST_STORE_GET_PRIVATE (object);
199
200 g_hash_table_destroy (priv->tag_index);
201
202 /* Chain up to parent's finalize() method. */
203 G_OBJECT_CLASS (e_mail_label_list_store_parent_class)->
204 finalize (object);
205 }
206
207 static void
208 labels_model_changed_cb (EMailLabelListStore *store)
209 {
210 GPtrArray *array;
211 GtkTreeIter tmp_iter;
212 gboolean iter_set;
213
214 g_return_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store));
215
216 /* Make sure we don't enter an infinite synchronizing loop */
217 g_signal_handlers_block_by_func (
218 store->priv->mail_settings,
219 labels_settings_changed_cb, store);
220
221 /* Build list to store in GSettings */
222
223 array = g_ptr_array_new ();
224
225 iter_set = gtk_tree_model_get_iter_first (
226 GTK_TREE_MODEL (store), &tmp_iter);
227
228 while (iter_set) {
229 gchar *string;
230
231 gtk_tree_model_get (
232 GTK_TREE_MODEL (store), &tmp_iter,
233 0, &string, -1);
234 g_ptr_array_add (array, string);
235
236 iter_set = gtk_tree_model_iter_next (
237 GTK_TREE_MODEL (store), &tmp_iter);
238 }
239
240 g_ptr_array_add (array, NULL);
241
242 g_settings_set_strv (
243 store->priv->mail_settings, "labels",
244 (const gchar * const *) array->pdata);
245
246 g_ptr_array_foreach (array, (GFunc) g_free, NULL);
247 g_ptr_array_free (array, TRUE);
248
249 g_signal_handlers_unblock_by_func (
250 store->priv->mail_settings,
251 labels_settings_changed_cb, store);
252 }
253
254 static void
255 labels_settings_changed_cb (GSettings *settings,
256 const gchar *key,
257 gpointer user_data)
258 {
259 EMailLabelListStore *store;
260 gchar **strv;
261 gint i;
262
263 store = E_MAIL_LABEL_LIST_STORE (user_data);
264
265 /* Make sure we don't enter an infinite synchronizing loop */
266 g_signal_handlers_block_by_func (
267 store, labels_model_changed_cb, store);
268
269 gtk_list_store_clear (GTK_LIST_STORE (store));
270
271 strv = g_settings_get_strv (store->priv->mail_settings, "labels");
272
273 for (i = 0; strv[i] != NULL; i++) {
274 GtkTreeIter iter;
275
276 gtk_list_store_insert_with_values (
277 GTK_LIST_STORE (store), &iter, -1, 0, strv[i], -1);
278 }
279
280 g_strfreev (strv);
281
282 g_signal_handlers_unblock_by_func (
283 store, labels_model_changed_cb, store);
284 }
285
286 static void
287 mail_label_list_store_constructed (GObject *object)
288 {
289 EMailLabelListStore *store;
290
291 store = E_MAIL_LABEL_LIST_STORE (object);
292
293 /* Connect to GSettings' change notifications */
294 store->priv->mail_settings = g_settings_new ("org.gnome.evolution.mail");
295 g_signal_connect (
296 store->priv->mail_settings, "changed::labels",
297 G_CALLBACK (labels_settings_changed_cb), store);
298 labels_settings_changed_cb (
299 store->priv->mail_settings, "labels", store);
300
301 /* Connect to ListStore change notifications */
302 g_signal_connect_swapped (
303 store, "row-inserted",
304 G_CALLBACK (labels_model_changed_cb), store);
305 g_signal_connect_swapped (
306 store, "row-changed",
307 G_CALLBACK (labels_model_changed_cb), store);
308 g_signal_connect_swapped (
309 store, "row-deleted",
310 G_CALLBACK (labels_model_changed_cb), store);
311 g_signal_connect_swapped (
312 store, "rows-reordered",
313 G_CALLBACK (labels_model_changed_cb), store);
314
315 mail_label_list_store_ensure_defaults (store);
316
317 /* Chain up to parent's constructed() method. */
318 G_OBJECT_CLASS (e_mail_label_list_store_parent_class)->
319 constructed (object);
320 }
321
322 static void
323 mail_label_list_store_row_inserted (GtkTreeModel *model,
324 GtkTreePath *path,
325 GtkTreeIter *iter)
326 {
327 EMailLabelListStore *store;
328 GtkTreeRowReference *reference;
329 GHashTable *tag_index;
330 gchar *tag;
331
332 store = E_MAIL_LABEL_LIST_STORE (model);
333 tag = e_mail_label_list_store_get_tag (store, iter);
334 g_return_if_fail (tag != NULL);
335
336 /* Hash table takes ownership of both tag and reference. */
337 tag_index = store->priv->tag_index;
338 reference = gtk_tree_row_reference_new (model, path);
339 g_hash_table_insert (tag_index, tag, reference);
340
341 /* We don't need to do anything special for row deletion.
342 * The reference will automatically become invalid (that's
343 * why we're storing references and not iterators or paths),
344 * so garbage collection is not important. We'll do it
345 * lazily. */
346 }
347
348 static void
349 e_mail_label_list_store_class_init (EMailLabelListStoreClass *class)
350 {
351 GObjectClass *object_class;
352
353 g_type_class_add_private (class, sizeof (EMailLabelListStorePrivate));
354
355 object_class = G_OBJECT_CLASS (class);
356 object_class->dispose = mail_label_list_store_dispose;
357 object_class->finalize = mail_label_list_store_finalize;
358 object_class->constructed = mail_label_list_store_constructed;
359
360 class->icon_factory = gtk_icon_factory_new ();
361 gtk_icon_factory_add_default (class->icon_factory);
362 }
363
364 static void
365 e_mail_label_list_store_interface_init (GtkTreeModelIface *interface)
366 {
367 interface->row_inserted = mail_label_list_store_row_inserted;
368 }
369
370 static void
371 e_mail_label_list_store_init (EMailLabelListStore *store)
372 {
373 GHashTable *tag_index;
374 GType type = G_TYPE_STRING;
375
376 tag_index = g_hash_table_new_full (
377 g_str_hash, g_str_equal,
378 (GDestroyNotify) g_free,
379 (GDestroyNotify) gtk_tree_row_reference_free);
380
381 store->priv = E_MAIL_LABEL_LIST_STORE_GET_PRIVATE (store);
382 store->priv->tag_index = tag_index;
383
384 /* XXX While it may seem awkward to cram the label name and color
385 * into a single string column, we do it for the benefit of
386 * letting GSettings keep the model in sync.
387 *
388 * XXX There's a valid argument to be made that this information
389 * doesn't belong in GSettings in the first place. A key file
390 * under $(user_data_dir)/mail would work better. */
391 gtk_list_store_set_column_types (GTK_LIST_STORE (store), 1, &type);
392 }
393
394 EMailLabelListStore *
395 e_mail_label_list_store_new (void)
396 {
397 return g_object_new (E_TYPE_MAIL_LABEL_LIST_STORE, NULL);
398 }
399
400 gchar *
401 e_mail_label_list_store_get_name (EMailLabelListStore *store,
402 GtkTreeIter *iter)
403 {
404 gchar *encoded;
405 gchar *result;
406 gchar **strv;
407
408 /* Encoded Form: <name> ':' <color> [ '|' <tag> ] */
409
410 g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), NULL);
411 g_return_val_if_fail (iter != NULL, NULL);
412
413 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, 0, &encoded, -1);
414
415 strv = g_strsplit_set (encoded, ":|", 3);
416
417 if (g_strv_length (strv) >= 2)
418 result = g_strdup (gettext (strv[0]));
419 else
420 result = NULL;
421
422 g_strfreev (strv);
423 g_free (encoded);
424
425 return result;
426 }
427
428 gboolean
429 e_mail_label_list_store_get_color (EMailLabelListStore *store,
430 GtkTreeIter *iter,
431 GdkColor *color)
432 {
433 gchar *encoded;
434 gchar **strv;
435 gboolean valid;
436
437 /* Encoded Form: <name> ':' <color> [ '|' <tag> ] */
438
439 g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), FALSE);
440 g_return_val_if_fail (iter != NULL, FALSE);
441 g_return_val_if_fail (color != NULL, FALSE);
442
443 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, 0, &encoded, -1);
444
445 strv = g_strsplit_set (encoded, ":|", 3);
446
447 if (g_strv_length (strv) >= 2)
448 valid = gdk_color_parse (strv[1], color);
449 else
450 valid = FALSE;
451
452 g_strfreev (strv);
453 g_free (encoded);
454
455 return valid;
456 }
457
458 gchar *
459 e_mail_label_list_store_get_stock_id (EMailLabelListStore *store,
460 GtkTreeIter *iter)
461 {
462 gchar *encoded;
463 gchar *result;
464 gchar **strv;
465
466 /* Encoded Form: <name> ':' <color> [ '|' <tag> ] */
467
468 g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), NULL);
469 g_return_val_if_fail (iter != NULL, NULL);
470 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, 0, &encoded, -1);
471
472 strv = g_strsplit_set (encoded, ":|", 3);
473
474 if (g_strv_length (strv) >= 2)
475 result = mail_label_list_store_get_stock_id (store, strv[1]);
476 else
477 result = NULL;
478
479 g_strfreev (strv);
480 g_free (encoded);
481
482 return result;
483 }
484
485 gchar *
486 e_mail_label_list_store_get_tag (EMailLabelListStore *store,
487 GtkTreeIter *iter)
488 {
489 gchar *encoded;
490 gchar *result;
491 gchar **strv;
492
493 /* Encoded Form: <name> ':' <color> [ '|' <tag> ] */
494
495 g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), NULL);
496 g_return_val_if_fail (iter != NULL, NULL);
497
498 gtk_tree_model_get (GTK_TREE_MODEL (store), iter, 0, &encoded, -1);
499
500 strv = g_strsplit_set (encoded, ":|", 3);
501
502 /* XXX I guess for historical reasons the default label tags have
503 * a "$Label" prefix, but the default list in GSettings doesn't
504 * include tags. That's why the <tag> part is optional.
505 * So if we're missing the <tag> part, look it up in the
506 * hard-coded default list above.
507 *
508 * Not sure I got my facts straight here. Double check. */
509 if (g_strv_length (strv) >= 3)
510 result = g_strdup (strv[2]);
511 else {
512 gint ii;
513
514 result = NULL;
515
516 for (ii = 0; ii < G_N_ELEMENTS (label_defaults); ii++) {
517 const gchar *label_name;
518 const gchar *label_tag;
519
520 label_name = label_defaults[ii].label_name;
521 label_tag = label_defaults[ii].label_tag;
522
523 if (strcmp (strv[0], label_name) == 0) {
524 result = g_strdup (label_tag);
525 break;
526 }
527 }
528 }
529
530 /* XXX Still no luck? The label list in GSettings must be screwed up.
531 * We must not return NULL because the tag is used as a key in
532 * the index hash table, so generate a tag from the name. */
533 if (result == NULL)
534 result = mail_label_list_store_tag_from_name (strv[0]);
535
536 g_strfreev (strv);
537 g_free (encoded);
538
539 return result;
540 }
541
542 void
543 e_mail_label_list_store_set (EMailLabelListStore *store,
544 GtkTreeIter *iter,
545 const gchar *name,
546 const GdkColor *color)
547 {
548 gchar *encoded;
549 gchar *label_color;
550 gchar *label_tag = NULL;
551
552 g_return_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store));
553 g_return_if_fail (name != NULL);
554 g_return_if_fail (color != NULL);
555
556 label_color = gdk_color_to_string (color);
557
558 if (iter != NULL)
559 label_tag = e_mail_label_list_store_get_tag (store, iter);
560 if (label_tag == NULL)
561 label_tag = mail_label_list_store_tag_from_name (name);
562
563 encoded = mail_label_list_store_encode_label (
564 name, label_color, label_tag);
565
566 /* We use gtk_list_store_insert_with_values() so the data is
567 * in place when the 'row-inserted' signal is emitted and our
568 * row_inserted() method executes. */
569 if (iter != NULL)
570 gtk_list_store_set (
571 GTK_LIST_STORE (store), iter, 0, encoded, -1);
572 else
573 gtk_list_store_insert_with_values (
574 GTK_LIST_STORE (store), NULL, -1, 0, encoded, -1);
575
576 g_free (label_color);
577 g_free (label_tag);
578 g_free (encoded);
579 }
580
581 gboolean
582 e_mail_label_list_store_lookup (EMailLabelListStore *store,
583 const gchar *tag,
584 GtkTreeIter *iter)
585 {
586 GtkTreeRowReference *reference;
587 GHashTable *tag_index;
588 GtkTreeModel *model;
589 GtkTreePath *path;
590
591 g_return_val_if_fail (E_IS_MAIL_LABEL_LIST_STORE (store), FALSE);
592 g_return_val_if_fail (tag != NULL, FALSE);
593 g_return_val_if_fail (iter != NULL, FALSE);
594
595 tag_index = store->priv->tag_index;
596 reference = g_hash_table_lookup (tag_index, tag);
597
598 if (reference == NULL)
599 return FALSE;
600
601 if (!gtk_tree_row_reference_valid (reference)) {
602 /* Garbage collect the dead reference. */
603 g_hash_table_remove (tag_index, tag);
604 return FALSE;
605 }
606
607 model = gtk_tree_row_reference_get_model (reference);
608 path = gtk_tree_row_reference_get_path (reference);
609 gtk_tree_model_get_iter (model, iter, path);
610 gtk_tree_path_free (path);
611
612 return TRUE;
613 }
614
615 gboolean
616 e_mail_label_tag_is_default (const gchar *tag)
617 {
618 g_return_val_if_fail (tag != NULL, FALSE);
619
620 return g_str_has_prefix (tag, "$Label");
621 }