evolution-3.6.4/mail/e-mail-label-list-store.c

No issues found

Incomplete coverage

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
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
Failure running clang-analyzer ('no-output-found')
Message
Unable to locate XML output from invoke-clang-analyzer
  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 }