No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | em-folder-tree-model.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | em-folder-tree-model.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Jeffrey Stedfast <fejj@ximian.com>
18 *
19 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20 *
21 */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "em-folder-tree-model.h"
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <sys/stat.h>
36
37 #include <glib/gi18n.h>
38
39 #include <e-util/e-util.h>
40 #include <shell/e-shell.h>
41
42 #include <libemail-utils/mail-mt.h>
43
44 #include <libemail-engine/e-mail-folder-utils.h>
45 #include <libemail-engine/mail-folder-cache.h>
46 #include <libemail-engine/mail-tools.h>
47 #include <libemail-engine/mail-ops.h>
48
49 #include <e-mail-account-store.h>
50 #include <e-mail-ui-session.h>
51 #include <em-utils.h>
52 #include <em-folder-utils.h>
53 #include <em-event.h>
54
55 #define EM_FOLDER_TREE_MODEL_GET_PRIVATE(obj) \
56 (G_TYPE_INSTANCE_GET_PRIVATE \
57 ((obj), EM_TYPE_FOLDER_TREE_MODEL, EMFolderTreeModelPrivate))
58
59 #define d(x)
60
61 #define EM_FOLDER_TREE_MODEL_GET_PRIVATE(obj) \
62 (G_TYPE_INSTANCE_GET_PRIVATE \
63 ((obj), EM_TYPE_FOLDER_TREE_MODEL, EMFolderTreeModelPrivate))
64
65 struct _EMFolderTreeModelPrivate {
66 /* This is set by EMailShellSidebar. It allows new EMFolderTree
67 * instances to initialize their selection and expanded states to
68 * mimic the sidebar. */
69 GtkTreeSelection *selection; /* weak reference */
70
71 EMailSession *session;
72 EMailAccountStore *account_store;
73
74 /* CamelStore -> EMFolderTreeStoreInfo */
75 GHashTable *store_index;
76 };
77
78 enum {
79 PROP_0,
80 PROP_SELECTION,
81 PROP_SESSION
82 };
83
84 enum {
85 LOADING_ROW,
86 LOADED_ROW,
87 LAST_SIGNAL
88 };
89
90 static guint signals[LAST_SIGNAL];
91
92 G_DEFINE_TYPE (EMFolderTreeModel, em_folder_tree_model, GTK_TYPE_TREE_STORE)
93
94 static void
95 store_info_free (EMFolderTreeModelStoreInfo *si)
96 {
97 g_signal_handler_disconnect (si->store, si->created_id);
98 g_signal_handler_disconnect (si->store, si->deleted_id);
99 g_signal_handler_disconnect (si->store, si->renamed_id);
100
101 if (si->subscribed_id > 0)
102 g_signal_handler_disconnect (si->store, si->subscribed_id);
103 if (si->unsubscribed_id > 0)
104 g_signal_handler_disconnect (si->store, si->unsubscribed_id);
105
106 g_object_unref (si->store);
107 gtk_tree_row_reference_free (si->row);
108 g_hash_table_destroy (si->full_hash);
109 g_free (si);
110 }
111
112 static gint
113 folder_tree_model_sort (GtkTreeModel *model,
114 GtkTreeIter *a,
115 GtkTreeIter *b,
116 gpointer unused)
117 {
118 EMFolderTreeModel *folder_tree_model;
119 gchar *aname, *bname;
120 CamelService *service_a;
121 CamelService *service_b;
122 gboolean a_is_store;
123 gboolean b_is_store;
124 const gchar *store_uid = NULL;
125 guint32 flags_a, flags_b;
126 gint rv = -2;
127
128 folder_tree_model = EM_FOLDER_TREE_MODEL (model);
129
130 gtk_tree_model_get (
131 model, a,
132 COL_BOOL_IS_STORE, &a_is_store,
133 COL_POINTER_CAMEL_STORE, &service_a,
134 COL_STRING_DISPLAY_NAME, &aname,
135 COL_UINT_FLAGS, &flags_a,
136 -1);
137
138 gtk_tree_model_get (
139 model, b,
140 COL_BOOL_IS_STORE, &b_is_store,
141 COL_POINTER_CAMEL_STORE, &service_b,
142 COL_STRING_DISPLAY_NAME, &bname,
143 COL_UINT_FLAGS, &flags_b,
144 -1);
145
146 if (CAMEL_IS_SERVICE (service_a))
147 store_uid = camel_service_get_uid (service_a);
148
149 if (a_is_store && b_is_store) {
150 rv = e_mail_account_store_compare_services (
151 folder_tree_model->priv->account_store,
152 service_a, service_b);
153
154 } else if (g_strcmp0 (store_uid, E_MAIL_SESSION_VFOLDER_UID) == 0) {
155 /* UNMATCHED is always last. */
156 if (g_strcmp0 (aname, _("UNMATCHED")) == 0)
157 rv = 1;
158 else if (g_strcmp0 (bname, _("UNMATCHED")) == 0)
159 rv = -1;
160
161 } else {
162 /* Inbox is always first. */
163 if ((flags_a & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX)
164 rv = -1;
165 else if ((flags_b & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX)
166 rv = 1;
167 }
168
169 if (rv == -2) {
170 if (aname != NULL && bname != NULL)
171 rv = g_utf8_collate (aname, bname);
172 else if (aname == bname)
173 rv = 0;
174 else if (aname == NULL)
175 rv = -1;
176 else
177 rv = 1;
178 }
179
180 g_free (aname);
181 g_free (bname);
182
183 return rv;
184 }
185
186 static void
187 folder_tree_model_service_removed (EMailAccountStore *account_store,
188 CamelService *service,
189 EMFolderTreeModel *folder_tree_model)
190 {
191 em_folder_tree_model_remove_store (
192 folder_tree_model, CAMEL_STORE (service));
193 }
194
195 static void
196 folder_tree_model_service_enabled (EMailAccountStore *account_store,
197 CamelService *service,
198 EMFolderTreeModel *folder_tree_model)
199 {
200 em_folder_tree_model_add_store (
201 folder_tree_model, CAMEL_STORE (service));
202 }
203
204 static void
205 folder_tree_model_service_disabled (EMailAccountStore *account_store,
206 CamelService *service,
207 EMFolderTreeModel *folder_tree_model)
208 {
209 em_folder_tree_model_remove_store (
210 folder_tree_model, CAMEL_STORE (service));
211 }
212
213 static void
214 folder_tree_model_services_reordered (EMailAccountStore *account_store,
215 gboolean default_restored,
216 EMFolderTreeModel *folder_tree_model)
217 {
218 /* This forces the tree store to re-sort. */
219 gtk_tree_sortable_set_default_sort_func (
220 GTK_TREE_SORTABLE (folder_tree_model),
221 folder_tree_model_sort, NULL, NULL);
222 }
223
224 static void
225 folder_tree_model_selection_finalized_cb (EMFolderTreeModel *model)
226 {
227 model->priv->selection = NULL;
228
229 g_object_notify (G_OBJECT (model), "selection");
230 }
231
232 static void
233 folder_tree_model_set_property (GObject *object,
234 guint property_id,
235 const GValue *value,
236 GParamSpec *pspec)
237 {
238 switch (property_id) {
239 case PROP_SELECTION:
240 em_folder_tree_model_set_selection (
241 EM_FOLDER_TREE_MODEL (object),
242 g_value_get_object (value));
243 return;
244
245 case PROP_SESSION:
246 em_folder_tree_model_set_session (
247 EM_FOLDER_TREE_MODEL (object),
248 g_value_get_object (value));
249 return;
250 }
251
252 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
253 }
254
255 static void
256 folder_tree_model_get_property (GObject *object,
257 guint property_id,
258 GValue *value,
259 GParamSpec *pspec)
260 {
261 switch (property_id) {
262 case PROP_SELECTION:
263 g_value_set_object (
264 value,
265 em_folder_tree_model_get_selection (
266 EM_FOLDER_TREE_MODEL (object)));
267 return;
268
269 case PROP_SESSION:
270 g_value_set_object (
271 value,
272 em_folder_tree_model_get_session (
273 EM_FOLDER_TREE_MODEL (object)));
274 return;
275 }
276
277 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
278 }
279
280 static void
281 folder_tree_model_dispose (GObject *object)
282 {
283 EMFolderTreeModelPrivate *priv;
284
285 priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (object);
286
287 if (priv->selection != NULL) {
288 g_object_weak_unref (
289 G_OBJECT (priv->selection), (GWeakNotify)
290 folder_tree_model_selection_finalized_cb, object);
291 priv->selection = NULL;
292 }
293
294 if (priv->session != NULL) {
295 g_object_unref (priv->session);
296 priv->session = NULL;
297 }
298
299 if (priv->account_store != NULL) {
300 g_signal_handlers_disconnect_matched (
301 priv->account_store, G_SIGNAL_MATCH_DATA,
302 0, 0, NULL, NULL, object);
303 g_object_unref (priv->account_store);
304 priv->account_store = NULL;
305 }
306
307 /* Chain up to parent's dispose() method. */
308 G_OBJECT_CLASS (em_folder_tree_model_parent_class)->dispose (object);
309 }
310
311 static void
312 folder_tree_model_finalize (GObject *object)
313 {
314 EMFolderTreeModelPrivate *priv;
315
316 priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (object);
317
318 g_hash_table_destroy (priv->store_index);
319
320 /* Chain up to parent's finalize() method. */
321 G_OBJECT_CLASS (em_folder_tree_model_parent_class)->finalize (object);
322 }
323
324 static void
325 folder_tree_model_constructed (GObject *object)
326 {
327 GType col_types[] = {
328 G_TYPE_STRING, /* display name */
329 G_TYPE_POINTER, /* store object */
330 G_TYPE_STRING, /* full name */
331 G_TYPE_STRING, /* icon name */
332 G_TYPE_UINT, /* unread count */
333 G_TYPE_UINT, /* flags */
334 G_TYPE_BOOLEAN, /* is a store node */
335 G_TYPE_BOOLEAN, /* is a folder node */
336 G_TYPE_BOOLEAN, /* has not-yet-loaded subfolders */
337 G_TYPE_UINT, /* last known unread count */
338 G_TYPE_BOOLEAN, /* folder is a draft folder */
339 G_TYPE_UINT /* user's sortorder */
340 };
341
342 gtk_tree_store_set_column_types (
343 GTK_TREE_STORE (object), NUM_COLUMNS, col_types);
344 gtk_tree_sortable_set_default_sort_func (
345 GTK_TREE_SORTABLE (object),
346 folder_tree_model_sort, NULL, NULL);
347 gtk_tree_sortable_set_sort_column_id (
348 GTK_TREE_SORTABLE (object),
349 GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
350 GTK_SORT_ASCENDING);
351
352 /* Chain up to parent's constructed() method. */
353 G_OBJECT_CLASS (em_folder_tree_model_parent_class)->
354 constructed (object);
355 }
356
357 static void
358 em_folder_tree_model_class_init (EMFolderTreeModelClass *class)
359 {
360 GObjectClass *object_class;
361
362 g_type_class_add_private (class, sizeof (EMFolderTreeModelPrivate));
363
364 object_class = G_OBJECT_CLASS (class);
365 object_class->set_property = folder_tree_model_set_property;
366 object_class->get_property = folder_tree_model_get_property;
367 object_class->dispose = folder_tree_model_dispose;
368 object_class->finalize = folder_tree_model_finalize;
369 object_class->constructed = folder_tree_model_constructed;
370
371 g_object_class_install_property (
372 object_class,
373 PROP_SESSION,
374 g_param_spec_object (
375 "session",
376 NULL,
377 NULL,
378 E_TYPE_MAIL_SESSION,
379 G_PARAM_READWRITE));
380
381 g_object_class_install_property (
382 object_class,
383 PROP_SELECTION,
384 g_param_spec_object (
385 "selection",
386 "Selection",
387 NULL,
388 GTK_TYPE_TREE_SELECTION,
389 G_PARAM_READWRITE));
390
391 signals[LOADING_ROW] = g_signal_new (
392 "loading-row",
393 G_OBJECT_CLASS_TYPE (object_class),
394 G_SIGNAL_RUN_FIRST,
395 G_STRUCT_OFFSET (EMFolderTreeModelClass, loading_row),
396 NULL, NULL,
397 e_marshal_VOID__POINTER_POINTER,
398 G_TYPE_NONE, 2,
399 G_TYPE_POINTER,
400 G_TYPE_POINTER);
401
402 signals[LOADED_ROW] = g_signal_new (
403 "loaded-row",
404 G_OBJECT_CLASS_TYPE (object_class),
405 G_SIGNAL_RUN_FIRST,
406 G_STRUCT_OFFSET (EMFolderTreeModelClass, loaded_row),
407 NULL, NULL,
408 e_marshal_VOID__POINTER_POINTER,
409 G_TYPE_NONE, 2,
410 G_TYPE_POINTER,
411 G_TYPE_POINTER);
412 }
413
414 static void
415 folder_tree_model_set_unread_count (EMFolderTreeModel *model,
416 CamelStore *store,
417 const gchar *full,
418 gint unread)
419 {
420 EMFolderTreeModelStoreInfo *si;
421 GtkTreeRowReference *reference;
422 GtkTreeModel *tree_model;
423 GtkTreePath *path;
424 GtkTreeIter parent;
425 GtkTreeIter iter;
426 guint old_unread = 0;
427
428 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
429 g_return_if_fail (CAMEL_IS_STORE (store));
430 g_return_if_fail (full != NULL);
431
432 if (unread < 0)
433 return;
434
435 si = em_folder_tree_model_lookup_store_info (model, store);
436 if (si == NULL)
437 return;
438
439 reference = g_hash_table_lookup (si->full_hash, full);
440 if (!gtk_tree_row_reference_valid (reference))
441 return;
442
443 tree_model = GTK_TREE_MODEL (model);
444
445 path = gtk_tree_row_reference_get_path (reference);
446 gtk_tree_model_get_iter (tree_model, &iter, path);
447 gtk_tree_path_free (path);
448
449 gtk_tree_model_get (
450 tree_model, &iter,
451 COL_UINT_UNREAD_LAST_SEL, &old_unread, -1);
452
453 gtk_tree_store_set (
454 GTK_TREE_STORE (model), &iter,
455 COL_UINT_UNREAD, unread,
456 COL_UINT_UNREAD_LAST_SEL, MIN (old_unread, unread), -1);
457
458 /* Folders are displayed with a bold weight to indicate that
459 * they contain unread messages. We signal that parent rows
460 * have changed here to update them. */
461 while (gtk_tree_model_iter_parent (tree_model, &parent, &iter)) {
462 path = gtk_tree_model_get_path (tree_model, &parent);
463 gtk_tree_model_row_changed (tree_model, path, &parent);
464 gtk_tree_path_free (path);
465 iter = parent;
466 }
467 }
468
469 static void
470 em_folder_tree_model_init (EMFolderTreeModel *model)
471 {
472 GHashTable *store_index;
473
474 store_index = g_hash_table_new_full (
475 g_direct_hash, g_direct_equal,
476 (GDestroyNotify) NULL,
477 (GDestroyNotify) store_info_free);
478
479 model->priv = EM_FOLDER_TREE_MODEL_GET_PRIVATE (model);
480 model->priv->store_index = store_index;
481 }
482
483 EMFolderTreeModel *
484 em_folder_tree_model_new (void)
485 {
486 return g_object_new (EM_TYPE_FOLDER_TREE_MODEL, NULL);
487 }
488
489 EMFolderTreeModel *
490 em_folder_tree_model_get_default (void)
491 {
492 static EMFolderTreeModel *default_folder_tree_model;
493
494 if (G_UNLIKELY (default_folder_tree_model == NULL))
495 default_folder_tree_model = em_folder_tree_model_new ();
496
497 return default_folder_tree_model;
498 }
499
500 GtkTreeSelection *
501 em_folder_tree_model_get_selection (EMFolderTreeModel *model)
502 {
503 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
504
505 return GTK_TREE_SELECTION (model->priv->selection);
506 }
507
508 void
509 em_folder_tree_model_set_selection (EMFolderTreeModel *model,
510 GtkTreeSelection *selection)
511 {
512 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
513
514 if (selection != NULL)
515 g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
516
517 if (model->priv->selection == selection)
518 return;
519
520 if (model->priv->selection != NULL) {
521 g_object_weak_unref (
522 G_OBJECT (model->priv->selection), (GWeakNotify)
523 folder_tree_model_selection_finalized_cb, model);
524 model->priv->selection = NULL;
525 }
526
527 model->priv->selection = selection;
528
529 if (model->priv->selection != NULL)
530 g_object_weak_ref (
531 G_OBJECT (model->priv->selection), (GWeakNotify)
532 folder_tree_model_selection_finalized_cb, model);
533
534 g_object_notify (G_OBJECT (model), "selection");
535 }
536
537 EMailSession *
538 em_folder_tree_model_get_session (EMFolderTreeModel *model)
539 {
540 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
541
542 return model->priv->session;
543 }
544
545 void
546 em_folder_tree_model_set_session (EMFolderTreeModel *model,
547 EMailSession *session)
548 {
549 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
550
551 if (model->priv->session == session)
552 return;
553
554 if (session != NULL) {
555 g_return_if_fail (E_IS_MAIL_SESSION (session));
556 g_object_ref (session);
557 }
558
559 if (model->priv->session != NULL)
560 g_object_unref (model->priv->session);
561
562 model->priv->session = session;
563
564 /* FIXME Technically we should be disconnecting this signal
565 * when replacing an old session with a new session,
566 * but at present this function is only called once. */
567 if (session != NULL) {
568 EMailAccountStore *account_store;
569 MailFolderCache *folder_cache;
570
571 folder_cache = e_mail_session_get_folder_cache (session);
572 account_store = e_mail_ui_session_get_account_store (
573 E_MAIL_UI_SESSION (session));
574
575 /* Keep our own reference since we connect to its signals. */
576 g_warn_if_fail (model->priv->account_store == NULL);
577 model->priv->account_store = g_object_ref (account_store);
578
579 /* No need to connect to "service-added" emissions since it's
580 * always immediately followed by either "service-enabled" or
581 * "service-disabled". */
582
583 g_signal_connect (
584 account_store, "service-removed",
585 G_CALLBACK (folder_tree_model_service_removed),
586 model);
587
588 g_signal_connect (
589 account_store, "service-enabled",
590 G_CALLBACK (folder_tree_model_service_enabled),
591 model);
592
593 g_signal_connect (
594 account_store, "service-disabled",
595 G_CALLBACK (folder_tree_model_service_disabled),
596 model);
597
598 g_signal_connect (
599 account_store, "services-reordered",
600 G_CALLBACK (folder_tree_model_services_reordered),
601 model);
602
603 g_signal_connect_swapped (
604 folder_cache, "folder-unread-updated",
605 G_CALLBACK (folder_tree_model_set_unread_count),
606 model);
607 }
608
609 g_object_notify (G_OBJECT (model), "session");
610 }
611
612 /* Helper for em_folder_tree_model_set_folder_info() */
613 static void
614 folder_tree_model_get_drafts_folder_uri (ESourceRegistry *registry,
615 CamelStore *store,
616 gchar **drafts_folder_uri)
617 {
618 ESource *source;
619 const gchar *extension_name;
620
621 /* In case we fail... */
622 *drafts_folder_uri = NULL;
623
624 source = em_utils_ref_mail_identity_for_store (registry, store);
625 if (source == NULL)
626 return;
627
628 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
629 if (e_source_has_extension (source, extension_name)) {
630 ESourceMailComposition *extension;
631
632 extension = e_source_get_extension (source, extension_name);
633
634 *drafts_folder_uri =
635 e_source_mail_composition_dup_drafts_folder (extension);
636 }
637
638 g_object_unref (source);
639 }
640
641 /* Helper for em_folder_tree_model_set_folder_info() */
642 static void
643 folder_tree_model_get_sent_folder_uri (ESourceRegistry *registry,
644 CamelStore *store,
645 gchar **sent_folder_uri)
646 {
647 ESource *source;
648 const gchar *extension_name;
649
650 /* In case we fail... */
651 *sent_folder_uri = NULL;
652
653 source = em_utils_ref_mail_identity_for_store (registry, store);
654 if (source == NULL)
655 return;
656
657 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
658 if (e_source_has_extension (source, extension_name)) {
659 ESourceMailSubmission *extension;
660
661 extension = e_source_get_extension (source, extension_name);
662
663 *sent_folder_uri =
664 e_source_mail_submission_dup_sent_folder (extension);
665 }
666
667 g_object_unref (source);
668 }
669
670 void
671 em_folder_tree_model_set_folder_info (EMFolderTreeModel *model,
672 GtkTreeIter *iter,
673 EMFolderTreeModelStoreInfo *si,
674 CamelFolderInfo *fi,
675 gint fully_loaded)
676 {
677 GtkTreeRowReference *path_row;
678 GtkTreeStore *tree_store;
679 MailFolderCache *folder_cache;
680 ESourceRegistry *registry;
681 EMailSession *session;
682 guint unread;
683 GtkTreePath *path;
684 GtkTreeIter sub;
685 CamelFolder *folder;
686 gboolean emitted = FALSE;
687 const gchar *uid;
688 const gchar *icon_name;
689 const gchar *display_name;
690 guint32 flags, add_flags = 0;
691 EMEventTargetCustomIcon *target;
692 gboolean load = FALSE;
693 gboolean folder_is_drafts = FALSE;
694 gboolean folder_is_outbox = FALSE;
695 gboolean folder_is_templates = FALSE;
696 gboolean store_is_local;
697 gchar *uri;
698
699 /* Make sure we don't already know about it. */
700 if (g_hash_table_lookup (si->full_hash, fi->full_name))
701 return;
702
703 tree_store = GTK_TREE_STORE (model);
704
705 session = em_folder_tree_model_get_session (model);
706 folder_cache = e_mail_session_get_folder_cache (session);
707 registry = e_mail_session_get_registry (session);
708
709 uid = camel_service_get_uid (CAMEL_SERVICE (si->store));
710 store_is_local = (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0);
711
712 if (!fully_loaded)
713 load = (fi->child == NULL) && !(fi->flags &
714 (CAMEL_FOLDER_NOCHILDREN | CAMEL_FOLDER_NOINFERIORS));
715
716 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
717 path_row = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
718 gtk_tree_path_free (path);
719
720 uri = e_mail_folder_uri_build (si->store, fi->full_name);
721
722 g_hash_table_insert (
723 si->full_hash, g_strdup (fi->full_name), path_row);
724
725 /* XXX If we have the folder, and its the Outbox folder, we need
726 * the total count, not unread. We do the same for Drafts. */
727
728 /* XXX This is duplicated in mail-folder-cache too, should perhaps
729 * be functionised. */
730 unread = fi->unread;
731 if (mail_folder_cache_get_folder_from_uri (
732 folder_cache, uri, &folder) && folder) {
733 folder_is_drafts = em_utils_folder_is_drafts (registry, folder);
734 folder_is_outbox = em_utils_folder_is_outbox (registry, folder);
735
736 if (folder_is_drafts || folder_is_outbox) {
737 gint total;
738
739 if ((total = camel_folder_get_message_count (folder)) > 0) {
740 gint deleted = camel_folder_get_deleted_message_count (folder);
741
742 if (deleted != -1)
743 total -= deleted;
744 }
745
746 unread = total > 0 ? total : 0;
747 }
748
749 g_object_unref (folder);
750 }
751
752 flags = fi->flags;
753 display_name = fi->display_name;
754
755 if (store_is_local) {
756 if (strcmp (fi->full_name, "Drafts") == 0) {
757 folder_is_drafts = TRUE;
758 display_name = _("Drafts");
759 } else if (strcmp (fi->full_name, "Templates") == 0) {
760 folder_is_templates = TRUE;
761 display_name = _("Templates");
762 } else if (strcmp (fi->full_name, "Inbox") == 0) {
763 flags = (flags & ~CAMEL_FOLDER_TYPE_MASK) |
764 CAMEL_FOLDER_TYPE_INBOX;
765 display_name = _("Inbox");
766 } else if (strcmp (fi->full_name, "Outbox") == 0) {
767 flags = (flags & ~CAMEL_FOLDER_TYPE_MASK) |
768 CAMEL_FOLDER_TYPE_OUTBOX;
769 display_name = _("Outbox");
770 } else if (strcmp (fi->full_name, "Sent") == 0) {
771 flags = (flags & ~CAMEL_FOLDER_TYPE_MASK) |
772 CAMEL_FOLDER_TYPE_SENT;
773 display_name = _("Sent");
774 }
775 }
776
777 if ((flags & CAMEL_FOLDER_TYPE_MASK) == 0) {
778 gchar *drafts_folder_uri;
779 gchar *sent_folder_uri;
780
781 folder_tree_model_get_drafts_folder_uri (
782 registry, si->store, &drafts_folder_uri);
783
784 folder_tree_model_get_sent_folder_uri (
785 registry, si->store, &sent_folder_uri);
786
787 if (!folder_is_drafts && drafts_folder_uri != NULL) {
788 folder_is_drafts = e_mail_folder_uri_equal (
789 CAMEL_SESSION (session),
790 uri, drafts_folder_uri);
791 }
792
793 if (sent_folder_uri != NULL) {
794 if (e_mail_folder_uri_equal (
795 CAMEL_SESSION (session),
796 uri, sent_folder_uri)) {
797 add_flags = CAMEL_FOLDER_TYPE_SENT;
798 }
799 }
800
801 g_free (drafts_folder_uri);
802 g_free (sent_folder_uri);
803 }
804
805 /* Choose an icon name for the folder. */
806 icon_name = em_folder_utils_get_icon_name (flags | add_flags);
807
808 if (g_str_equal (icon_name, "folder")) {
809 if (folder_is_drafts)
810 icon_name = "accessories-text-editor";
811 else if (folder_is_templates)
812 icon_name = "text-x-generic-template";
813 }
814
815 gtk_tree_store_set (
816 tree_store, iter,
817 COL_STRING_DISPLAY_NAME, display_name,
818 COL_POINTER_CAMEL_STORE, si->store,
819 COL_STRING_FULL_NAME, fi->full_name,
820 COL_STRING_ICON_NAME, icon_name,
821 COL_UINT_FLAGS, flags,
822 COL_BOOL_IS_STORE, FALSE,
823 COL_BOOL_IS_FOLDER, TRUE,
824 COL_BOOL_LOAD_SUBDIRS, load,
825 COL_UINT_UNREAD_LAST_SEL, 0,
826 COL_BOOL_IS_DRAFT, folder_is_drafts,
827 -1);
828
829 g_free (uri);
830 uri = NULL;
831
832 target = em_event_target_new_custom_icon (
833 em_event_peek (), tree_store, iter,
834 fi->full_name, EM_EVENT_CUSTOM_ICON);
835 e_event_emit (
836 (EEvent *) em_event_peek (), "folder.customicon",
837 (EEventTarget *) target);
838
839 if (unread != ~0)
840 gtk_tree_store_set (
841 tree_store, iter, COL_UINT_UNREAD, unread,
842 COL_UINT_UNREAD_LAST_SEL, unread, -1);
843
844 if (load) {
845 /* create a placeholder node for our subfolders... */
846 gtk_tree_store_append (tree_store, &sub, iter);
847 gtk_tree_store_set (
848 tree_store, &sub,
849 COL_STRING_DISPLAY_NAME, _("Loading..."),
850 COL_POINTER_CAMEL_STORE, si->store,
851 COL_STRING_FULL_NAME, NULL,
852 COL_STRING_ICON_NAME, NULL,
853 COL_BOOL_LOAD_SUBDIRS, FALSE,
854 COL_BOOL_IS_STORE, FALSE,
855 COL_BOOL_IS_FOLDER, FALSE,
856 COL_UINT_UNREAD, 0,
857 COL_UINT_UNREAD_LAST_SEL, 0,
858 COL_BOOL_IS_DRAFT, FALSE,
859 -1);
860
861 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
862 g_signal_emit (model, signals[LOADING_ROW], 0, path, iter);
863 gtk_tree_path_free (path);
864 return;
865 }
866
867 if (fi->child) {
868 fi = fi->child;
869
870 do {
871 gtk_tree_store_append (tree_store, &sub, iter);
872
873 if (!emitted) {
874 path = gtk_tree_model_get_path (
875 GTK_TREE_MODEL (model), iter);
876 g_signal_emit (
877 model, signals[LOADED_ROW],
878 0, path, iter);
879 gtk_tree_path_free (path);
880 emitted = TRUE;
881 }
882
883 em_folder_tree_model_set_folder_info (
884 model, &sub, si, fi, fully_loaded);
885 fi = fi->next;
886 } while (fi);
887 }
888
889 if (!emitted) {
890 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), iter);
891 g_signal_emit (model, signals[LOADED_ROW], 0, path, iter);
892 gtk_tree_path_free (path);
893 }
894 }
895
896 static void
897 folder_subscribed_cb (CamelStore *store,
898 CamelFolderInfo *fi,
899 EMFolderTreeModel *model)
900 {
901 EMFolderTreeModelStoreInfo *si;
902 GtkTreeRowReference *reference;
903 GtkTreeIter parent, iter;
904 GtkTreePath *path;
905 gboolean load;
906 gchar *dirname, *p;
907
908 si = em_folder_tree_model_lookup_store_info (model, store);
909 if (si == NULL)
910 return;
911
912 /* Make sure we don't already know about it? */
913 if (g_hash_table_lookup (si->full_hash, fi->full_name))
914 return;
915
916 /* Get our parent folder's path. */
917 dirname = g_alloca (strlen (fi->full_name) + 1);
918 strcpy (dirname, fi->full_name);
919 p = strrchr (dirname, '/');
920 if (p == NULL) {
921 /* User subscribed to a toplevel folder. */
922 reference = si->row;
923 } else {
924 *p = 0;
925 reference = g_hash_table_lookup (si->full_hash, dirname);
926 }
927
928 if (!gtk_tree_row_reference_valid (reference))
929 return;
930
931 path = gtk_tree_row_reference_get_path (reference);
932 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &parent, path);
933 gtk_tree_path_free (path);
934
935 /* Make sure parent's subfolders have already been loaded. */
936 gtk_tree_model_get (
937 GTK_TREE_MODEL (model), &parent,
938 COL_BOOL_LOAD_SUBDIRS, &load, -1);
939 if (load)
940 return;
941
942 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &parent);
943
944 em_folder_tree_model_set_folder_info (model, &iter, si, fi, TRUE);
945 }
946
947 static void
948 folder_unsubscribed_cb (CamelStore *store,
949 CamelFolderInfo *fi,
950 EMFolderTreeModel *model)
951 {
952 EMFolderTreeModelStoreInfo *si;
953 GtkTreeRowReference *reference;
954 GtkTreePath *path;
955 GtkTreeIter iter;
956
957 si = em_folder_tree_model_lookup_store_info (model, store);
958 if (si == NULL)
959 return;
960
961 reference = g_hash_table_lookup (si->full_hash, fi->full_name);
962 if (!gtk_tree_row_reference_valid (reference))
963 return;
964
965 path = gtk_tree_row_reference_get_path (reference);
966 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
967 gtk_tree_path_free (path);
968
969 em_folder_tree_model_remove_folders (model, si, &iter);
970 }
971
972 static void
973 folder_created_cb (CamelStore *store,
974 CamelFolderInfo *fi,
975 EMFolderTreeModel *model)
976 {
977 EMFolderTreeModelStoreInfo *si;
978
979 /* We only want created events to do more
980 * work if we don't support subscriptions. */
981 if (CAMEL_IS_SUBSCRIBABLE (store))
982 return;
983
984 /* process "folder-created" event only when store already loaded */
985 si = em_folder_tree_model_lookup_store_info (model, store);
986 if (si == NULL || g_hash_table_size (si->full_hash) == 0)
987 return;
988
989 folder_subscribed_cb (store, fi, model);
990 }
991
992 static void
993 folder_deleted_cb (CamelStore *store,
994 CamelFolderInfo *fi,
995 EMFolderTreeModel *model)
996 {
997 /* We only want deleted events to do more
998 * work if we don't support subscriptions. */
999 if (CAMEL_IS_SUBSCRIBABLE (store))
1000 return;
1001
1002 folder_unsubscribed_cb (store, fi, model);
1003 }
1004
1005 typedef struct {
1006 gchar *old_base;
1007 CamelFolderInfo *new;
1008 } RenameInfo;
1009
1010 static void
1011 folder_renamed_cb (CamelStore *store,
1012 const gchar *old_name,
1013 CamelFolderInfo *info,
1014 EMFolderTreeModel *model)
1015 {
1016 EMFolderTreeModelStoreInfo *si;
1017 GtkTreeRowReference *reference;
1018 GtkTreeIter root, iter;
1019 GtkTreePath *path;
1020 gchar *parent, *p;
1021
1022 si = em_folder_tree_model_lookup_store_info (model, store);
1023 if (si == NULL)
1024 return;
1025
1026 reference = g_hash_table_lookup (si->full_hash, old_name);
1027 if (!gtk_tree_row_reference_valid (reference))
1028 return;
1029
1030 path = gtk_tree_row_reference_get_path (reference);
1031 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1032 gtk_tree_path_free (path);
1033
1034 em_folder_tree_model_remove_folders (model, si, &iter);
1035
1036 parent = g_strdup (info->full_name);
1037 p = strrchr (parent, '/');
1038 if (p)
1039 *p = 0;
1040 if (p == NULL || parent == p)
1041 /* renamed to a toplevel folder on the store */
1042 reference = si->row;
1043 else
1044 reference = g_hash_table_lookup (si->full_hash, parent);
1045
1046 g_free (parent);
1047
1048 if (!gtk_tree_row_reference_valid (reference))
1049 return;
1050
1051 path = gtk_tree_row_reference_get_path (reference);
1052 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &root, path);
1053 gtk_tree_path_free (path);
1054
1055 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, &root);
1056 em_folder_tree_model_set_folder_info (model, &iter, si, info, TRUE);
1057 }
1058
1059 void
1060 em_folder_tree_model_add_store (EMFolderTreeModel *model,
1061 CamelStore *store)
1062 {
1063 EMFolderTreeModelStoreInfo *si;
1064 GtkTreeRowReference *reference;
1065 GtkTreeStore *tree_store;
1066 GtkTreeIter root, iter;
1067 GtkTreePath *path;
1068 CamelService *service;
1069 CamelProvider *provider;
1070 CamelURL *service_url;
1071 const gchar *display_name;
1072 gchar *uri;
1073
1074 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
1075 g_return_if_fail (CAMEL_IS_STORE (store));
1076
1077 tree_store = GTK_TREE_STORE (model);
1078
1079 service = CAMEL_SERVICE (store);
1080 provider = camel_service_get_provider (service);
1081 display_name = camel_service_get_display_name (service);
1082
1083 /* Ignore stores that should not be added to the tree model. */
1084
1085 if (provider == NULL)
1086 return;
1087
1088 if ((provider->flags & CAMEL_PROVIDER_IS_STORAGE) == 0)
1089 return;
1090
1091 service_url = camel_service_new_camel_url (service);
1092 if (em_utils_is_local_delivery_mbox_file (service_url)) {
1093 camel_url_free (service_url);
1094 return;
1095 }
1096 uri = camel_url_to_string (service_url, CAMEL_URL_HIDE_ALL);
1097 camel_url_free (service_url);
1098
1099 si = em_folder_tree_model_lookup_store_info (model, store);
1100 if (si != NULL)
1101 em_folder_tree_model_remove_store (model, store);
1102
1103 /* Add the store to the tree. */
1104 gtk_tree_store_append (tree_store, &iter, NULL);
1105 gtk_tree_store_set (
1106 tree_store, &iter,
1107 COL_STRING_DISPLAY_NAME, display_name,
1108 COL_POINTER_CAMEL_STORE, store,
1109 COL_STRING_FULL_NAME, NULL,
1110 COL_BOOL_LOAD_SUBDIRS, TRUE,
1111 COL_BOOL_IS_STORE, TRUE,
1112 -1);
1113
1114 g_free (uri);
1115
1116 path = gtk_tree_model_get_path (GTK_TREE_MODEL (model), &iter);
1117 reference = gtk_tree_row_reference_new (GTK_TREE_MODEL (model), path);
1118
1119 si = g_new0 (EMFolderTreeModelStoreInfo, 1);
1120 si->store = g_object_ref (store);
1121 si->row = reference; /* takes ownership */
1122 si->full_hash = g_hash_table_new_full (
1123 g_str_hash, g_str_equal,
1124 (GDestroyNotify) g_free,
1125 (GDestroyNotify) gtk_tree_row_reference_free);
1126 g_hash_table_insert (model->priv->store_index, store, si);
1127
1128 /* Each store has folders, but we don't load them until
1129 * the user demands them. */
1130 root = iter;
1131 gtk_tree_store_append (tree_store, &iter, &root);
1132 gtk_tree_store_set (
1133 tree_store, &iter,
1134 COL_STRING_DISPLAY_NAME, _("Loading..."),
1135 COL_POINTER_CAMEL_STORE, store,
1136 COL_STRING_FULL_NAME, NULL,
1137 COL_BOOL_LOAD_SUBDIRS, FALSE,
1138 COL_BOOL_IS_STORE, FALSE,
1139 COL_BOOL_IS_FOLDER, FALSE,
1140 COL_UINT_UNREAD, 0,
1141 COL_UINT_UNREAD_LAST_SEL, 0,
1142 COL_BOOL_IS_DRAFT, FALSE,
1143 -1);
1144
1145 /* Listen to store events. */
1146 si->created_id = g_signal_connect (
1147 store, "folder-created",
1148 G_CALLBACK (folder_created_cb), model);
1149 si->deleted_id = g_signal_connect (
1150 store, "folder-deleted",
1151 G_CALLBACK (folder_deleted_cb), model);
1152 si->renamed_id = g_signal_connect (
1153 store, "folder_renamed",
1154 G_CALLBACK (folder_renamed_cb), model);
1155 if (CAMEL_IS_SUBSCRIBABLE (store)) {
1156 si->subscribed_id = g_signal_connect (
1157 store, "folder_subscribed",
1158 G_CALLBACK (folder_subscribed_cb), model);
1159 si->unsubscribed_id = g_signal_connect (
1160 store, "folder_unsubscribed",
1161 G_CALLBACK (folder_unsubscribed_cb), model);
1162 }
1163
1164 g_signal_emit (model, signals[LOADED_ROW], 0, path, &root);
1165 gtk_tree_path_free (path);
1166 }
1167
1168 void
1169 em_folder_tree_model_remove_store (EMFolderTreeModel *model,
1170 CamelStore *store)
1171 {
1172 EMFolderTreeModelStoreInfo *si;
1173 GtkTreePath *path;
1174 GtkTreeIter iter;
1175
1176 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
1177 g_return_if_fail (CAMEL_IS_STORE (store));
1178
1179 si = em_folder_tree_model_lookup_store_info (model, store);
1180 if (si == NULL)
1181 return;
1182
1183 path = gtk_tree_row_reference_get_path (si->row);
1184 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1185 gtk_tree_path_free (path);
1186
1187 /* recursively remove subfolders and finally the toplevel store */
1188 em_folder_tree_model_remove_folders (model, si, &iter);
1189 }
1190
1191 GList *
1192 em_folder_tree_model_list_stores (EMFolderTreeModel *model)
1193 {
1194 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
1195
1196 return g_hash_table_get_keys (model->priv->store_index);
1197 }
1198
1199 void
1200 em_folder_tree_model_remove_folders (EMFolderTreeModel *model,
1201 EMFolderTreeModelStoreInfo *si,
1202 GtkTreeIter *toplevel)
1203 {
1204 gchar *full_name;
1205 gboolean is_store, go;
1206 GtkTreeIter iter;
1207
1208 if (gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, toplevel)) {
1209 do {
1210 GtkTreeIter next = iter;
1211
1212 go = gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &next);
1213 em_folder_tree_model_remove_folders (model, si, &iter);
1214 iter = next;
1215 } while (go);
1216 }
1217
1218 gtk_tree_model_get (
1219 GTK_TREE_MODEL (model), toplevel,
1220 COL_STRING_FULL_NAME, &full_name,
1221 COL_BOOL_IS_STORE, &is_store, -1);
1222
1223 if (full_name != NULL)
1224 g_hash_table_remove (si->full_hash, full_name);
1225
1226 gtk_tree_store_remove ((GtkTreeStore *) model, toplevel);
1227
1228 /* Freeing the GtkTreeRowReference in the store info may finalize
1229 * the model. Keep the model alive until the store info is fully
1230 * removed from the hash table. */
1231 if (is_store) {
1232 g_object_ref (model);
1233 g_hash_table_remove (model->priv->store_index, si->store);
1234 g_object_unref (model);
1235 }
1236
1237 g_free (full_name);
1238 }
1239
1240 gboolean
1241 em_folder_tree_model_is_type_inbox (EMFolderTreeModel *model,
1242 CamelStore *store,
1243 const gchar *full)
1244 {
1245 EMFolderTreeModelStoreInfo *si;
1246 GtkTreeRowReference *reference;
1247 GtkTreePath *path;
1248 GtkTreeIter iter;
1249 guint32 flags;
1250
1251 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), FALSE);
1252 g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
1253 g_return_val_if_fail (full != NULL, FALSE);
1254
1255 si = em_folder_tree_model_lookup_store_info (model, store);
1256 if (si == NULL)
1257 return FALSE;
1258
1259 reference = g_hash_table_lookup (si->full_hash, full);
1260 if (!gtk_tree_row_reference_valid (reference))
1261 return FALSE;
1262
1263 path = gtk_tree_row_reference_get_path (reference);
1264 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1265 gtk_tree_path_free (path);
1266
1267 gtk_tree_model_get (
1268 GTK_TREE_MODEL (model), &iter,
1269 COL_UINT_FLAGS, &flags, -1);
1270
1271 return ((flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX);
1272 }
1273
1274 gchar *
1275 em_folder_tree_model_get_folder_name (EMFolderTreeModel *model,
1276 CamelStore *store,
1277 const gchar *full)
1278 {
1279 EMFolderTreeModelStoreInfo *si;
1280 GtkTreeRowReference *reference;
1281 GtkTreePath *path;
1282 GtkTreeIter iter;
1283 gchar *name = NULL;
1284
1285 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
1286 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1287 g_return_val_if_fail (full != NULL, NULL);
1288
1289 si = em_folder_tree_model_lookup_store_info (model, store);
1290 if (si == NULL)
1291 return g_strdup (full);
1292
1293 reference = g_hash_table_lookup (si->full_hash, full);
1294 if (!gtk_tree_row_reference_valid (reference))
1295 return g_strdup (full);
1296
1297 path = gtk_tree_row_reference_get_path (reference);
1298 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1299 gtk_tree_path_free (path);
1300
1301 gtk_tree_model_get (
1302 GTK_TREE_MODEL (model), &iter,
1303 COL_STRING_DISPLAY_NAME, &name, -1);
1304
1305 return name;
1306 }
1307
1308 EMFolderTreeModelStoreInfo *
1309 em_folder_tree_model_lookup_store_info (EMFolderTreeModel *model,
1310 CamelStore *store)
1311 {
1312 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
1313 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1314
1315 return g_hash_table_lookup (model->priv->store_index, store);
1316 }
1317
1318 GtkTreeRowReference *
1319 em_folder_tree_model_lookup_uri (EMFolderTreeModel *model,
1320 const gchar *folder_uri)
1321 {
1322 GtkTreeRowReference *reference = NULL;
1323 EMFolderTreeModelStoreInfo *si;
1324 EMailSession *session;
1325 CamelStore *store = NULL;
1326 gchar *folder_name = NULL;
1327 GError *error = NULL;
1328
1329 g_return_val_if_fail (EM_IS_FOLDER_TREE_MODEL (model), NULL);
1330 g_return_val_if_fail (folder_uri != NULL, NULL);
1331
1332 session = em_folder_tree_model_get_session (model);
1333 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1334
1335 e_mail_folder_uri_parse (
1336 CAMEL_SESSION (session),
1337 folder_uri, &store, &folder_name, &error);
1338
1339 if (error != NULL) {
1340 g_warn_if_fail (store == NULL);
1341 g_warn_if_fail (folder_name == NULL);
1342 g_warning ("%s: %s", G_STRFUNC, error->message);
1343 g_error_free (error);
1344 return NULL;
1345 }
1346
1347 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1348 g_return_val_if_fail (folder_name != NULL, NULL);
1349
1350 si = g_hash_table_lookup (model->priv->store_index, store);
1351
1352 if (si != NULL)
1353 reference = g_hash_table_lookup (si->full_hash, folder_name);
1354
1355 if (!gtk_tree_row_reference_valid (reference))
1356 reference = NULL;
1357
1358 g_object_unref (store);
1359 g_free (folder_name);
1360
1361 return reference;
1362 }
1363
1364 void
1365 em_folder_tree_model_user_marked_unread (EMFolderTreeModel *model,
1366 CamelFolder *folder,
1367 guint n_marked)
1368 {
1369 GtkTreeRowReference *reference;
1370 GtkTreePath *path;
1371 GtkTreeIter iter;
1372 gchar *folder_uri;
1373 guint unread;
1374
1375 /* The user marked messages in the given folder as unread.
1376 * Update our unread counts so we don't misinterpret this
1377 * event as new mail arriving. */
1378
1379 g_return_if_fail (EM_IS_FOLDER_TREE_MODEL (model));
1380 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1381
1382 folder_uri = e_mail_folder_uri_from_folder (folder);
1383 reference = em_folder_tree_model_lookup_uri (model, folder_uri);
1384 g_free (folder_uri);
1385
1386 g_return_if_fail (gtk_tree_row_reference_valid (reference));
1387
1388 path = gtk_tree_row_reference_get_path (reference);
1389 gtk_tree_model_get_iter (GTK_TREE_MODEL (model), &iter, path);
1390 gtk_tree_path_free (path);
1391
1392 gtk_tree_model_get (
1393 GTK_TREE_MODEL (model), &iter,
1394 COL_UINT_UNREAD, &unread, -1);
1395
1396 unread += n_marked;
1397
1398 gtk_tree_store_set (
1399 GTK_TREE_STORE (model), &iter,
1400 COL_UINT_UNREAD_LAST_SEL, unread,
1401 COL_UINT_UNREAD, unread, -1);
1402 }