No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-account-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-account-store.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * e-mail-account-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
19 #include "e-mail-account-store.h"
20
21 #include <config.h>
22 #include <glib/gstdio.h>
23 #include <glib/gi18n-lib.h>
24
25 #include <libebackend/libebackend.h>
26
27 #include <e-util/e-marshal.h>
28 #include <libevolution-utils/e-alert-dialog.h>
29
30 #include <libemail-engine/mail-ops.h>
31
32 #include <mail/mail-vfolder-ui.h>
33
34 #define E_MAIL_ACCOUNT_STORE_GET_PRIVATE(obj) \
35 (G_TYPE_INSTANCE_GET_PRIVATE \
36 ((obj), E_TYPE_MAIL_ACCOUNT_STORE, EMailAccountStorePrivate))
37
38 typedef struct _IndexItem IndexItem;
39
40 struct _EMailAccountStorePrivate {
41 CamelService *default_service;
42 GHashTable *service_index;
43 gchar *sort_order_filename;
44 gboolean express_mode;
45 gpointer session; /* weak pointer */
46 guint busy_count;
47 };
48
49 struct _IndexItem {
50 CamelService *service;
51 GtkTreeRowReference *reference;
52 gulong notify_handler_id;
53 };
54
55 enum {
56 PROP_0,
57 PROP_BUSY,
58 PROP_DEFAULT_SERVICE,
59 PROP_EXPRESS_MODE,
60 PROP_SESSION
61 };
62
63 enum {
64 SERVICE_ADDED,
65 SERVICE_REMOVED,
66 SERVICE_ENABLED,
67 SERVICE_DISABLED,
68 SERVICES_REORDERED,
69 REMOVE_REQUESTED,
70 ENABLE_REQUESTED,
71 DISABLE_REQUESTED,
72 LAST_SIGNAL
73 };
74
75 static guint signals[LAST_SIGNAL];
76
77 /* Forward Declarations */
78 static void e_mail_account_store_interface_init
79 (GtkTreeModelIface *interface);
80
81 G_DEFINE_TYPE_WITH_CODE (
82 EMailAccountStore,
83 e_mail_account_store,
84 GTK_TYPE_LIST_STORE,
85 G_IMPLEMENT_INTERFACE (
86 GTK_TYPE_TREE_MODEL,
87 e_mail_account_store_interface_init)
88 G_IMPLEMENT_INTERFACE (
89 E_TYPE_EXTENSIBLE, NULL))
90
91 static void
92 index_item_free (IndexItem *item)
93 {
94 g_signal_handler_disconnect (
95 item->service, item->notify_handler_id);
96
97 g_object_unref (item->service);
98 gtk_tree_row_reference_free (item->reference);
99
100 g_slice_free (IndexItem, item);
101 }
102
103 static gboolean
104 mail_account_store_get_iter (EMailAccountStore *store,
105 CamelService *service,
106 GtkTreeIter *iter)
107 {
108 IndexItem *item;
109 GtkTreeModel *model;
110 GtkTreePath *path;
111 gboolean iter_set;
112
113 g_return_val_if_fail (service != NULL, FALSE);
114
115 item = g_hash_table_lookup (store->priv->service_index, service);
116
117 if (item == NULL)
118 return FALSE;
119
120 if (!gtk_tree_row_reference_valid (item->reference))
121 return FALSE;
122
123 model = gtk_tree_row_reference_get_model (item->reference);
124 path = gtk_tree_row_reference_get_path (item->reference);
125 iter_set = gtk_tree_model_get_iter (model, iter, path);
126 gtk_tree_path_free (path);
127
128 return iter_set;
129 }
130
131 static gint
132 mail_account_store_default_compare (CamelService *service_a,
133 CamelService *service_b,
134 EMailAccountStore *store)
135 {
136 const gchar *display_name_a;
137 const gchar *display_name_b;
138 const gchar *uid_a;
139 const gchar *uid_b;
140
141 uid_a = camel_service_get_uid (service_a);
142 uid_b = camel_service_get_uid (service_b);
143
144 /* Check for special cases first. */
145
146 if (e_mail_account_store_get_express_mode (store)) {
147 if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID) &&
148 g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
149 return -1;
150 else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID) &&
151 g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
152 return 1;
153 else if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
154 return 1;
155 else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
156 return -1;
157 else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
158 return 1;
159 else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
160 return -1;
161 } else {
162 if (g_str_equal (uid_a, E_MAIL_SESSION_LOCAL_UID))
163 return -1;
164 else if (g_str_equal (uid_b, E_MAIL_SESSION_LOCAL_UID))
165 return 1;
166 else if (g_str_equal (uid_a, E_MAIL_SESSION_VFOLDER_UID))
167 return 1;
168 else if (g_str_equal (uid_b, E_MAIL_SESSION_VFOLDER_UID))
169 return -1;
170 }
171
172 /* Otherwise sort them alphabetically. */
173
174 display_name_a = camel_service_get_display_name (service_a);
175 display_name_b = camel_service_get_display_name (service_b);
176
177 if (display_name_a == NULL)
178 display_name_a = "";
179
180 if (display_name_b == NULL)
181 display_name_b = "";
182
183 return g_utf8_collate (display_name_a, display_name_b);
184 }
185
186 static void
187 mail_account_store_update_row (EMailAccountStore *store,
188 CamelService *service,
189 GtkTreeIter *iter)
190 {
191 CamelProvider *provider;
192 gboolean is_default;
193 const gchar *backend_name;
194 const gchar *display_name;
195
196 is_default = (service == store->priv->default_service);
197 display_name = camel_service_get_display_name (service);
198
199 provider = camel_service_get_provider (service);
200 backend_name = (provider != NULL) ? provider->protocol : NULL;
201
202 gtk_list_store_set (
203 GTK_LIST_STORE (store), iter,
204 E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT, is_default,
205 E_MAIL_ACCOUNT_STORE_COLUMN_BACKEND_NAME, backend_name,
206 E_MAIL_ACCOUNT_STORE_COLUMN_DISPLAY_NAME, display_name,
207 -1);
208 }
209
210 static void
211 mail_account_store_service_notify_cb (CamelService *service,
212 GParamSpec *pspec,
213 EMailAccountStore *store)
214 {
215 GtkTreeIter iter;
216
217 if (mail_account_store_get_iter (store, service, &iter))
218 mail_account_store_update_row (store, service, &iter);
219 }
220
221 static void
222 mail_account_store_remove_source_cb (ESource *source,
223 GAsyncResult *result,
224 EMailAccountStore *store)
225 {
226 GError *error = NULL;
227
228 /* FIXME EMailAccountStore should implement EAlertSink. */
229 if (!e_source_remove_finish (source, result, &error)) {
230 g_warning ("%s: %s", G_STRFUNC, error->message);
231 g_error_free (error);
232 }
233
234 g_return_if_fail (store->priv->busy_count > 0);
235 store->priv->busy_count--;
236 g_object_notify (G_OBJECT (store), "busy");
237
238 g_object_unref (store);
239 }
240
241 static void
242 mail_account_store_write_source_cb (ESource *source,
243 GAsyncResult *result,
244 EMailAccountStore *store)
245 {
246 GError *error = NULL;
247
248 /* FIXME EMailAccountStore should implement EAlertSink. */
249 if (!e_source_write_finish (source, result, &error)) {
250 g_warning ("%s: %s", G_STRFUNC, error->message);
251 g_error_free (error);
252 }
253
254 g_return_if_fail (store->priv->busy_count > 0);
255 store->priv->busy_count--;
256 g_object_notify (G_OBJECT (store), "busy");
257
258 g_object_unref (store);
259 }
260
261 static void
262 mail_account_store_clean_index (EMailAccountStore *store)
263 {
264 GQueue trash = G_QUEUE_INIT;
265 GHashTable *hash_table;
266 GHashTableIter iter;
267 gpointer key, value;
268
269 hash_table = store->priv->service_index;
270 g_hash_table_iter_init (&iter, hash_table);
271
272 /* Remove index items with invalid GtkTreeRowReferences. */
273
274 while (g_hash_table_iter_next (&iter, &key, &value)) {
275 IndexItem *item = value;
276
277 if (!gtk_tree_row_reference_valid (item->reference))
278 g_queue_push_tail (&trash, key);
279 }
280
281 while ((key = g_queue_pop_head (&trash)) != NULL)
282 g_hash_table_remove (hash_table, key);
283 }
284
285 static void
286 mail_account_store_update_index (EMailAccountStore *store,
287 GtkTreePath *path,
288 GtkTreeIter *iter)
289 {
290 CamelService *service = NULL;
291 GHashTable *hash_table;
292 GtkTreeModel *model;
293 IndexItem *item;
294
295 model = GTK_TREE_MODEL (store);
296 hash_table = store->priv->service_index;
297
298 gtk_tree_model_get (
299 model, iter,
300 E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, &service, -1);
301
302 if (service == NULL)
303 return;
304
305 item = g_hash_table_lookup (hash_table, service);
306
307 if (item == NULL) {
308 item = g_slice_new0 (IndexItem);
309 item->service = g_object_ref (service);
310
311 item->notify_handler_id = g_signal_connect (
312 service, "notify", G_CALLBACK (
313 mail_account_store_service_notify_cb), store);
314
315 g_hash_table_insert (hash_table, item->service, item);
316 }
317
318 /* Update the row reference so the IndexItem will survive
319 * drag-and-drop (new row is inserted, old row is deleted). */
320 gtk_tree_row_reference_free (item->reference);
321 item->reference = gtk_tree_row_reference_new (model, path);
322
323 g_object_unref (service);
324 }
325
326 static void
327 mail_account_store_set_session (EMailAccountStore *store,
328 EMailSession *session)
329 {
330 g_return_if_fail (E_IS_MAIL_SESSION (session));
331 g_return_if_fail (store->priv->session == NULL);
332
333 store->priv->session = session;
334
335 g_object_add_weak_pointer (
336 G_OBJECT (store->priv->session),
337 &store->priv->session);
338 }
339
340 static void
341 mail_account_store_set_property (GObject *object,
342 guint property_id,
343 const GValue *value,
344 GParamSpec *pspec)
345 {
346 switch (property_id) {
347 case PROP_DEFAULT_SERVICE:
348 e_mail_account_store_set_default_service (
349 E_MAIL_ACCOUNT_STORE (object),
350 g_value_get_object (value));
351 return;
352
353 case PROP_EXPRESS_MODE:
354 e_mail_account_store_set_express_mode (
355 E_MAIL_ACCOUNT_STORE (object),
356 g_value_get_boolean (value));
357 return;
358
359 case PROP_SESSION:
360 mail_account_store_set_session (
361 E_MAIL_ACCOUNT_STORE (object),
362 g_value_get_object (value));
363 return;
364 }
365
366 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
367 }
368
369 static void
370 mail_account_store_get_property (GObject *object,
371 guint property_id,
372 GValue *value,
373 GParamSpec *pspec)
374 {
375 switch (property_id) {
376 case PROP_BUSY:
377 g_value_set_boolean (
378 value,
379 e_mail_account_store_get_busy (
380 E_MAIL_ACCOUNT_STORE (object)));
381 return;
382
383 case PROP_DEFAULT_SERVICE:
384 g_value_set_object (
385 value,
386 e_mail_account_store_get_default_service (
387 E_MAIL_ACCOUNT_STORE (object)));
388 return;
389
390 case PROP_EXPRESS_MODE:
391 g_value_set_boolean (
392 value,
393 e_mail_account_store_get_express_mode (
394 E_MAIL_ACCOUNT_STORE (object)));
395 return;
396
397 case PROP_SESSION:
398 g_value_set_object (
399 value,
400 e_mail_account_store_get_session (
401 E_MAIL_ACCOUNT_STORE (object)));
402 return;
403 }
404
405 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
406 }
407
408 static void
409 mail_account_store_dispose (GObject *object)
410 {
411 EMailAccountStorePrivate *priv;
412
413 priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
414
415 if (priv->session != NULL) {
416 g_object_remove_weak_pointer (
417 G_OBJECT (priv->session), &priv->session);
418 priv->session = NULL;
419 }
420
421 if (priv->default_service != NULL) {
422 g_object_unref (priv->default_service);
423 priv->default_service = NULL;
424 }
425
426 g_hash_table_remove_all (priv->service_index);
427
428 /* Chain up to parent's dispose() method. */
429 G_OBJECT_CLASS (e_mail_account_store_parent_class)->dispose (object);
430 }
431
432 static void
433 mail_account_store_finalize (GObject *object)
434 {
435 EMailAccountStorePrivate *priv;
436
437 priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (object);
438
439 g_warn_if_fail (priv->busy_count == 0);
440 g_hash_table_destroy (priv->service_index);
441 g_free (priv->sort_order_filename);
442
443 /* Chain up to parent's finalize() method. */
444 G_OBJECT_CLASS (e_mail_account_store_parent_class)->finalize (object);
445 }
446
447 static void
448 mail_account_store_constructed (GObject *object)
449 {
450 EMailAccountStore *store;
451 EMailSession *session;
452 ESourceRegistry *registry;
453 const gchar *config_dir;
454
455 /* Chain up to parent's constructed() method. */
456 G_OBJECT_CLASS (e_mail_account_store_parent_class)->constructed (object);
457
458 store = E_MAIL_ACCOUNT_STORE (object);
459 session = e_mail_account_store_get_session (store);
460 registry = e_mail_session_get_registry (session);
461
462 /* Bind the default mail account ESource to our default
463 * CamelService, with help from some transform functions. */
464 g_object_bind_property_full (
465 registry, "default-mail-account",
466 store, "default-service",
467 G_BINDING_BIDIRECTIONAL |
468 G_BINDING_SYNC_CREATE,
469 e_binding_transform_source_to_service,
470 e_binding_transform_service_to_source,
471 session, (GDestroyNotify) NULL);
472
473 config_dir = mail_session_get_config_dir ();
474
475 /* XXX Should we take the filename as a constructor property? */
476 store->priv->sort_order_filename = g_build_filename (
477 config_dir, "sortorder.ini", NULL);
478
479 e_extensible_load_extensions (E_EXTENSIBLE (object));
480 }
481
482 static void
483 mail_account_store_service_added (EMailAccountStore *store,
484 CamelService *service)
485 {
486 /* Placeholder so subclasses can safely chain up. */
487 }
488
489 static void
490 mail_account_store_service_removed (EMailAccountStore *store,
491 CamelService *service)
492 {
493 EMailSession *session;
494 MailFolderCache *cache;
495 ESourceRegistry *registry;
496 ESource *source;
497 const gchar *uid;
498
499 session = e_mail_account_store_get_session (store);
500 cache = e_mail_session_get_folder_cache (session);
501
502 mail_folder_cache_service_removed (cache, service);
503
504 uid = camel_service_get_uid (service);
505 registry = e_mail_session_get_registry (session);
506 source = e_source_registry_ref_source (registry, uid);
507
508 /* If this ESource is part of a collection, we need to remove
509 * the entire collection. Check the ESource and its ancestors
510 * for a collection extension and remove the containing source. */
511 if (source != NULL) {
512 ESource *collection;
513
514 collection = e_source_registry_find_extension (
515 registry, source, E_SOURCE_EXTENSION_COLLECTION);
516 if (collection != NULL) {
517 g_object_unref (source);
518 source = collection;
519 }
520 }
521
522 if (source != NULL && e_source_get_removable (source)) {
523 store->priv->busy_count++;
524 g_object_notify (G_OBJECT (store), "busy");
525
526 /* XXX Should this be cancellable? */
527 e_source_remove (
528 source, NULL, (GAsyncReadyCallback)
529 mail_account_store_remove_source_cb,
530 g_object_ref (store));
531
532 g_object_unref (source);
533 }
534 }
535
536 static void
537 mail_account_store_service_enabled (EMailAccountStore *store,
538 CamelService *service)
539 {
540 EMailSession *session;
541 MailFolderCache *cache;
542 ESourceRegistry *registry;
543 ESource *source;
544 const gchar *uid;
545
546 session = e_mail_account_store_get_session (store);
547 cache = e_mail_session_get_folder_cache (session);
548
549 mail_folder_cache_service_enabled (cache, service);
550
551 uid = camel_service_get_uid (service);
552 registry = e_mail_session_get_registry (session);
553 source = e_source_registry_ref_source (registry, uid);
554
555 /* Locate the identity source referenced in the [Mail Account]
556 * extension. We want to keep its enabled state synchronized
557 * with the account's enabled state. (Need to do this before
558 * we swap the mail account ESource for a collection below.) */
559 if (source != NULL) {
560 ESource *identity = NULL;
561 ESourceMailAccount *extension;
562 const gchar *extension_name;
563
564 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
565 extension = e_source_get_extension (source, extension_name);
566 uid = e_source_mail_account_get_identity_uid (extension);
567
568 if (uid != NULL)
569 identity = e_source_registry_ref_source (registry, uid);
570
571 if (identity != NULL && e_source_get_writable (identity)) {
572 e_source_set_enabled (identity, TRUE);
573
574 store->priv->busy_count++;
575 g_object_notify (G_OBJECT (store), "busy");
576
577 /* XXX Should this be cancellable? */
578 e_source_write (
579 identity, NULL, (GAsyncReadyCallback)
580 mail_account_store_write_source_cb,
581 g_object_ref (store));
582
583 g_object_unref (identity);
584 }
585 }
586
587 /* If this ESource is part of a collection, we need to enable
588 * the entire collection. Check the ESource and its ancestors
589 * for a collection extension and enable the containing source. */
590 if (source != NULL) {
591 ESource *collection;
592
593 collection = e_source_registry_find_extension (
594 registry, source, E_SOURCE_EXTENSION_COLLECTION);
595 if (collection != NULL) {
596 g_object_unref (source);
597 source = collection;
598 }
599 }
600
601 if (source != NULL && e_source_get_writable (source)) {
602 e_source_set_enabled (source, TRUE);
603
604 store->priv->busy_count++;
605 g_object_notify (G_OBJECT (store), "busy");
606
607 /* XXX Should this be cancellable? */
608 e_source_write (
609 source, NULL, (GAsyncReadyCallback)
610 mail_account_store_write_source_cb,
611 g_object_ref (store));
612
613 g_object_unref (source);
614 }
615 }
616
617 static void
618 mail_account_store_service_disabled (EMailAccountStore *store,
619 CamelService *service)
620 {
621 EMailSession *session;
622 MailFolderCache *cache;
623 ESourceRegistry *registry;
624 ESource *source;
625 const gchar *uid;
626
627 session = e_mail_account_store_get_session (store);
628 cache = e_mail_session_get_folder_cache (session);
629
630 mail_folder_cache_service_disabled (cache, service);
631
632 uid = camel_service_get_uid (service);
633 registry = e_mail_session_get_registry (session);
634 source = e_source_registry_ref_source (registry, uid);
635
636 /* Locate the identity source referenced in the [Mail Account]
637 * extension. We want to keep its enabled state synchronized
638 * with the account's enabled state. (Need to do this before
639 * we swap the mail account ESource for a collection below.) */
640 if (source != NULL) {
641 ESource *identity = NULL;
642 ESourceMailAccount *extension;
643 const gchar *extension_name;
644
645 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
646 extension = e_source_get_extension (source, extension_name);
647 uid = e_source_mail_account_get_identity_uid (extension);
648
649 if (uid != NULL)
650 identity = e_source_registry_ref_source (registry, uid);
651
652 if (identity != NULL && e_source_get_writable (identity)) {
653 e_source_set_enabled (identity, FALSE);
654
655 store->priv->busy_count++;
656 g_object_notify (G_OBJECT (store), "busy");
657
658 /* XXX Should this be cancellable? */
659 e_source_write (
660 identity, NULL, (GAsyncReadyCallback)
661 mail_account_store_write_source_cb,
662 g_object_ref (store));
663
664 g_object_unref (identity);
665 }
666 }
667
668 /* If this ESource is part of a collection, we need to disable
669 * the entire collection. Check the ESource and its ancestors
670 * for a collection extension and disable the containing source. */
671 if (source != NULL) {
672 ESource *collection;
673
674 collection = e_source_registry_find_extension (
675 registry, source, E_SOURCE_EXTENSION_COLLECTION);
676 if (collection != NULL) {
677 g_object_unref (source);
678 source = collection;
679 }
680 }
681
682 if (source != NULL && e_source_get_writable (source)) {
683 e_source_set_enabled (source, FALSE);
684
685 store->priv->busy_count++;
686 g_object_notify (G_OBJECT (store), "busy");
687
688 /* XXX Should this be cancellable? */
689 e_source_write (
690 source, NULL, (GAsyncReadyCallback)
691 mail_account_store_write_source_cb,
692 g_object_ref (store));
693
694 g_object_unref (source);
695 }
696 }
697
698 static void
699 mail_account_store_services_reordered (EMailAccountStore *store,
700 gboolean default_restored)
701 {
702 /* XXX Should this be made asynchronous? */
703
704 GError *error = NULL;
705
706 if (default_restored) {
707 const gchar *filename;
708
709 filename = store->priv->sort_order_filename;
710
711 if (g_file_test (filename, G_FILE_TEST_EXISTS))
712 g_unlink (filename);
713
714 return;
715 }
716
717 if (!e_mail_account_store_save_sort_order (store, &error)) {
718 g_warning ("%s: %s", G_STRFUNC, error->message);
719 g_error_free (error);
720 }
721 }
722
723 static gboolean
724 mail_account_store_remove_requested (EMailAccountStore *store,
725 GtkWindow *parent_window,
726 CamelService *service)
727 {
728 gint response;
729
730 /* FIXME Need to use "mail:ask-delete-account-with-proxies" if the
731 * mail account has proxies. But this is groupwise-specific
732 * and doesn't belong here anyway. Think of a better idea. */
733
734 response = e_alert_run_dialog_for_args (
735 parent_window, "mail:ask-delete-account", NULL);
736
737 return (response == GTK_RESPONSE_YES);
738 }
739
740 static gboolean
741 mail_account_store_enable_requested (EMailAccountStore *store,
742 GtkWindow *parent_window,
743 CamelService *service)
744 {
745 return TRUE;
746 }
747
748 static gboolean
749 mail_account_store_disable_requested (EMailAccountStore *store,
750 GtkWindow *parent_window,
751 CamelService *service)
752 {
753 /* FIXME Need to check whether the account has proxies and run a
754 * "mail:ask-delete-proxy-accounts" alert dialog, but this
755 * is groupwise-specific and doesn't belong here anyway.
756 * Think of a better idea. */
757
758 return TRUE;
759 }
760
761 static void
762 mail_account_store_row_changed (GtkTreeModel *tree_model,
763 GtkTreePath *path,
764 GtkTreeIter *iter)
765 {
766 EMailAccountStore *store;
767
768 /* Neither GtkTreeModel nor GtkListStore implements
769 * this method, so there is nothing to chain up to. */
770
771 store = E_MAIL_ACCOUNT_STORE (tree_model);
772 mail_account_store_update_index (store, path, iter);
773 }
774
775 static void
776 mail_account_store_row_inserted (GtkTreeModel *tree_model,
777 GtkTreePath *path,
778 GtkTreeIter *iter)
779 {
780 EMailAccountStore *store;
781
782 /* Neither GtkTreeModel nor GtkListStore implements
783 * this method, so there is nothing to chain up to. */
784
785 store = E_MAIL_ACCOUNT_STORE (tree_model);
786 mail_account_store_update_index (store, path, iter);
787 }
788
789 static gboolean
790 mail_account_store_true_proceed (GSignalInvocationHint *ihint,
791 GValue *return_accumulator,
792 const GValue *handler_return,
793 gpointer not_used)
794 {
795 gboolean proceed;
796
797 proceed = g_value_get_boolean (handler_return);
798 g_value_set_boolean (return_accumulator, proceed);
799
800 return proceed;
801 }
802
803 static void
804 e_mail_account_store_class_init (EMailAccountStoreClass *class)
805 {
806 GObjectClass *object_class;
807
808 g_type_class_add_private (class, sizeof (EMailAccountStorePrivate));
809
810 object_class = G_OBJECT_CLASS (class);
811 object_class->set_property = mail_account_store_set_property;
812 object_class->get_property = mail_account_store_get_property;
813 object_class->dispose = mail_account_store_dispose;
814 object_class->finalize = mail_account_store_finalize;
815 object_class->constructed = mail_account_store_constructed;
816
817 class->service_added = mail_account_store_service_added;
818 class->service_removed = mail_account_store_service_removed;
819 class->service_enabled = mail_account_store_service_enabled;
820 class->service_disabled = mail_account_store_service_disabled;
821 class->services_reordered = mail_account_store_services_reordered;
822 class->remove_requested = mail_account_store_remove_requested;
823 class->enable_requested = mail_account_store_enable_requested;
824 class->disable_requested = mail_account_store_disable_requested;
825
826 g_object_class_install_property (
827 object_class,
828 PROP_BUSY,
829 g_param_spec_boolean (
830 "busy",
831 "Busy",
832 "Whether async operations are in progress",
833 FALSE,
834 G_PARAM_READABLE |
835 G_PARAM_STATIC_STRINGS));
836
837 g_object_class_install_property (
838 object_class,
839 PROP_DEFAULT_SERVICE,
840 g_param_spec_object (
841 "default-service",
842 "Default Service",
843 "Default mail store",
844 CAMEL_TYPE_SERVICE,
845 G_PARAM_READWRITE |
846 G_PARAM_STATIC_STRINGS));
847
848 g_object_class_install_property (
849 object_class,
850 PROP_EXPRESS_MODE,
851 g_param_spec_boolean (
852 "express-mode",
853 "Express Mode",
854 "Whether express mode is enabled",
855 FALSE,
856 G_PARAM_READWRITE |
857 G_PARAM_STATIC_STRINGS));
858
859 g_object_class_install_property (
860 object_class,
861 PROP_SESSION,
862 g_param_spec_object (
863 "session",
864 "Session",
865 "Mail session",
866 E_TYPE_MAIL_SESSION,
867 G_PARAM_READWRITE |
868 G_PARAM_CONSTRUCT_ONLY |
869 G_PARAM_STATIC_STRINGS));
870
871 signals[SERVICE_ADDED] = g_signal_new (
872 "service-added",
873 G_TYPE_FROM_CLASS (class),
874 G_SIGNAL_RUN_LAST,
875 G_STRUCT_OFFSET (EMailAccountStoreClass, service_added),
876 NULL, NULL,
877 g_cclosure_marshal_VOID__OBJECT,
878 G_TYPE_NONE, 1,
879 CAMEL_TYPE_SERVICE);
880
881 signals[SERVICE_REMOVED] = g_signal_new (
882 "service-removed",
883 G_TYPE_FROM_CLASS (class),
884 G_SIGNAL_RUN_LAST,
885 G_STRUCT_OFFSET (EMailAccountStoreClass, service_removed),
886 NULL, NULL,
887 g_cclosure_marshal_VOID__OBJECT,
888 G_TYPE_NONE, 1,
889 CAMEL_TYPE_SERVICE);
890
891 signals[SERVICE_ENABLED] = g_signal_new (
892 "service-enabled",
893 G_TYPE_FROM_CLASS (class),
894 G_SIGNAL_RUN_LAST,
895 G_STRUCT_OFFSET (EMailAccountStoreClass, service_enabled),
896 NULL, NULL,
897 g_cclosure_marshal_VOID__OBJECT,
898 G_TYPE_NONE, 1,
899 CAMEL_TYPE_SERVICE);
900
901 signals[SERVICE_DISABLED] = g_signal_new (
902 "service-disabled",
903 G_TYPE_FROM_CLASS (class),
904 G_SIGNAL_RUN_LAST,
905 G_STRUCT_OFFSET (EMailAccountStoreClass, service_disabled),
906 NULL, NULL,
907 g_cclosure_marshal_VOID__OBJECT,
908 G_TYPE_NONE, 1,
909 CAMEL_TYPE_SERVICE);
910
911 signals[SERVICES_REORDERED] = g_signal_new (
912 "services-reordered",
913 G_TYPE_FROM_CLASS (class),
914 G_SIGNAL_RUN_LAST,
915 G_STRUCT_OFFSET (EMailAccountStoreClass, services_reordered),
916 NULL, NULL,
917 g_cclosure_marshal_VOID__BOOLEAN,
918 G_TYPE_NONE, 1,
919 G_TYPE_BOOLEAN);
920
921 signals[REMOVE_REQUESTED] = g_signal_new (
922 "remove-requested",
923 G_TYPE_FROM_CLASS (class),
924 G_SIGNAL_RUN_LAST,
925 G_STRUCT_OFFSET (EMailAccountStoreClass, remove_requested),
926 mail_account_store_true_proceed, NULL,
927 e_marshal_BOOLEAN__OBJECT_OBJECT,
928 G_TYPE_BOOLEAN, 2,
929 GTK_TYPE_WINDOW,
930 CAMEL_TYPE_SERVICE);
931
932 signals[ENABLE_REQUESTED] = g_signal_new (
933 "enable-requested",
934 G_TYPE_FROM_CLASS (class),
935 G_SIGNAL_RUN_LAST,
936 G_STRUCT_OFFSET (EMailAccountStoreClass, enable_requested),
937 mail_account_store_true_proceed, NULL,
938 e_marshal_BOOLEAN__OBJECT_OBJECT,
939 G_TYPE_BOOLEAN, 2,
940 GTK_TYPE_WINDOW,
941 CAMEL_TYPE_SERVICE);
942
943 signals[DISABLE_REQUESTED] = g_signal_new (
944 "disable-requested",
945 G_TYPE_FROM_CLASS (class),
946 G_SIGNAL_RUN_LAST,
947 G_STRUCT_OFFSET (EMailAccountStoreClass, disable_requested),
948 mail_account_store_true_proceed, NULL,
949 e_marshal_BOOLEAN__OBJECT_OBJECT,
950 G_TYPE_BOOLEAN, 2,
951 GTK_TYPE_WINDOW,
952 CAMEL_TYPE_SERVICE);
953 }
954
955 static void
956 e_mail_account_store_interface_init (GtkTreeModelIface *interface)
957 {
958 interface->row_changed = mail_account_store_row_changed;
959 interface->row_inserted = mail_account_store_row_inserted;
960 }
961
962 static void
963 e_mail_account_store_init (EMailAccountStore *store)
964 {
965 GType types[E_MAIL_ACCOUNT_STORE_NUM_COLUMNS];
966 GHashTable *service_index;
967 gint ii = 0;
968
969 service_index = g_hash_table_new_full (
970 (GHashFunc) g_direct_hash,
971 (GEqualFunc) g_direct_equal,
972 (GDestroyNotify) NULL,
973 (GDestroyNotify) index_item_free);
974
975 store->priv = E_MAIL_ACCOUNT_STORE_GET_PRIVATE (store);
976 store->priv->service_index = service_index;
977
978 types[ii++] = CAMEL_TYPE_SERVICE; /* COLUMN_SERVICE */
979 types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_BUILTIN */
980 types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_ENABLED */
981 types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_DEFAULT */
982 types[ii++] = G_TYPE_STRING; /* COLUMN_BACKEND_NAME */
983 types[ii++] = G_TYPE_STRING; /* COLUMN_DISPLAY_NAME */
984 types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_ONLINE_ACCOUNT */
985 types[ii++] = G_TYPE_BOOLEAN; /* COLUMN_ENABLED_VISIBLE */
986
987 g_assert (ii == E_MAIL_ACCOUNT_STORE_NUM_COLUMNS);
988
989 gtk_list_store_set_column_types (
990 GTK_LIST_STORE (store),
991 G_N_ELEMENTS (types), types);
992 }
993
994 EMailAccountStore *
995 e_mail_account_store_new (EMailSession *session)
996 {
997 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
998
999 return g_object_new (
1000 E_TYPE_MAIL_ACCOUNT_STORE,
1001 "session", session, NULL);
1002 }
1003
1004 void
1005 e_mail_account_store_clear (EMailAccountStore *store)
1006 {
1007 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1008
1009 gtk_list_store_clear (GTK_LIST_STORE (store));
1010 g_hash_table_remove_all (store->priv->service_index);
1011 }
1012
1013 gboolean
1014 e_mail_account_store_get_busy (EMailAccountStore *store)
1015 {
1016 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1017
1018 return (store->priv->busy_count > 0);
1019 }
1020
1021 EMailSession *
1022 e_mail_account_store_get_session (EMailAccountStore *store)
1023 {
1024 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
1025
1026 return E_MAIL_SESSION (store->priv->session);
1027 }
1028
1029 CamelService *
1030 e_mail_account_store_get_default_service (EMailAccountStore *store)
1031 {
1032 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), NULL);
1033
1034 return store->priv->default_service;
1035 }
1036
1037 void
1038 e_mail_account_store_set_default_service (EMailAccountStore *store,
1039 CamelService *service)
1040 {
1041 GtkTreeModel *model;
1042 GtkTreeIter iter;
1043 gboolean iter_set;
1044
1045 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1046
1047 if (service == store->priv->default_service)
1048 return;
1049
1050 if (service != NULL) {
1051 g_return_if_fail (CAMEL_IS_SERVICE (service));
1052 g_object_ref (service);
1053 }
1054
1055 if (store->priv->default_service != NULL)
1056 g_object_unref (store->priv->default_service);
1057
1058 store->priv->default_service = service;
1059
1060 model = GTK_TREE_MODEL (store);
1061 iter_set = gtk_tree_model_get_iter_first (model, &iter);
1062
1063 while (iter_set) {
1064 CamelService *candidate;
1065
1066 gtk_tree_model_get (
1067 model, &iter,
1068 E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE,
1069 &candidate, -1);
1070
1071 gtk_list_store_set (
1072 GTK_LIST_STORE (model), &iter,
1073 E_MAIL_ACCOUNT_STORE_COLUMN_DEFAULT,
1074 service == candidate, -1);
1075
1076 g_object_unref (candidate);
1077
1078 iter_set = gtk_tree_model_iter_next (model, &iter);
1079 }
1080
1081 g_object_notify (G_OBJECT (store), "default-service");
1082 }
1083
1084 gboolean
1085 e_mail_account_store_get_express_mode (EMailAccountStore *store)
1086 {
1087 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1088
1089 return store->priv->express_mode;
1090 }
1091
1092 void
1093 e_mail_account_store_set_express_mode (EMailAccountStore *store,
1094 gboolean express_mode)
1095 {
1096 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1097
1098 if (store->priv->express_mode == express_mode)
1099 return;
1100
1101 store->priv->express_mode = express_mode;
1102
1103 g_object_notify (G_OBJECT (store), "express-mode");
1104 }
1105
1106 void
1107 e_mail_account_store_add_service (EMailAccountStore *store,
1108 CamelService *service)
1109 {
1110 EMailSession *session;
1111 ESourceRegistry *registry;
1112 ESource *collection;
1113 ESource *source;
1114 GtkTreeIter iter;
1115 const gchar *filename;
1116 const gchar *uid;
1117 gboolean builtin;
1118 gboolean enabled;
1119 gboolean online_account = FALSE;
1120 gboolean enabled_visible = TRUE;
1121
1122 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1123 g_return_if_fail (CAMEL_IS_SERVICE (service));
1124
1125 /* Avoid duplicate services in the account store. */
1126 if (mail_account_store_get_iter (store, service, &iter))
1127 g_return_if_reached ();
1128
1129 uid = camel_service_get_uid (service);
1130
1131 builtin =
1132 (g_strcmp0 (uid, E_MAIL_SESSION_LOCAL_UID) == 0) ||
1133 (g_strcmp0 (uid, E_MAIL_SESSION_VFOLDER_UID) == 0);
1134
1135 session = e_mail_account_store_get_session (store);
1136
1137 registry = e_mail_session_get_registry (session);
1138 source = e_source_registry_ref_source (registry, uid);
1139 g_return_if_fail (source != NULL);
1140
1141 /* If this ESource is part of a collection, we need to
1142 * pick up the enabled state for the entire collection.
1143 * Check the ESource and its ancestors for a collection
1144 * extension and read from the containing source. */
1145 collection = e_source_registry_find_extension (
1146 registry, source, E_SOURCE_EXTENSION_COLLECTION);
1147 if (collection != NULL) {
1148 const gchar *extension_name;
1149
1150 enabled = e_source_get_enabled (collection);
1151
1152 /* Check for GNOME Online Accounts linkage. */
1153 extension_name = E_SOURCE_EXTENSION_GOA;
1154 if (e_source_has_extension (collection, extension_name)) {
1155 online_account = TRUE;
1156 enabled_visible = FALSE;
1157 }
1158
1159 g_object_unref (collection);
1160 } else {
1161 enabled = e_source_get_enabled (source);
1162 }
1163
1164 g_object_unref (source);
1165
1166 /* Where do we insert new services now that accounts can be
1167 * reordered? This is just a simple policy I came up with.
1168 * It's certainly subject to debate and tweaking.
1169 *
1170 * Always insert new services in row 0 initially. Then test
1171 * for the presence of the sort order file. If present, the
1172 * user has messed around with the ordering so leave the new
1173 * service at row 0. If not present, services are sorted in
1174 * their default order. So re-apply the default order using
1175 * e_mail_account_store_reorder_services(store, NULL) so the
1176 * new service moves to its proper default position. */
1177
1178 gtk_list_store_prepend (GTK_LIST_STORE (store), &iter);
1179
1180 gtk_list_store_set (
1181 GTK_LIST_STORE (store), &iter,
1182 E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE, service,
1183 E_MAIL_ACCOUNT_STORE_COLUMN_BUILTIN, builtin,
1184 E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, enabled,
1185 E_MAIL_ACCOUNT_STORE_COLUMN_ONLINE_ACCOUNT, online_account,
1186 E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED_VISIBLE, enabled_visible,
1187 -1);
1188
1189 /* This populates the rest of the columns. */
1190 mail_account_store_update_row (store, service, &iter);
1191
1192 /* No need to connect to "service-added" emissions since it's
1193 * always immediately followed by either "service-enabled" or
1194 * "service-disabled" in MailFolderCache */
1195
1196 g_signal_emit (store, signals[SERVICE_ADDED], 0, service);
1197
1198 if (enabled)
1199 g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
1200 else
1201 g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
1202
1203 filename = store->priv->sort_order_filename;
1204
1205 if (!g_file_test (filename, G_FILE_TEST_EXISTS))
1206 e_mail_account_store_reorder_services (store, NULL);
1207 }
1208
1209 void
1210 e_mail_account_store_remove_service (EMailAccountStore *store,
1211 GtkWindow *parent_window,
1212 CamelService *service)
1213 {
1214 GtkTreeIter iter;
1215 gboolean proceed = TRUE;
1216
1217 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1218 g_return_if_fail (CAMEL_IS_SERVICE (service));
1219
1220 /* XXX Our service_removed() class method calls e_source_remove(),
1221 * which causes the registry service to emit a "source-removed"
1222 * signal. But since other applications may also induce signal
1223 * emissions from the registry service, EMailUISession handles
1224 * "source-removed" by calling this function. So quietly break
1225 * the cycle if we don't find the service in our tree model. */
1226 if (!mail_account_store_get_iter (store, service, &iter))
1227 return;
1228
1229 /* If no parent window was given, skip the request signal. */
1230 if (GTK_IS_WINDOW (parent_window))
1231 g_signal_emit (
1232 store, signals[REMOVE_REQUESTED], 0,
1233 parent_window, service, &proceed);
1234
1235 if (proceed) {
1236 g_object_ref (service);
1237
1238 gtk_list_store_remove (GTK_LIST_STORE (store), &iter);
1239
1240 mail_account_store_clean_index (store);
1241
1242 g_signal_emit (store, signals[SERVICE_REMOVED], 0, service);
1243
1244 g_object_unref (service);
1245 }
1246 }
1247
1248 void
1249 e_mail_account_store_enable_service (EMailAccountStore *store,
1250 GtkWindow *parent_window,
1251 CamelService *service)
1252 {
1253 GtkTreeIter iter;
1254 gboolean proceed;
1255
1256 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1257 g_return_if_fail (CAMEL_IS_SERVICE (service));
1258
1259 if (!mail_account_store_get_iter (store, service, &iter))
1260 g_return_if_reached ();
1261
1262 /* If no parent window was given, skip the request signal. */
1263 if (GTK_IS_WINDOW (parent_window))
1264 g_signal_emit (
1265 store, signals[ENABLE_REQUESTED], 0,
1266 parent_window, service, &proceed);
1267
1268 if (proceed) {
1269 gtk_list_store_set (
1270 GTK_LIST_STORE (store), &iter,
1271 E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, TRUE, -1);
1272 g_signal_emit (store, signals[SERVICE_ENABLED], 0, service);
1273 }
1274 }
1275
1276 void
1277 e_mail_account_store_disable_service (EMailAccountStore *store,
1278 GtkWindow *parent_window,
1279 CamelService *service)
1280 {
1281 GtkTreeIter iter;
1282 gboolean proceed;
1283
1284 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1285 g_return_if_fail (CAMEL_IS_SERVICE (service));
1286
1287 if (!mail_account_store_get_iter (store, service, &iter))
1288 g_return_if_reached ();
1289
1290 /* If no parent window was given, skip the request signal. */
1291 if (GTK_IS_WINDOW (parent_window))
1292 g_signal_emit (
1293 store, signals[DISABLE_REQUESTED], 0,
1294 parent_window, service, &proceed);
1295
1296 if (proceed) {
1297 gtk_list_store_set (
1298 GTK_LIST_STORE (store), &iter,
1299 E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED, FALSE, -1);
1300 g_signal_emit (store, signals[SERVICE_DISABLED], 0, service);
1301 }
1302 }
1303
1304 void
1305 e_mail_account_store_queue_services (EMailAccountStore *store,
1306 GQueue *out_queue)
1307 {
1308 GtkTreeModel *tree_model;
1309 GtkTreeIter iter;
1310 gboolean iter_set;
1311 gint column;
1312
1313 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1314 g_return_if_fail (out_queue != NULL);
1315
1316 tree_model = GTK_TREE_MODEL (store);
1317
1318 iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1319
1320 while (iter_set) {
1321 GValue value = G_VALUE_INIT;
1322
1323 column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1324 gtk_tree_model_get_value (tree_model, &iter, column, &value);
1325 g_queue_push_tail (out_queue, g_value_get_object (&value));
1326 g_value_unset (&value);
1327
1328 iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1329 }
1330 }
1331
1332 void
1333 e_mail_account_store_queue_enabled_services (EMailAccountStore *store,
1334 GQueue *out_queue)
1335 {
1336 GtkTreeModel *tree_model;
1337 GtkTreeIter iter;
1338 gboolean iter_set;
1339 gint column;
1340
1341 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1342 g_return_if_fail (out_queue != NULL);
1343
1344 tree_model = GTK_TREE_MODEL (store);
1345
1346 iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1347
1348 while (iter_set) {
1349 GValue value = G_VALUE_INIT;
1350 gboolean enabled;
1351
1352 column = E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED;
1353 gtk_tree_model_get_value (tree_model, &iter, column, &value);
1354 enabled = g_value_get_boolean (&value);
1355 g_value_unset (&value);
1356
1357 if (enabled) {
1358 column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1359 gtk_tree_model_get_value (tree_model, &iter, column, &value);
1360 g_queue_push_tail (out_queue, g_value_get_object (&value));
1361 g_value_unset (&value);
1362 }
1363
1364 iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1365 }
1366 }
1367
1368 gboolean
1369 e_mail_account_store_have_enabled_service (EMailAccountStore *store,
1370 GType service_type)
1371 {
1372 GtkTreeModel *tree_model;
1373 GtkTreeIter iter;
1374 gboolean iter_set;
1375 gint column;
1376 gboolean found = FALSE;
1377
1378 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1379
1380 tree_model = GTK_TREE_MODEL (store);
1381
1382 iter_set = gtk_tree_model_get_iter_first (tree_model, &iter);
1383
1384 while (iter_set && !found) {
1385 GValue value = G_VALUE_INIT;
1386 gboolean enabled;
1387
1388 column = E_MAIL_ACCOUNT_STORE_COLUMN_ENABLED;
1389 gtk_tree_model_get_value (tree_model, &iter, column, &value);
1390 enabled = g_value_get_boolean (&value);
1391 g_value_unset (&value);
1392
1393 if (enabled) {
1394 CamelService *service;
1395
1396 column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1397 gtk_tree_model_get_value (tree_model, &iter, column, &value);
1398 service = g_value_get_object (&value);
1399 found = service && G_TYPE_CHECK_INSTANCE_TYPE (service, service_type);
1400 g_value_unset (&value);
1401 }
1402
1403 iter_set = gtk_tree_model_iter_next (tree_model, &iter);
1404 }
1405
1406 return found;
1407 }
1408
1409 void
1410 e_mail_account_store_reorder_services (EMailAccountStore *store,
1411 GQueue *ordered_services)
1412 {
1413 GQueue *current_order = NULL;
1414 GQueue *default_order = NULL;
1415 GtkTreeModel *tree_model;
1416 gboolean use_default_order;
1417 GList *head, *link;
1418 gint *new_order;
1419 gint n_children;
1420 gint new_pos = 0;
1421
1422 g_return_if_fail (E_IS_MAIL_ACCOUNT_STORE (store));
1423
1424 tree_model = GTK_TREE_MODEL (store);
1425 n_children = gtk_tree_model_iter_n_children (tree_model, NULL);
1426
1427 /* Treat NULL queues and empty queues the same. */
1428 if (ordered_services != NULL && g_queue_is_empty (ordered_services))
1429 ordered_services = NULL;
1430
1431 /* If the length of the custom ordering disagrees with the
1432 * number of rows in the store, revert to default ordering. */
1433 if (ordered_services != NULL) {
1434 if (g_queue_get_length (ordered_services) != n_children)
1435 ordered_services = NULL;
1436 }
1437
1438 use_default_order = (ordered_services == NULL);
1439
1440 /* Build a queue of CamelServices in the order they appear in
1441 * the list store. We'll use this to construct the mapping to
1442 * pass to gtk_list_store_reorder(). */
1443 current_order = g_queue_new ();
1444 e_mail_account_store_queue_services (store, current_order);
1445
1446 /* If a custom ordering was not given, revert to default. */
1447 if (use_default_order) {
1448 default_order = g_queue_copy (current_order);
1449
1450 g_queue_sort (
1451 default_order, (GCompareDataFunc)
1452 mail_account_store_default_compare, store);
1453
1454 ordered_services = default_order;
1455 }
1456
1457 new_order = g_new0 (gint, n_children);
1458 head = g_queue_peek_head_link (ordered_services);
1459
1460 for (link = head; link != NULL; link = g_list_next (link)) {
1461 GList *matching_link;
1462 gint old_pos;
1463
1464 matching_link = g_queue_find (current_order, link->data);
1465
1466 if (matching_link == NULL || matching_link->data == NULL)
1467 break;
1468
1469 old_pos = g_queue_link_index (current_order, matching_link);
1470
1471 matching_link->data = NULL;
1472 new_order[new_pos++] = old_pos;
1473 }
1474
1475 if (new_pos == n_children) {
1476 gtk_list_store_reorder (GTK_LIST_STORE (store), new_order);
1477 g_signal_emit (
1478 store, signals[SERVICES_REORDERED], 0,
1479 use_default_order);
1480 }
1481
1482 g_free (new_order);
1483
1484 if (current_order != NULL)
1485 g_queue_free (current_order);
1486
1487 if (default_order != NULL)
1488 g_queue_free (default_order);
1489 }
1490
1491 gint
1492 e_mail_account_store_compare_services (EMailAccountStore *store,
1493 CamelService *service_a,
1494 CamelService *service_b)
1495 {
1496 GtkTreeModel *model;
1497 GtkTreePath *path_a;
1498 GtkTreePath *path_b;
1499 GtkTreeIter iter_a;
1500 GtkTreeIter iter_b;
1501 gboolean iter_a_set;
1502 gboolean iter_b_set;
1503 gint result;
1504
1505 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), -1);
1506 g_return_val_if_fail (CAMEL_IS_SERVICE (service_a), -1);
1507 g_return_val_if_fail (CAMEL_IS_SERVICE (service_b), -1);
1508
1509 /* XXX This is horribly inefficient but should be
1510 * over a small enough set to not be noticable. */
1511
1512 iter_a_set = mail_account_store_get_iter (store, service_a, &iter_a);
1513 iter_b_set = mail_account_store_get_iter (store, service_b, &iter_b);
1514
1515 if (!iter_a_set && !iter_b_set)
1516 return 0;
1517
1518 if (!iter_a_set)
1519 return -1;
1520
1521 if (!iter_b_set)
1522 return 1;
1523
1524 model = GTK_TREE_MODEL (store);
1525
1526 path_a = gtk_tree_model_get_path (model, &iter_a);
1527 path_b = gtk_tree_model_get_path (model, &iter_b);
1528
1529 result = gtk_tree_path_compare (path_a, path_b);
1530
1531 gtk_tree_path_free (path_a);
1532 gtk_tree_path_free (path_b);
1533
1534 return result;
1535 }
1536
1537 gboolean
1538 e_mail_account_store_load_sort_order (EMailAccountStore *store,
1539 GError **error)
1540 {
1541 GQueue service_queue = G_QUEUE_INIT;
1542 EMailSession *session;
1543 GKeyFile *key_file;
1544 const gchar *filename;
1545 gchar **service_uids;
1546 gboolean success = TRUE;
1547 gsize ii, length;
1548
1549 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1550
1551 session = e_mail_account_store_get_session (store);
1552
1553 key_file = g_key_file_new ();
1554 filename = store->priv->sort_order_filename;
1555
1556 if (g_file_test (filename, G_FILE_TEST_EXISTS))
1557 success = g_key_file_load_from_file (
1558 key_file, filename, G_KEY_FILE_NONE, error);
1559
1560 if (!success) {
1561 g_key_file_free (key_file);
1562 return FALSE;
1563 }
1564
1565 /* If the key is not present, length is set to zero. */
1566 service_uids = g_key_file_get_string_list (
1567 key_file, "Accounts", "SortOrder", &length, NULL);
1568
1569 for (ii = 0; ii < length; ii++) {
1570 CamelService *service;
1571
1572 service = camel_session_ref_service (
1573 CAMEL_SESSION (session), service_uids[ii]);
1574 if (service != NULL)
1575 g_queue_push_tail (&service_queue, service);
1576 }
1577
1578 e_mail_account_store_reorder_services (store, &service_queue);
1579
1580 while (!g_queue_is_empty (&service_queue))
1581 g_object_unref (g_queue_pop_head (&service_queue));
1582
1583 g_strfreev (service_uids);
1584
1585 g_key_file_free (key_file);
1586
1587 return TRUE;
1588 }
1589
1590 gboolean
1591 e_mail_account_store_save_sort_order (EMailAccountStore *store,
1592 GError **error)
1593 {
1594 GKeyFile *key_file;
1595 GtkTreeModel *model;
1596 GtkTreeIter iter;
1597 const gchar **service_uids;
1598 const gchar *filename;
1599 gchar *contents;
1600 gboolean iter_set;
1601 gboolean success;
1602 gsize length;
1603 gsize ii = 0;
1604
1605 g_return_val_if_fail (E_IS_MAIL_ACCOUNT_STORE (store), FALSE);
1606
1607 model = GTK_TREE_MODEL (store);
1608 length = gtk_tree_model_iter_n_children (model, NULL);
1609
1610 /* Empty store, nothing to save. */
1611 if (length == 0)
1612 return TRUE;
1613
1614 service_uids = g_new0 (const gchar *, length);
1615
1616 iter_set = gtk_tree_model_get_iter_first (model, &iter);
1617
1618 while (iter_set) {
1619 GValue value = G_VALUE_INIT;
1620 const gint column = E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE;
1621 CamelService *service;
1622
1623 gtk_tree_model_get_value (model, &iter, column, &value);
1624 service = g_value_get_object (&value);
1625 service_uids[ii++] = camel_service_get_uid (service);
1626 g_value_unset (&value);
1627
1628 iter_set = gtk_tree_model_iter_next (model, &iter);
1629 }
1630
1631 key_file = g_key_file_new ();
1632 filename = store->priv->sort_order_filename;
1633
1634 g_key_file_set_string_list (
1635 key_file, "Accounts", "SortOrder", service_uids, length);
1636
1637 contents = g_key_file_to_data (key_file, &length, NULL);
1638 success = g_file_set_contents (filename, contents, length, error);
1639 g_free (contents);
1640
1641 g_key_file_free (key_file);
1642
1643 g_free (service_uids);
1644
1645 return success;
1646 }