1 /*
2 * em-subscription-editor.c
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11 * for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 #include "evolution-config.h"
19
20 #include <string.h>
21 #include <glib/gi18n-lib.h>
22
23 #include <e-util/e-util.h>
24
25 #include "em-folder-utils.h"
26 #include "em-subscription-editor.h"
27
28 #define EM_SUBSCRIPTION_EDITOR_GET_PRIVATE(obj) \
29 (G_TYPE_INSTANCE_GET_PRIVATE \
30 ((obj), EM_TYPE_SUBSCRIPTION_EDITOR, EMSubscriptionEditorPrivate))
31
32 #define FOLDER_CAN_SELECT(folder_info) \
33 ((folder_info) != NULL && \
34 ((folder_info)->flags & CAMEL_FOLDER_NOSELECT) == 0)
35 #define FOLDER_SUBSCRIBED(folder_info) \
36 ((folder_info) != NULL && \
37 ((folder_info)->flags & CAMEL_FOLDER_SUBSCRIBED) != 0)
38
39 typedef struct _AsyncContext AsyncContext;
40 typedef struct _TreeRowData TreeRowData;
41 typedef struct _StoreData StoreData;
42
43 struct _EMSubscriptionEditorPrivate {
44 EMailSession *session;
45 CamelStore *initial_store;
46
47 GtkWidget *combo_box; /* not referenced */
48 GtkWidget *entry; /* not referenced */
49 GtkWidget *notebook; /* not referenced */
50 GtkWidget *subscribe_button; /* not referenced */
51 GtkWidget *subscribe_arrow; /* not referenced */
52 GtkWidget *unsubscribe_button; /* not referenced */
53 GtkWidget *unsubscribe_arrow; /* not referenced */
54 GtkWidget *collapse_all_button; /* not referenced */
55 GtkWidget *expand_all_button; /* not referenced */
56 GtkWidget *refresh_button; /* not referenced */
57 GtkWidget *stop_button; /* not referenced */
58
59 /* Indicies coincide with the combo box. */
60 GPtrArray *stores;
61
62 /* Points at an item in the stores array. */
63 StoreData *active;
64
65 /* Casefolded search string. */
66 gchar *search_string;
67
68 guint timeout_id;
69 };
70
71 struct _TreeRowData {
72 CamelFolderInfo *folder_info;
73 GtkTreeRowReference *reference;
74 };
75
76 struct _AsyncContext {
77 EMSubscriptionEditor *editor;
78 GQueue *tree_rows;
79 };
80
81 struct _StoreData {
82 CamelStore *store;
83 GtkTreeView *tree_view;
84 GtkTreeModel *list_store;
85 GtkTreeModel *tree_store;
86 GCancellable *cancellable;
87 CamelFolderInfo *folder_info;
88 gboolean filtered_view;
89 gboolean needs_refresh;
90 };
91
92 enum {
93 PROP_0,
94 PROP_SESSION,
95 PROP_STORE
96 };
97
98 enum {
99 COL_CASEFOLDED, /* G_TYPE_STRING */
100 COL_FOLDER_ICON, /* G_TYPE_STRING */
101 COL_FOLDER_NAME, /* G_TYPE_STRING */
102 COL_FOLDER_INFO, /* G_TYPE_POINTER */
103 N_COLUMNS
104 };
105
G_DEFINE_TYPE(EMSubscriptionEditor,em_subscription_editor,GTK_TYPE_DIALOG)106 G_DEFINE_TYPE (EMSubscriptionEditor, em_subscription_editor, GTK_TYPE_DIALOG)
107
108 static void
109 tree_row_data_free (TreeRowData *tree_row_data)
110 {
111 g_return_if_fail (tree_row_data != NULL);
112
113 gtk_tree_row_reference_free (tree_row_data->reference);
114 g_slice_free (TreeRowData, tree_row_data);
115 }
116
117 static AsyncContext *
async_context_new(EMSubscriptionEditor * editor,GQueue * tree_rows)118 async_context_new (EMSubscriptionEditor *editor,
119 GQueue *tree_rows)
120 {
121 AsyncContext *context;
122
123 context = g_slice_new0 (AsyncContext);
124 context->editor = g_object_ref (editor);
125
126 /* Transfer GQueue contents. */
127 context->tree_rows = g_queue_copy (tree_rows);
128 g_queue_clear (tree_rows);
129
130 return context;
131 }
132
133 static void
async_context_free(AsyncContext * context)134 async_context_free (AsyncContext *context)
135 {
136 while (!g_queue_is_empty (context->tree_rows))
137 tree_row_data_free (g_queue_pop_head (context->tree_rows));
138
139 g_object_unref (context->editor);
140 g_queue_free (context->tree_rows);
141
142 g_slice_free (AsyncContext, context);
143 }
144
145 static void
store_data_free(StoreData * data)146 store_data_free (StoreData *data)
147 {
148 if (data->store != NULL)
149 g_object_unref (data->store);
150
151 if (data->tree_view != NULL)
152 g_object_unref (data->tree_view);
153
154 if (data->list_store != NULL)
155 g_object_unref (data->list_store);
156
157 if (data->tree_store != NULL)
158 g_object_unref (data->tree_store);
159
160 if (data->cancellable != NULL) {
161 g_cancellable_cancel (data->cancellable);
162 g_object_unref (data->cancellable);
163 }
164
165 camel_folder_info_free (data->folder_info);
166
167 g_slice_free (StoreData, data);
168 }
169
170 static void
subscription_editor_populate(EMSubscriptionEditor * editor,CamelFolderInfo * folder_info,GtkTreeIter * parent,GList ** expand_paths)171 subscription_editor_populate (EMSubscriptionEditor *editor,
172 CamelFolderInfo *folder_info,
173 GtkTreeIter *parent,
174 GList **expand_paths)
175 {
176 GtkListStore *list_store;
177 GtkTreeStore *tree_store;
178
179 list_store = GTK_LIST_STORE (editor->priv->active->list_store);
180 tree_store = GTK_TREE_STORE (editor->priv->active->tree_store);
181
182 while (folder_info != NULL) {
183 GtkTreeIter iter;
184 const gchar *icon_name;
185 gchar *casefolded;
186
187 icon_name =
188 em_folder_utils_get_icon_name (folder_info->flags);
189
190 casefolded = g_utf8_casefold (folder_info->full_name, -1);
191
192 gtk_list_store_append (list_store, &iter);
193
194 gtk_list_store_set (
195 list_store, &iter,
196 COL_CASEFOLDED, casefolded,
197 COL_FOLDER_ICON, icon_name,
198 COL_FOLDER_NAME, folder_info->full_name,
199 COL_FOLDER_INFO, folder_info, -1);
200
201 gtk_tree_store_append (tree_store, &iter, parent);
202
203 gtk_tree_store_set (
204 tree_store, &iter,
205 COL_CASEFOLDED, NULL, /* not needed */
206 COL_FOLDER_ICON, icon_name,
207 COL_FOLDER_NAME, folder_info->display_name,
208 COL_FOLDER_INFO, folder_info, -1);
209
210 if (FOLDER_SUBSCRIBED (folder_info)) {
211 GtkTreePath *path;
212
213 path = gtk_tree_model_get_path (
214 GTK_TREE_MODEL (tree_store), &iter);
215 *expand_paths = g_list_prepend (*expand_paths, path);
216 }
217
218 g_free (casefolded);
219
220 if (folder_info->child != NULL)
221 subscription_editor_populate (
222 editor, folder_info->child,
223 &iter, expand_paths);
224
225 folder_info = folder_info->next;
226 }
227 }
228
229 static void
expand_paths_cb(gpointer path,gpointer tree_view)230 expand_paths_cb (gpointer path,
231 gpointer tree_view)
232 {
233 gtk_tree_view_expand_to_path (tree_view, path);
234 }
235
236 static void
subscription_editor_get_folder_info_done(CamelStore * store,GAsyncResult * result,EMSubscriptionEditor * editor)237 subscription_editor_get_folder_info_done (CamelStore *store,
238 GAsyncResult *result,
239 EMSubscriptionEditor *editor)
240 {
241 GtkTreePath *path;
242 GtkTreeView *tree_view;
243 GtkTreeModel *list_store;
244 GtkTreeModel *tree_store;
245 GtkTreeModel *model;
246 GtkTreeSelection *selection;
247 CamelFolderInfo *folder_info;
248 GdkWindow *window;
249 GList *expand_paths = NULL;
250 GError *error = NULL;
251
252 folder_info = camel_store_get_folder_info_finish (
253 store, result, &error);
254
255 /* Just return quietly if we were cancelled. */
256 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
257 g_warn_if_fail (folder_info == NULL);
258 g_error_free (error);
259 goto exit;
260 }
261
262 gtk_widget_set_sensitive (editor->priv->notebook, TRUE);
263 gtk_widget_set_sensitive (editor->priv->refresh_button, TRUE);
264 gtk_widget_set_sensitive (editor->priv->stop_button, FALSE);
265
266 window = gtk_widget_get_window (GTK_WIDGET (editor));
267 gdk_window_set_cursor (window, NULL);
268
269 /* XXX Do something smarter with errors. */
270 if (error != NULL) {
271 g_warn_if_fail (folder_info == NULL);
272 e_notice (GTK_WINDOW (editor), GTK_MESSAGE_ERROR, "%s", error->message);
273 g_error_free (error);
274 goto exit;
275 }
276
277 g_return_if_fail (folder_info != NULL);
278
279 camel_folder_info_free (editor->priv->active->folder_info);
280 editor->priv->active->folder_info = folder_info;
281
282 tree_view = editor->priv->active->tree_view;
283 list_store = editor->priv->active->list_store;
284 tree_store = editor->priv->active->tree_store;
285
286 gtk_list_store_clear (GTK_LIST_STORE (list_store));
287 gtk_tree_store_clear (GTK_TREE_STORE (tree_store));
288
289 model = gtk_tree_view_get_model (tree_view);
290 gtk_tree_view_set_model (tree_view, NULL);
291 subscription_editor_populate (editor, folder_info, NULL, &expand_paths);
292 gtk_tree_view_set_model (tree_view, model);
293 gtk_tree_view_set_search_column (tree_view, COL_FOLDER_NAME);
294
295 g_list_foreach (expand_paths, expand_paths_cb, tree_view);
296 g_list_foreach (expand_paths, (GFunc) gtk_tree_path_free, NULL);
297 g_list_free (expand_paths);
298
299 path = gtk_tree_path_new_first ();
300 selection = gtk_tree_view_get_selection (tree_view);
301 gtk_tree_selection_select_path (selection, path);
302 gtk_tree_path_free (path);
303
304 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
305
306 exit:
307 g_object_unref (editor);
308 }
309
310 static void
subscription_editor_subscribe_folder_done(CamelSubscribable * subscribable,GAsyncResult * result,AsyncContext * context)311 subscription_editor_subscribe_folder_done (CamelSubscribable *subscribable,
312 GAsyncResult *result,
313 AsyncContext *context)
314 {
315 GtkTreeView *tree_view;
316 GtkTreeModel *tree_model;
317 GtkTreeSelection *selection;
318 GtkTreePath *path;
319 GtkTreeIter iter;
320 GdkWindow *window;
321 GError *error = NULL;
322 TreeRowData *tree_row_data;
323
324 camel_subscribable_subscribe_folder_finish (
325 subscribable, result, &error);
326
327 /* Just return quietly if we were cancelled. */
328 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
329 g_error_free (error);
330 goto exit;
331 }
332
333 tree_row_data = g_queue_pop_head (context->tree_rows);
334
335 /* XXX Do something smarter with errors. */
336 if (error == NULL)
337 tree_row_data->folder_info->flags |= CAMEL_FOLDER_SUBSCRIBED;
338 else {
339 e_notice (
340 GTK_WINDOW (context->editor),
341 GTK_MESSAGE_ERROR, "%s", error->message);
342 g_error_free (error);
343 tree_row_data_free (tree_row_data);
344 goto exit;
345 }
346
347 /* Update the toggle renderer in the selected row. */
348 tree_model = gtk_tree_row_reference_get_model (tree_row_data->reference);
349 path = gtk_tree_row_reference_get_path (tree_row_data->reference);
350 gtk_tree_model_get_iter (tree_model, &iter, path);
351 gtk_tree_model_row_changed (tree_model, path, &iter);
352 gtk_tree_path_free (path);
353
354 tree_row_data_free (tree_row_data);
355
356 if (!g_queue_is_empty (context->tree_rows)) {
357 GCancellable *cancellable;
358
359 /* continue with the next to subscribe */
360 tree_row_data = g_queue_peek_head (context->tree_rows);
361 g_return_if_fail (tree_row_data != NULL);
362
363 cancellable = context->editor->priv->active->cancellable;
364
365 camel_subscribable_subscribe_folder (
366 subscribable, tree_row_data->folder_info->full_name,
367 G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)
368 subscription_editor_subscribe_folder_done, context);
369 return;
370 }
371
372 exit:
373 gtk_widget_set_sensitive (context->editor->priv->notebook, TRUE);
374 gtk_widget_set_sensitive (context->editor->priv->refresh_button, TRUE);
375 gtk_widget_set_sensitive (context->editor->priv->stop_button, FALSE);
376
377 window = gtk_widget_get_window (GTK_WIDGET (context->editor));
378 gdk_window_set_cursor (window, NULL);
379
380 /* Update the Subscription/Unsubscription buttons. */
381 tree_view = context->editor->priv->active->tree_view;
382 selection = gtk_tree_view_get_selection (tree_view);
383 g_signal_emit_by_name (selection, "changed");
384
385 async_context_free (context);
386
387 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
388 }
389
390 static void
subscription_editor_subscribe_many(EMSubscriptionEditor * editor,GQueue * tree_rows)391 subscription_editor_subscribe_many (EMSubscriptionEditor *editor,
392 GQueue *tree_rows)
393 {
394 TreeRowData *tree_row_data;
395 GCancellable *cancellable;
396 CamelStore *active_store;
397 AsyncContext *context;
398 GdkCursor *cursor;
399 GdkWindow *window;
400
401 g_return_if_fail (editor != NULL);
402
403 if (g_queue_is_empty (tree_rows))
404 return;
405
406 tree_row_data = g_queue_peek_head (tree_rows);
407 g_return_if_fail (tree_row_data != NULL);
408
409 /* Cancel any operation on this store still in progress. */
410 gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
411
412 /* Start a new 'subscription' operation. */
413 editor->priv->active->cancellable = g_cancellable_new ();
414
415 gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
416 gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
417 gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
418 gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
419 gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
420 gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
421 gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
422
423 cursor = gdk_cursor_new (GDK_WATCH);
424 window = gtk_widget_get_window (GTK_WIDGET (editor));
425 gdk_window_set_cursor (window, cursor);
426 g_object_unref (cursor);
427
428 context = async_context_new (editor, tree_rows);
429
430 active_store = editor->priv->active->store;
431 cancellable = editor->priv->active->cancellable;
432
433 camel_subscribable_subscribe_folder (
434 CAMEL_SUBSCRIBABLE (active_store),
435 tree_row_data->folder_info->full_name,
436 G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)
437 subscription_editor_subscribe_folder_done, context);
438 }
439
440 static void
subscription_editor_unsubscribe_folder_done(CamelSubscribable * subscribable,GAsyncResult * result,AsyncContext * context)441 subscription_editor_unsubscribe_folder_done (CamelSubscribable *subscribable,
442 GAsyncResult *result,
443 AsyncContext *context)
444 {
445 GtkTreeView *tree_view;
446 GtkTreeModel *tree_model;
447 GtkTreeSelection *selection;
448 GtkTreePath *path;
449 GtkTreeIter iter;
450 GdkWindow *window;
451 GError *error = NULL;
452 TreeRowData *tree_row_data;
453
454 camel_subscribable_unsubscribe_folder_finish (
455 subscribable, result, &error);
456
457 /* Just return quietly if we were cancelled. */
458 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
459 g_error_free (error);
460 goto exit;
461 }
462
463 tree_row_data = g_queue_pop_head (context->tree_rows);
464
465 /* XXX Do something smarter with errors. */
466 if (error == NULL)
467 tree_row_data->folder_info->flags &= ~CAMEL_FOLDER_SUBSCRIBED;
468 else {
469 e_notice (
470 GTK_WINDOW (context->editor),
471 GTK_MESSAGE_ERROR, "%s", error->message);
472 g_error_free (error);
473 tree_row_data_free (tree_row_data);
474 goto exit;
475 }
476
477 /* Update the toggle renderer in the selected row. */
478 tree_model = gtk_tree_row_reference_get_model (tree_row_data->reference);
479 path = gtk_tree_row_reference_get_path (tree_row_data->reference);
480 gtk_tree_model_get_iter (tree_model, &iter, path);
481 gtk_tree_model_row_changed (tree_model, path, &iter);
482 gtk_tree_path_free (path);
483
484 tree_row_data_free (tree_row_data);
485
486 if (!g_queue_is_empty (context->tree_rows)) {
487 GCancellable *cancellable;
488
489 /* continue with the next to unsubscribe */
490 tree_row_data = g_queue_peek_head (context->tree_rows);
491 g_return_if_fail (tree_row_data != NULL);
492
493 cancellable = context->editor->priv->active->cancellable;
494
495 camel_subscribable_unsubscribe_folder (
496 subscribable, tree_row_data->folder_info->full_name,
497 G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback)
498 subscription_editor_unsubscribe_folder_done, context);
499 return;
500 }
501
502 exit:
503 gtk_widget_set_sensitive (context->editor->priv->notebook, TRUE);
504 gtk_widget_set_sensitive (context->editor->priv->refresh_button, TRUE);
505 gtk_widget_set_sensitive (context->editor->priv->stop_button, FALSE);
506
507 window = gtk_widget_get_window (GTK_WIDGET (context->editor));
508 gdk_window_set_cursor (window, NULL);
509
510 /* Update the Subscription/Unsubscription buttons. */
511 tree_view = context->editor->priv->active->tree_view;
512 selection = gtk_tree_view_get_selection (tree_view);
513 g_signal_emit_by_name (selection, "changed");
514
515 async_context_free (context);
516
517 gtk_widget_grab_focus (GTK_WIDGET (tree_view));
518 }
519
520 static void
subscription_editor_unsubscribe_many(EMSubscriptionEditor * editor,GQueue * tree_rows)521 subscription_editor_unsubscribe_many (EMSubscriptionEditor *editor,
522 GQueue *tree_rows)
523 {
524 TreeRowData *tree_row_data;
525 AsyncContext *context;
526 CamelStore *active_store;
527 GdkCursor *cursor;
528 GdkWindow *window;
529
530 g_return_if_fail (editor != NULL);
531
532 if (g_queue_is_empty (tree_rows))
533 return;
534
535 tree_row_data = g_queue_peek_head (tree_rows);
536 g_return_if_fail (tree_row_data != NULL);
537
538 /* Cancel any operation on this store still in progress. */
539 gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
540
541 /* Start a new 'subscription' operation. */
542 editor->priv->active->cancellable = g_cancellable_new ();
543
544 gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
545 gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
546 gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
547 gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
548 gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
549 gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
550 gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
551
552 cursor = gdk_cursor_new (GDK_WATCH);
553 window = gtk_widget_get_window (GTK_WIDGET (editor));
554 gdk_window_set_cursor (window, cursor);
555 g_object_unref (cursor);
556
557 context = async_context_new (editor, tree_rows);
558
559 active_store = editor->priv->active->store;
560
561 camel_subscribable_unsubscribe_folder (
562 CAMEL_SUBSCRIBABLE (active_store),
563 tree_row_data->folder_info->full_name, G_PRIORITY_DEFAULT,
564 editor->priv->active->cancellable, (GAsyncReadyCallback)
565 subscription_editor_unsubscribe_folder_done, context);
566 }
567
568 static GtkWidget *
subscription_editor_create_menu_item(const gchar * label,gboolean sensitive,GCallback activate_cb,EMSubscriptionEditor * editor)569 subscription_editor_create_menu_item (const gchar *label,
570 gboolean sensitive,
571 GCallback activate_cb,
572 EMSubscriptionEditor *editor)
573 {
574 GtkWidget *item;
575
576 item = gtk_menu_item_new_with_mnemonic (label);
577 gtk_widget_set_sensitive (item, sensitive);
578
579 gtk_widget_show (item);
580
581 g_signal_connect_swapped (
582 item, "activate", activate_cb, editor);
583
584 return item;
585 }
586
587 static TreeRowData *
subscription_editor_tree_row_data_from_iter(GtkTreeView * tree_view,GtkTreeModel * model,GtkTreeIter * iter,gboolean * is_expanded)588 subscription_editor_tree_row_data_from_iter (GtkTreeView *tree_view,
589 GtkTreeModel *model,
590 GtkTreeIter *iter,
591 gboolean *is_expanded)
592 {
593 TreeRowData *tree_row_data;
594 CamelFolderInfo *folder_info = NULL;
595 GtkTreeRowReference *reference;
596 GtkTreePath *path;
597
598 gtk_tree_model_get (
599 model, iter, COL_FOLDER_INFO, &folder_info, -1);
600
601 if (!FOLDER_CAN_SELECT (folder_info))
602 return NULL;
603
604 path = gtk_tree_model_get_path (model, iter);
605 reference = gtk_tree_row_reference_new (model, path);
606 if (is_expanded)
607 *is_expanded = gtk_tree_view_row_expanded (tree_view, path);
608 gtk_tree_path_free (path);
609
610 tree_row_data = g_slice_new0 (TreeRowData);
611 tree_row_data->folder_info = folder_info;
612 tree_row_data->reference = reference;
613
614 return tree_row_data;
615 }
616
617 typedef enum {
618 PICK_ALL,
619 PICK_SUBSCRIBED,
620 PICK_UNSUBSCRIBED
621 } EPickMode;
622
623 static gboolean
can_pick_folder_info(CamelFolderInfo * fi,EPickMode mode)624 can_pick_folder_info (CamelFolderInfo *fi,
625 EPickMode mode)
626 {
627 if (!FOLDER_CAN_SELECT (fi))
628 return FALSE;
629
630 if (mode == PICK_ALL)
631 return TRUE;
632
633 return (FOLDER_SUBSCRIBED (fi) ? 1 : 0) == (mode == PICK_SUBSCRIBED ? 1 : 0);
634 }
635
636 struct PickAllData {
637 GtkTreeView *tree_view;
638 EPickMode mode;
639 GHashTable *skip_folder_infos;
640 GQueue *out_tree_rows;
641 };
642
643 static gboolean
pick_all_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)644 pick_all_cb (GtkTreeModel *model,
645 GtkTreePath *path,
646 GtkTreeIter *iter,
647 gpointer user_data)
648 {
649 struct PickAllData *data = user_data;
650 TreeRowData *tree_row_data;
651
652 tree_row_data = subscription_editor_tree_row_data_from_iter (
653 data->tree_view, model, iter, NULL);
654 if (tree_row_data == NULL)
655 return FALSE;
656
657 if (can_pick_folder_info (tree_row_data->folder_info, data->mode) &&
658 (data->skip_folder_infos == NULL ||
659 !g_hash_table_contains (
660 data->skip_folder_infos,
661 tree_row_data->folder_info))) {
662 g_queue_push_tail (data->out_tree_rows, tree_row_data);
663 } else
664 tree_row_data_free (tree_row_data);
665
666 return FALSE;
667 }
668
669 /* skip_folder_infos contains CamelFolderInfo-s to skip;
670 * these should come from the tree view; can be NULL
671 * to include everything.
672 */
673 static void
subscription_editor_pick_all(EMSubscriptionEditor * editor,EPickMode mode,GHashTable * skip_folder_infos,GQueue * out_tree_rows)674 subscription_editor_pick_all (EMSubscriptionEditor *editor,
675 EPickMode mode,
676 GHashTable *skip_folder_infos,
677 GQueue *out_tree_rows)
678 {
679 GtkTreeView *tree_view;
680 GtkTreeModel *tree_model;
681 struct PickAllData data;
682
683 tree_view = editor->priv->active->tree_view;
684 tree_model = gtk_tree_view_get_model (tree_view);
685
686 data.tree_view = tree_view;
687 data.mode = mode;
688 data.skip_folder_infos = skip_folder_infos;
689 data.out_tree_rows = out_tree_rows;
690
691 gtk_tree_model_foreach (tree_model, pick_all_cb, &data);
692 }
693
694 static void
subscription_editor_pick_shown(EMSubscriptionEditor * editor,EPickMode mode,GQueue * out_tree_rows)695 subscription_editor_pick_shown (EMSubscriptionEditor *editor,
696 EPickMode mode,
697 GQueue *out_tree_rows)
698 {
699 GtkTreeView *tree_view;
700 GtkTreeModel *tree_model;
701 GtkTreeIter iter, iter2;
702 gboolean found = TRUE;
703
704 tree_view = editor->priv->active->tree_view;
705 tree_model = gtk_tree_view_get_model (tree_view);
706
707 if (!gtk_tree_model_get_iter_first (tree_model, &iter))
708 return;
709
710 while (found) {
711 TreeRowData *tree_row_data;
712 gboolean is_expanded = FALSE;
713
714 tree_row_data = subscription_editor_tree_row_data_from_iter (
715 tree_view, tree_model, &iter, &is_expanded);
716
717 if (tree_row_data != NULL) {
718 if (can_pick_folder_info (tree_row_data->folder_info, mode))
719 g_queue_push_tail (out_tree_rows, tree_row_data);
720 else
721 tree_row_data_free (tree_row_data);
722 }
723
724 if (is_expanded && gtk_tree_model_iter_children (
725 tree_model, &iter2, &iter)) {
726 iter = iter2;
727 found = TRUE;
728 } else {
729 iter2 = iter;
730 if (gtk_tree_model_iter_next (tree_model, &iter2)) {
731 iter = iter2;
732 found = TRUE;
733 } else {
734 while (found = gtk_tree_model_iter_parent (
735 tree_model, &iter2, &iter), found) {
736 iter = iter2;
737 if (gtk_tree_model_iter_next (
738 tree_model, &iter2)) {
739 iter = iter2;
740 break;
741 }
742 }
743 }
744 }
745 }
746 }
747
748 static void
subscription_editor_subscribe(EMSubscriptionEditor * editor)749 subscription_editor_subscribe (EMSubscriptionEditor *editor)
750 {
751 GtkTreeSelection *selection;
752 GtkTreeModel *tree_model;
753 GtkTreeView *tree_view;
754 GtkTreeIter iter;
755 gboolean have_selection;
756 GQueue tree_rows = G_QUEUE_INIT;
757 TreeRowData *tree_row_data;
758
759 tree_view = editor->priv->active->tree_view;
760 selection = gtk_tree_view_get_selection (tree_view);
761
762 have_selection = gtk_tree_selection_get_selected (
763 selection, &tree_model, &iter);
764 g_return_if_fail (have_selection);
765
766 tree_row_data = subscription_editor_tree_row_data_from_iter (
767 tree_view, tree_model, &iter, NULL);
768
769 g_queue_push_tail (&tree_rows, tree_row_data);
770 subscription_editor_subscribe_many (editor, &tree_rows);
771 g_warn_if_fail (g_queue_is_empty (&tree_rows));
772 }
773
774 static void
subscription_editor_subscribe_shown(EMSubscriptionEditor * editor)775 subscription_editor_subscribe_shown (EMSubscriptionEditor *editor)
776 {
777 GQueue tree_rows = G_QUEUE_INIT;
778
779 subscription_editor_pick_shown (
780 editor, PICK_UNSUBSCRIBED, &tree_rows);
781 subscription_editor_subscribe_many (editor, &tree_rows);
782 }
783
784 static void
subscription_editor_subscribe_all(EMSubscriptionEditor * editor)785 subscription_editor_subscribe_all (EMSubscriptionEditor *editor)
786 {
787 GQueue tree_rows = G_QUEUE_INIT;
788
789 subscription_editor_pick_all (
790 editor, PICK_UNSUBSCRIBED, NULL, &tree_rows);
791 subscription_editor_subscribe_many (editor, &tree_rows);
792 }
793
794 static void
subscription_editor_subscribe_popup_cb(EMSubscriptionEditor * editor)795 subscription_editor_subscribe_popup_cb (EMSubscriptionEditor *editor)
796 {
797 GtkWidget *menu;
798 GtkTreeIter iter;
799 gboolean tree_filled;
800
801 tree_filled = editor->priv->active &&
802 gtk_tree_model_get_iter_first (
803 editor->priv->active->filtered_view
804 ? editor->priv->active->list_store
805 : editor->priv->active->tree_store,
806 &iter);
807
808 menu = gtk_menu_new ();
809
810 gtk_menu_shell_append (
811 GTK_MENU_SHELL (menu),
812 subscription_editor_create_menu_item (
813 _("_Subscribe"),
814 gtk_widget_get_sensitive (
815 editor->priv->subscribe_button),
816 G_CALLBACK (subscription_editor_subscribe),
817 editor));
818
819 gtk_menu_shell_append (
820 GTK_MENU_SHELL (menu),
821 subscription_editor_create_menu_item (
822 _("Su_bscribe To Shown"),
823 tree_filled,
824 G_CALLBACK (subscription_editor_subscribe_shown),
825 editor));
826
827 gtk_menu_shell_append (
828 GTK_MENU_SHELL (menu),
829 subscription_editor_create_menu_item (
830 _("Subscribe To _All"),
831 tree_filled,
832 G_CALLBACK (subscription_editor_subscribe_all),
833 editor));
834
835 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
836 g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
837
838 g_object_set (menu,
839 "anchor-hints", (GDK_ANCHOR_FLIP_Y |
840 GDK_ANCHOR_SLIDE |
841 GDK_ANCHOR_RESIZE),
842 NULL);
843
844 gtk_menu_popup_at_widget (GTK_MENU (menu),
845 editor->priv->subscribe_button,
846 GDK_GRAVITY_SOUTH_WEST,
847 GDK_GRAVITY_NORTH_WEST,
848 NULL);
849 }
850
851 static void
subscription_editor_unsubscribe_hidden(EMSubscriptionEditor * editor)852 subscription_editor_unsubscribe_hidden (EMSubscriptionEditor *editor)
853 {
854 GQueue tree_rows = G_QUEUE_INIT;
855 GHashTable *skip_shown;
856
857 subscription_editor_pick_shown (editor, PICK_ALL, &tree_rows);
858 g_return_if_fail (!g_queue_is_empty (&tree_rows));
859
860 skip_shown = g_hash_table_new (g_direct_hash, g_direct_equal);
861
862 while (!g_queue_is_empty (&tree_rows)) {
863 TreeRowData *tree_row_data;
864
865 tree_row_data = g_queue_pop_head (&tree_rows);
866
867 if (tree_row_data == NULL)
868 continue;
869
870 g_hash_table_add (skip_shown, tree_row_data->folder_info);
871
872 tree_row_data_free (tree_row_data);
873 }
874
875 subscription_editor_pick_all (
876 editor, PICK_SUBSCRIBED, skip_shown, &tree_rows);
877 subscription_editor_unsubscribe_many (editor, &tree_rows);
878
879 g_hash_table_destroy (skip_shown);
880 }
881
882 static void
subscription_editor_unsubscribe_all(EMSubscriptionEditor * editor)883 subscription_editor_unsubscribe_all (EMSubscriptionEditor *editor)
884 {
885 GQueue tree_rows = G_QUEUE_INIT;
886
887 subscription_editor_pick_all (
888 editor, PICK_SUBSCRIBED, NULL, &tree_rows);
889 subscription_editor_unsubscribe_many (editor, &tree_rows);
890 }
891
892 static void
subscription_editor_unsubscribe(EMSubscriptionEditor * editor)893 subscription_editor_unsubscribe (EMSubscriptionEditor *editor)
894 {
895 GtkTreeSelection *selection;
896 GtkTreeModel *tree_model;
897 GtkTreeView *tree_view;
898 GtkTreeIter iter;
899 gboolean have_selection;
900 GQueue tree_rows = G_QUEUE_INIT;
901 TreeRowData *tree_row_data;
902
903 tree_view = editor->priv->active->tree_view;
904 selection = gtk_tree_view_get_selection (tree_view);
905
906 have_selection = gtk_tree_selection_get_selected (
907 selection, &tree_model, &iter);
908 g_return_if_fail (have_selection);
909
910 tree_row_data = subscription_editor_tree_row_data_from_iter (
911 tree_view, tree_model, &iter, NULL);
912
913 g_queue_push_tail (&tree_rows, tree_row_data);
914 subscription_editor_unsubscribe_many (editor, &tree_rows);
915 }
916
917 static void
subscription_editor_unsubscribe_popup_cb(EMSubscriptionEditor * editor)918 subscription_editor_unsubscribe_popup_cb (EMSubscriptionEditor *editor)
919 {
920 GtkWidget *menu;
921 GtkTreeIter iter;
922 gboolean tree_filled;
923
924 tree_filled = editor->priv->active &&
925 gtk_tree_model_get_iter_first (
926 editor->priv->active->filtered_view
927 ? editor->priv->active->list_store
928 : editor->priv->active->tree_store,
929 &iter);
930
931 menu = gtk_menu_new ();
932
933 gtk_menu_shell_append (
934 GTK_MENU_SHELL (menu),
935 subscription_editor_create_menu_item (
936 _("_Unsubscribe"),
937 gtk_widget_get_sensitive (
938 editor->priv->unsubscribe_button),
939 G_CALLBACK (subscription_editor_unsubscribe),
940 editor));
941
942 gtk_menu_shell_append (
943 GTK_MENU_SHELL (menu),
944 subscription_editor_create_menu_item (
945 _("Unsu_bscribe From Hidden"),
946 tree_filled,
947 G_CALLBACK (subscription_editor_unsubscribe_hidden),
948 editor));
949
950 gtk_menu_shell_append (
951 GTK_MENU_SHELL (menu),
952 subscription_editor_create_menu_item (
953 _("Unsubscribe From _All"),
954 tree_filled,
955 G_CALLBACK (subscription_editor_unsubscribe_all),
956 editor));
957
958 gtk_menu_attach_to_widget (GTK_MENU (menu), GTK_WIDGET (editor), NULL);
959 g_signal_connect (menu, "deactivate", G_CALLBACK (gtk_menu_detach), NULL);
960
961 g_object_set (menu,
962 "anchor-hints", (GDK_ANCHOR_FLIP_Y |
963 GDK_ANCHOR_SLIDE |
964 GDK_ANCHOR_RESIZE),
965 NULL);
966
967 gtk_menu_popup_at_widget (GTK_MENU (menu),
968 editor->priv->unsubscribe_button,
969 GDK_GRAVITY_SOUTH_WEST,
970 GDK_GRAVITY_NORTH_WEST,
971 NULL);
972 }
973
974 static void
subscription_editor_collapse_all(EMSubscriptionEditor * editor)975 subscription_editor_collapse_all (EMSubscriptionEditor *editor)
976 {
977 gtk_tree_view_collapse_all (editor->priv->active->tree_view);
978 }
979
980 static void
subscription_editor_expand_all(EMSubscriptionEditor * editor)981 subscription_editor_expand_all (EMSubscriptionEditor *editor)
982 {
983 gtk_tree_view_expand_all (editor->priv->active->tree_view);
984 }
985
986 static void
subscription_editor_refresh(EMSubscriptionEditor * editor)987 subscription_editor_refresh (EMSubscriptionEditor *editor)
988 {
989 GdkCursor *cursor;
990 GdkWindow *window;
991
992 /* Cancel any operation on this store still in progress. */
993 gtk_button_clicked (GTK_BUTTON (editor->priv->stop_button));
994
995 /* Start a new 'refresh' operation. */
996 editor->priv->active->cancellable = g_cancellable_new ();
997
998 gtk_widget_set_sensitive (editor->priv->notebook, FALSE);
999 gtk_widget_set_sensitive (editor->priv->subscribe_button, FALSE);
1000 gtk_widget_set_sensitive (editor->priv->subscribe_arrow, FALSE);
1001 gtk_widget_set_sensitive (editor->priv->unsubscribe_button, FALSE);
1002 gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, FALSE);
1003 gtk_widget_set_sensitive (editor->priv->refresh_button, FALSE);
1004 gtk_widget_set_sensitive (editor->priv->stop_button, TRUE);
1005
1006 cursor = gdk_cursor_new (GDK_WATCH);
1007 window = gtk_widget_get_window (GTK_WIDGET (editor));
1008 gdk_window_set_cursor (window, cursor);
1009 g_object_unref (cursor);
1010
1011 camel_store_get_folder_info (
1012 editor->priv->active->store, NULL,
1013 CAMEL_STORE_FOLDER_INFO_RECURSIVE |
1014 CAMEL_STORE_FOLDER_INFO_NO_VIRTUAL |
1015 CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST,
1016 G_PRIORITY_DEFAULT, editor->priv->active->cancellable,
1017 (GAsyncReadyCallback) subscription_editor_get_folder_info_done,
1018 g_object_ref (editor));
1019 }
1020
1021 static void
subscription_editor_stop(EMSubscriptionEditor * editor)1022 subscription_editor_stop (EMSubscriptionEditor *editor)
1023 {
1024 GdkWindow *window;
1025
1026 if (editor->priv->active->cancellable != NULL) {
1027 g_cancellable_cancel (editor->priv->active->cancellable);
1028 g_object_unref (editor->priv->active->cancellable);
1029 editor->priv->active->cancellable = NULL;
1030 }
1031
1032 gtk_widget_set_sensitive (editor->priv->notebook, TRUE);
1033 gtk_widget_set_sensitive (editor->priv->subscribe_button, TRUE);
1034 gtk_widget_set_sensitive (editor->priv->subscribe_arrow, TRUE);
1035 gtk_widget_set_sensitive (editor->priv->unsubscribe_button, TRUE);
1036 gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, TRUE);
1037 gtk_widget_set_sensitive (editor->priv->refresh_button, TRUE);
1038 gtk_widget_set_sensitive (editor->priv->stop_button, FALSE);
1039 gtk_widget_grab_focus (GTK_WIDGET (editor->priv->active->tree_view));
1040
1041 window = gtk_widget_get_window (GTK_WIDGET (editor));
1042 gdk_window_set_cursor (window, NULL);
1043 }
1044
1045 static gboolean
subscription_editor_filter_cb(GtkTreeModel * tree_model,GtkTreeIter * iter,EMSubscriptionEditor * editor)1046 subscription_editor_filter_cb (GtkTreeModel *tree_model,
1047 GtkTreeIter *iter,
1048 EMSubscriptionEditor *editor)
1049 {
1050 CamelFolderInfo *folder_info;
1051 gchar *casefolded;
1052 gboolean match;
1053
1054 /* If there's no search string let everything through. */
1055 if (editor->priv->search_string == NULL)
1056 return TRUE;
1057
1058 gtk_tree_model_get (
1059 tree_model, iter,
1060 COL_CASEFOLDED, &casefolded,
1061 COL_FOLDER_INFO, &folder_info, -1);
1062
1063 match = FOLDER_CAN_SELECT (folder_info) &&
1064 (casefolded != NULL) && (*casefolded != '\0') &&
1065 (strstr (casefolded, editor->priv->search_string) != NULL);
1066
1067 g_free (casefolded);
1068
1069 return match;
1070 }
1071
1072 static void
subscription_editor_update_view(EMSubscriptionEditor * editor)1073 subscription_editor_update_view (EMSubscriptionEditor *editor)
1074 {
1075 GtkEntry *entry;
1076 GtkTreeView *tree_view;
1077 GtkTreeModel *tree_model;
1078 const gchar *text;
1079
1080 entry = GTK_ENTRY (editor->priv->entry);
1081 tree_view = editor->priv->active->tree_view;
1082
1083 editor->priv->timeout_id = 0;
1084
1085 text = gtk_entry_get_text (entry);
1086
1087 if (text != NULL && *text != '\0') {
1088 g_free (editor->priv->search_string);
1089 editor->priv->search_string = g_utf8_casefold (text, -1);
1090
1091 /* Install the list store in the tree view if needed. */
1092 if (!editor->priv->active->filtered_view) {
1093 GtkTreeSelection *selection;
1094 GtkTreePath *path;
1095
1096 tree_model = gtk_tree_model_filter_new (
1097 editor->priv->active->list_store, NULL);
1098 gtk_tree_model_filter_set_visible_func (
1099 GTK_TREE_MODEL_FILTER (tree_model),
1100 (GtkTreeModelFilterVisibleFunc)
1101 subscription_editor_filter_cb, editor,
1102 (GDestroyNotify) NULL);
1103 gtk_tree_view_set_model (tree_view, tree_model);
1104 gtk_tree_view_set_search_column (tree_view, COL_FOLDER_NAME);
1105 g_object_unref (tree_model);
1106
1107 path = gtk_tree_path_new_first ();
1108 selection = gtk_tree_view_get_selection (tree_view);
1109 gtk_tree_selection_select_path (selection, path);
1110 gtk_tree_path_free (path);
1111
1112 editor->priv->active->filtered_view = TRUE;
1113 }
1114
1115 tree_model = gtk_tree_view_get_model (tree_view);
1116 gtk_tree_model_filter_refilter (
1117 GTK_TREE_MODEL_FILTER (tree_model));
1118
1119 gtk_entry_set_icon_sensitive (
1120 entry, GTK_ENTRY_ICON_SECONDARY, TRUE);
1121
1122 gtk_widget_set_sensitive (
1123 editor->priv->collapse_all_button, FALSE);
1124 gtk_widget_set_sensitive (
1125 editor->priv->expand_all_button, FALSE);
1126
1127 } else {
1128 /* Install the tree store in the tree view if needed. */
1129 if (editor->priv->active->filtered_view) {
1130 GtkTreeSelection *selection;
1131 GtkTreePath *path;
1132
1133 tree_model = editor->priv->active->tree_store;
1134 gtk_tree_view_set_model (tree_view, tree_model);
1135 gtk_tree_view_set_search_column (tree_view, COL_FOLDER_NAME);
1136
1137 path = gtk_tree_path_new_first ();
1138 selection = gtk_tree_view_get_selection (tree_view);
1139 gtk_tree_selection_select_path (selection, path);
1140 gtk_tree_path_free (path);
1141
1142 editor->priv->active->filtered_view = FALSE;
1143 }
1144
1145 gtk_entry_set_icon_sensitive (
1146 entry, GTK_ENTRY_ICON_SECONDARY, FALSE);
1147
1148 gtk_widget_set_sensitive (
1149 editor->priv->collapse_all_button, TRUE);
1150 gtk_widget_set_sensitive (
1151 editor->priv->expand_all_button, TRUE);
1152 }
1153 }
1154
1155 static gboolean
subscription_editor_timeout_cb(gpointer user_data)1156 subscription_editor_timeout_cb (gpointer user_data)
1157 {
1158 EMSubscriptionEditor *editor;
1159
1160 editor = EM_SUBSCRIPTION_EDITOR (user_data);
1161 subscription_editor_update_view (editor);
1162 editor->priv->timeout_id = 0;
1163
1164 return FALSE;
1165 }
1166
1167 static void
subscription_editor_combo_box_changed_cb(GtkComboBox * combo_box,EMSubscriptionEditor * editor)1168 subscription_editor_combo_box_changed_cb (GtkComboBox *combo_box,
1169 EMSubscriptionEditor *editor)
1170 {
1171 StoreData *data;
1172 gint index;
1173
1174 index = gtk_combo_box_get_active (combo_box);
1175 g_return_if_fail (index < editor->priv->stores->len);
1176
1177 data = g_ptr_array_index (editor->priv->stores, index);
1178 g_return_if_fail (data != NULL);
1179
1180 editor->priv->active = data;
1181
1182 subscription_editor_stop (editor);
1183 subscription_editor_update_view (editor);
1184
1185 g_object_notify (G_OBJECT (editor), "store");
1186
1187 if (data->needs_refresh) {
1188 subscription_editor_refresh (editor);
1189 data->needs_refresh = FALSE;
1190 }
1191 }
1192
1193 static void
subscription_editor_entry_changed_cb(GtkEntry * entry,EMSubscriptionEditor * editor)1194 subscription_editor_entry_changed_cb (GtkEntry *entry,
1195 EMSubscriptionEditor *editor)
1196 {
1197 const gchar *text;
1198
1199 if (editor->priv->timeout_id > 0) {
1200 g_source_remove (editor->priv->timeout_id);
1201 editor->priv->timeout_id = 0;
1202 }
1203
1204 text = gtk_entry_get_text (entry);
1205
1206 if (text != NULL && *text != '\0') {
1207 editor->priv->timeout_id = e_named_timeout_add_seconds (
1208 1, subscription_editor_timeout_cb, editor);
1209 } else {
1210 subscription_editor_update_view (editor);
1211 }
1212 }
1213
1214 static void
subscription_editor_icon_release_cb(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,EMSubscriptionEditor * editor)1215 subscription_editor_icon_release_cb (GtkEntry *entry,
1216 GtkEntryIconPosition icon_pos,
1217 GdkEvent *event,
1218 EMSubscriptionEditor *editor)
1219 {
1220 if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
1221 gtk_entry_set_text (entry, "");
1222 }
1223
1224 static void
subscription_editor_renderer_toggled_cb(GtkCellRendererToggle * renderer,const gchar * path_string,EMSubscriptionEditor * editor)1225 subscription_editor_renderer_toggled_cb (GtkCellRendererToggle *renderer,
1226 const gchar *path_string,
1227 EMSubscriptionEditor *editor)
1228 {
1229 GtkTreeSelection *selection;
1230 GtkTreeView *tree_view;
1231 GtkTreePath *path;
1232
1233 tree_view = editor->priv->active->tree_view;
1234 selection = gtk_tree_view_get_selection (tree_view);
1235
1236 path = gtk_tree_path_new_from_string (path_string);
1237 gtk_tree_selection_select_path (selection, path);
1238 gtk_tree_path_free (path);
1239
1240 if (gtk_cell_renderer_toggle_get_active (renderer))
1241 subscription_editor_unsubscribe (editor);
1242 else
1243 subscription_editor_subscribe (editor);
1244 }
1245
1246 static void
subscription_editor_render_toggle_cb(GtkCellLayout * cell_layout,GtkCellRenderer * renderer,GtkTreeModel * tree_model,GtkTreeIter * iter)1247 subscription_editor_render_toggle_cb (GtkCellLayout *cell_layout,
1248 GtkCellRenderer *renderer,
1249 GtkTreeModel *tree_model,
1250 GtkTreeIter *iter)
1251 {
1252 CamelFolderInfo *folder_info;
1253
1254 gtk_tree_model_get (
1255 tree_model, iter, COL_FOLDER_INFO, &folder_info, -1);
1256
1257 g_object_set (
1258 renderer, "active", FOLDER_SUBSCRIBED (folder_info),
1259 "visible", FOLDER_CAN_SELECT (folder_info), NULL);
1260 }
1261
1262 static void
subscription_editor_selection_changed_cb(GtkTreeSelection * selection,EMSubscriptionEditor * editor)1263 subscription_editor_selection_changed_cb (GtkTreeSelection *selection,
1264 EMSubscriptionEditor *editor)
1265 {
1266 GtkTreeModel *tree_model;
1267 GtkTreeIter iter;
1268
1269 if (gtk_tree_selection_get_selected (selection, &tree_model, &iter)) {
1270 CamelFolderInfo *folder_info;
1271
1272 gtk_tree_model_get (
1273 tree_model, &iter,
1274 COL_FOLDER_INFO, &folder_info, -1);
1275 gtk_widget_set_sensitive (
1276 editor->priv->subscribe_button,
1277 FOLDER_CAN_SELECT (folder_info) &&
1278 !FOLDER_SUBSCRIBED (folder_info));
1279 gtk_widget_set_sensitive (
1280 editor->priv->unsubscribe_button,
1281 FOLDER_CAN_SELECT (folder_info) &&
1282 FOLDER_SUBSCRIBED (folder_info));
1283 } else {
1284 gtk_widget_set_sensitive (
1285 editor->priv->subscribe_button, FALSE);
1286 gtk_widget_set_sensitive (
1287 editor->priv->unsubscribe_button, FALSE);
1288 }
1289
1290 gtk_widget_set_sensitive (editor->priv->subscribe_arrow, TRUE);
1291 gtk_widget_set_sensitive (editor->priv->unsubscribe_arrow, TRUE);
1292 }
1293
1294 static void
em_subscription_editor_get_unread_total_text_cb(GtkTreeViewColumn * tree_column,GtkCellRenderer * cell,GtkTreeModel * tree_model,GtkTreeIter * iter,gpointer user_data)1295 em_subscription_editor_get_unread_total_text_cb (GtkTreeViewColumn *tree_column,
1296 GtkCellRenderer *cell,
1297 GtkTreeModel *tree_model,
1298 GtkTreeIter *iter,
1299 gpointer user_data)
1300 {
1301 CamelFolderInfo *folder_info = NULL;
1302 GString *text = NULL;
1303
1304 g_return_if_fail (GTK_IS_CELL_RENDERER_TEXT (cell));
1305 g_return_if_fail (GTK_IS_TREE_MODEL (tree_model));
1306 g_return_if_fail (iter != NULL);
1307
1308 gtk_tree_model_get (tree_model, iter, COL_FOLDER_INFO, &folder_info, -1);
1309
1310 if (folder_info && folder_info->total > 0 && folder_info->unread >= 0 && folder_info->unread <= folder_info->total) {
1311 text = g_string_new ("");
1312
1313 if (folder_info->unread > 0)
1314 g_string_append_printf (
1315 text, ngettext ("%d unread, ",
1316 "%d unread, ", folder_info->unread), folder_info->unread);
1317
1318 g_string_append_printf (
1319 text, ngettext ("%d total", "%d total",
1320 folder_info->total), folder_info->total);
1321 }
1322
1323 g_object_set (G_OBJECT (cell), "text", text ? text->str : NULL, NULL);
1324
1325 if (text)
1326 g_string_free (text, TRUE);
1327 }
1328
1329 static void
subscription_editor_add_store(EMSubscriptionEditor * editor,CamelStore * store)1330 subscription_editor_add_store (EMSubscriptionEditor *editor,
1331 CamelStore *store)
1332 {
1333 StoreData *data;
1334 CamelService *service;
1335 GtkListStore *list_store;
1336 GtkTreeStore *tree_store;
1337 GtkTreeViewColumn *column;
1338 GtkTreeSelection *selection;
1339 GtkCellRenderer *renderer;
1340 GtkComboBoxText *combo_box;
1341 GtkWidget *container;
1342 GtkWidget *widget;
1343 const gchar *display_name;
1344
1345 service = CAMEL_SERVICE (store);
1346 display_name = camel_service_get_display_name (service);
1347
1348 combo_box = GTK_COMBO_BOX_TEXT (editor->priv->combo_box);
1349 gtk_combo_box_text_append_text (combo_box, display_name);
1350
1351 tree_store = gtk_tree_store_new (
1352 N_COLUMNS,
1353 /* COL_CASEFOLDED */ G_TYPE_STRING,
1354 /* COL_FOLDER_ICON */ G_TYPE_STRING,
1355 /* COL_FOLDER_NAME */ G_TYPE_STRING,
1356 /* COL_FOLDER_INFO */ G_TYPE_POINTER);
1357
1358 list_store = gtk_list_store_new (
1359 N_COLUMNS,
1360 /* COL_CASEFOLDED */ G_TYPE_STRING,
1361 /* COL_FOLDER_ICON */ G_TYPE_STRING,
1362 /* COL_FOLDER_NAME */ G_TYPE_STRING,
1363 /* COL_FOLDER_INFO */ G_TYPE_POINTER);
1364
1365 container = editor->priv->notebook;
1366
1367 widget = gtk_scrolled_window_new (NULL, NULL);
1368 gtk_scrolled_window_set_policy (
1369 GTK_SCROLLED_WINDOW (widget),
1370 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1371 gtk_scrolled_window_set_shadow_type (
1372 GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN);
1373 gtk_notebook_append_page (GTK_NOTEBOOK (container), widget, NULL);
1374 gtk_container_child_set (
1375 GTK_CONTAINER (container), widget,
1376 "tab-fill", FALSE, "tab-expand", FALSE, NULL);
1377 gtk_widget_show (widget);
1378
1379 container = widget;
1380
1381 widget = gtk_tree_view_new_with_model (GTK_TREE_MODEL (tree_store));
1382 gtk_tree_view_set_enable_search (GTK_TREE_VIEW (widget), TRUE);
1383 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (widget), FALSE);
1384 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (widget), TRUE);
1385 gtk_tree_view_set_search_column (
1386 GTK_TREE_VIEW (widget), COL_FOLDER_NAME);
1387 gtk_container_add (GTK_CONTAINER (container), widget);
1388 gtk_widget_show (widget);
1389
1390 column = gtk_tree_view_column_new ();
1391 gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
1392
1393 renderer = gtk_cell_renderer_toggle_new ();
1394 g_object_set (renderer, "activatable", TRUE, NULL);
1395 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1396
1397 gtk_cell_layout_set_cell_data_func (
1398 GTK_CELL_LAYOUT (column), renderer,
1399 (GtkCellLayoutDataFunc) subscription_editor_render_toggle_cb,
1400 NULL, (GDestroyNotify) NULL);
1401
1402 g_signal_connect (
1403 renderer, "toggled",
1404 G_CALLBACK (subscription_editor_renderer_toggled_cb), editor);
1405
1406 column = gtk_tree_view_column_new ();
1407 gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
1408 gtk_tree_view_set_expander_column (GTK_TREE_VIEW (widget), column);
1409
1410 renderer = gtk_cell_renderer_pixbuf_new ();
1411 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1412 gtk_tree_view_column_add_attribute (
1413 column, renderer, "icon-name", COL_FOLDER_ICON);
1414
1415 renderer = gtk_cell_renderer_text_new ();
1416 gtk_tree_view_column_pack_start (column, renderer, TRUE);
1417 gtk_tree_view_column_add_attribute (
1418 column, renderer, "text", COL_FOLDER_NAME);
1419
1420 column = gtk_tree_view_column_new ();
1421 gtk_tree_view_append_column (GTK_TREE_VIEW (widget), column);
1422
1423 renderer = gtk_cell_renderer_text_new ();
1424 gtk_tree_view_column_pack_start (column, renderer, FALSE);
1425 gtk_tree_view_column_set_cell_data_func (column, renderer,
1426 em_subscription_editor_get_unread_total_text_cb, NULL, NULL);
1427
1428 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (widget));
1429
1430 g_signal_connect (
1431 selection, "changed",
1432 G_CALLBACK (subscription_editor_selection_changed_cb), editor);
1433
1434 data = g_slice_new0 (StoreData);
1435 data->store = g_object_ref (store);
1436 data->tree_view = GTK_TREE_VIEW (g_object_ref (widget));
1437 data->list_store = GTK_TREE_MODEL (list_store);
1438 data->tree_store = GTK_TREE_MODEL (tree_store);
1439 data->needs_refresh = TRUE;
1440
1441 g_ptr_array_add (editor->priv->stores, data);
1442 }
1443
1444 static void
emse_notebook_sensitive_changed_cb(GtkWidget * notebook,GParamSpec * param,GtkDialog * editor)1445 emse_notebook_sensitive_changed_cb (GtkWidget *notebook,
1446 GParamSpec *param,
1447 GtkDialog *editor)
1448 {
1449 gtk_dialog_set_response_sensitive (
1450 editor, GTK_RESPONSE_CLOSE,
1451 gtk_widget_get_sensitive (notebook));
1452 }
1453
1454 static gboolean
subscription_editor_delete_event_cb(EMSubscriptionEditor * editor,GdkEvent * event,gpointer user_data)1455 subscription_editor_delete_event_cb (EMSubscriptionEditor *editor,
1456 GdkEvent *event,
1457 gpointer user_data)
1458 {
1459 /* stop processing if the button is insensitive */
1460 return !gtk_widget_get_sensitive (editor->priv->notebook);
1461 }
1462
1463 static void
subscription_editor_response_cb(EMSubscriptionEditor * editor,gint response_id,gpointer user_data)1464 subscription_editor_response_cb (EMSubscriptionEditor *editor,
1465 gint response_id,
1466 gpointer user_data)
1467 {
1468 if (!gtk_widget_get_sensitive (editor->priv->notebook))
1469 g_signal_stop_emission_by_name (editor, "response");
1470 }
1471
1472 static void
subscription_editor_set_store(EMSubscriptionEditor * editor,CamelStore * store)1473 subscription_editor_set_store (EMSubscriptionEditor *editor,
1474 CamelStore *store)
1475 {
1476 g_return_if_fail (editor->priv->initial_store == NULL);
1477
1478 if (CAMEL_IS_SUBSCRIBABLE (store))
1479 editor->priv->initial_store = g_object_ref (store);
1480 }
1481
1482 static void
subscription_editor_set_session(EMSubscriptionEditor * editor,EMailSession * session)1483 subscription_editor_set_session (EMSubscriptionEditor *editor,
1484 EMailSession *session)
1485 {
1486 g_return_if_fail (E_IS_MAIL_SESSION (session));
1487 g_return_if_fail (editor->priv->session == NULL);
1488
1489 editor->priv->session = g_object_ref (session);
1490 }
1491
1492 static void
subscription_editor_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1493 subscription_editor_set_property (GObject *object,
1494 guint property_id,
1495 const GValue *value,
1496 GParamSpec *pspec)
1497 {
1498 switch (property_id) {
1499 case PROP_SESSION:
1500 subscription_editor_set_session (
1501 EM_SUBSCRIPTION_EDITOR (object),
1502 g_value_get_object (value));
1503 return;
1504
1505 case PROP_STORE:
1506 subscription_editor_set_store (
1507 EM_SUBSCRIPTION_EDITOR (object),
1508 g_value_get_object (value));
1509 return;
1510 }
1511
1512 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1513 }
1514
1515 static void
subscription_editor_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1516 subscription_editor_get_property (GObject *object,
1517 guint property_id,
1518 GValue *value,
1519 GParamSpec *pspec)
1520 {
1521 switch (property_id) {
1522 case PROP_SESSION:
1523 g_value_set_object (
1524 value,
1525 em_subscription_editor_get_session (
1526 EM_SUBSCRIPTION_EDITOR (object)));
1527 return;
1528
1529 case PROP_STORE:
1530 g_value_set_object (
1531 value,
1532 em_subscription_editor_get_store (
1533 EM_SUBSCRIPTION_EDITOR (object)));
1534 return;
1535 }
1536
1537 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1538 }
1539
1540 static void
subscription_editor_dispose(GObject * object)1541 subscription_editor_dispose (GObject *object)
1542 {
1543 EMSubscriptionEditorPrivate *priv;
1544
1545 priv = EM_SUBSCRIPTION_EDITOR_GET_PRIVATE (object);
1546
1547 g_clear_object (&priv->session);
1548 g_clear_object (&priv->initial_store);
1549
1550 if (priv->timeout_id > 0) {
1551 g_source_remove (priv->timeout_id);
1552 priv->timeout_id = 0;
1553 }
1554
1555 g_ptr_array_set_size (priv->stores, 0);
1556
1557 /* Chain up to parent's dispose() method. */
1558 G_OBJECT_CLASS (em_subscription_editor_parent_class)->dispose (object);
1559 }
1560
1561 static void
subscription_editor_finalize(GObject * object)1562 subscription_editor_finalize (GObject *object)
1563 {
1564 EMSubscriptionEditorPrivate *priv;
1565
1566 priv = EM_SUBSCRIPTION_EDITOR_GET_PRIVATE (object);
1567
1568 g_ptr_array_free (priv->stores, TRUE);
1569
1570 g_free (priv->search_string);
1571
1572 /* Chain up to parent's finalize() method. */
1573 G_OBJECT_CLASS (em_subscription_editor_parent_class)->finalize (object);
1574 }
1575
1576 static void
subscription_editor_constructed(GObject * object)1577 subscription_editor_constructed (GObject *object)
1578 {
1579 EMSubscriptionEditor *editor;
1580
1581 editor = EM_SUBSCRIPTION_EDITOR (object);
1582
1583 /* Pick an initial store based on the default mail account, if
1584 * one wasn't already given in em_subscription_editor_new(). */
1585 if (editor->priv->initial_store == NULL) {
1586 ESource *source;
1587 ESourceRegistry *registry;
1588 CamelService *service;
1589 EMailSession *session;
1590
1591 session = em_subscription_editor_get_session (editor);
1592 registry = e_mail_session_get_registry (session);
1593
1594 source = e_source_registry_ref_default_mail_account (registry);
1595
1596 service = camel_session_ref_service (
1597 CAMEL_SESSION (session),
1598 e_source_get_uid (source));
1599
1600 if (CAMEL_IS_SUBSCRIBABLE (service))
1601 editor->priv->initial_store = CAMEL_STORE (g_object_ref (service));
1602
1603 if (service != NULL)
1604 g_object_unref (service);
1605
1606 g_object_unref (source);
1607 }
1608
1609 /* Chain up to parent's constructed() method. */
1610 G_OBJECT_CLASS (em_subscription_editor_parent_class)->constructed (object);
1611
1612 g_signal_connect (
1613 editor, "delete-event",
1614 G_CALLBACK (subscription_editor_delete_event_cb), NULL);
1615 g_signal_connect (
1616 editor, "response",
1617 G_CALLBACK (subscription_editor_response_cb), NULL);
1618 }
1619
1620 static void
subscription_editor_realize(GtkWidget * widget)1621 subscription_editor_realize (GtkWidget *widget)
1622 {
1623 EMSubscriptionEditor *editor;
1624 EMFolderTreeModel *model;
1625 GtkComboBox *combo_box;
1626 GList *list, *link;
1627 gint initial_index = 0;
1628
1629 editor = EM_SUBSCRIPTION_EDITOR (widget);
1630
1631 /* Chain up to parent's realize() method. */
1632 GTK_WIDGET_CLASS (em_subscription_editor_parent_class)->realize (widget);
1633
1634 /* Find stores to display, and watch for the initial store. */
1635
1636 model = em_folder_tree_model_get_default ();
1637 list = em_folder_tree_model_list_stores (model);
1638
1639 for (link = list; link != NULL; link = g_list_next (link)) {
1640 CamelStore *store = CAMEL_STORE (link->data);
1641
1642 if (!CAMEL_IS_SUBSCRIBABLE (store))
1643 continue;
1644
1645 if (store == editor->priv->initial_store)
1646 initial_index = editor->priv->stores->len;
1647
1648 subscription_editor_add_store (editor, store);
1649 }
1650
1651 g_list_free (list);
1652
1653 /* The subscription editor should only be accessible if
1654 * at least one enabled store supports subscriptions. */
1655 g_return_if_fail (editor->priv->stores->len > 0);
1656
1657 combo_box = GTK_COMBO_BOX (editor->priv->combo_box);
1658 gtk_combo_box_set_active (combo_box, initial_index);
1659
1660 g_signal_connect (
1661 combo_box, "changed",
1662 G_CALLBACK (subscription_editor_combo_box_changed_cb), editor);
1663
1664 subscription_editor_combo_box_changed_cb (combo_box, editor);
1665 }
1666
1667 static void
em_subscription_editor_class_init(EMSubscriptionEditorClass * class)1668 em_subscription_editor_class_init (EMSubscriptionEditorClass *class)
1669 {
1670 GObjectClass *object_class;
1671 GtkWidgetClass *widget_class;
1672
1673 g_type_class_add_private (class, sizeof (EMSubscriptionEditorPrivate));
1674
1675 object_class = G_OBJECT_CLASS (class);
1676 object_class->set_property = subscription_editor_set_property;
1677 object_class->get_property = subscription_editor_get_property;
1678 object_class->dispose = subscription_editor_dispose;
1679 object_class->finalize = subscription_editor_finalize;
1680 object_class->constructed = subscription_editor_constructed;
1681
1682 widget_class = GTK_WIDGET_CLASS (class);
1683 widget_class->realize = subscription_editor_realize;
1684
1685 g_object_class_install_property (
1686 object_class,
1687 PROP_SESSION,
1688 g_param_spec_object (
1689 "session",
1690 NULL,
1691 NULL,
1692 E_TYPE_MAIL_SESSION,
1693 G_PARAM_READWRITE |
1694 G_PARAM_CONSTRUCT_ONLY |
1695 G_PARAM_STATIC_STRINGS));
1696
1697 g_object_class_install_property (
1698 object_class,
1699 PROP_STORE,
1700 g_param_spec_object (
1701 "store",
1702 NULL,
1703 NULL,
1704 CAMEL_TYPE_STORE,
1705 G_PARAM_READWRITE |
1706 G_PARAM_CONSTRUCT_ONLY |
1707 G_PARAM_STATIC_STRINGS));
1708 }
1709
1710 static void
em_subscription_editor_init(EMSubscriptionEditor * editor)1711 em_subscription_editor_init (EMSubscriptionEditor *editor)
1712 {
1713 GtkWidget *container;
1714 GtkWidget *widget;
1715 GtkWidget *box;
1716 const gchar *tooltip;
1717
1718 editor->priv = EM_SUBSCRIPTION_EDITOR_GET_PRIVATE (editor);
1719
1720 editor->priv->stores = g_ptr_array_new_with_free_func (
1721 (GDestroyNotify) store_data_free);
1722
1723 gtk_container_set_border_width (GTK_CONTAINER (editor), 5);
1724 gtk_window_set_title (GTK_WINDOW (editor), _("Folder Subscriptions"));
1725 gtk_window_set_default_size (GTK_WINDOW (editor), 600, 400);
1726
1727 e_restore_window (
1728 GTK_WINDOW (editor),
1729 "/org/gnome/evolution/mail/subscription-window/",
1730 E_RESTORE_WINDOW_SIZE);
1731
1732 gtk_dialog_add_button (
1733 GTK_DIALOG (editor),
1734 _("_Close"), GTK_RESPONSE_CLOSE);
1735
1736 container = gtk_dialog_get_content_area (GTK_DIALOG (editor));
1737
1738 widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1739 gtk_container_set_border_width (GTK_CONTAINER (widget), 5);
1740 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
1741 gtk_widget_show (widget);
1742
1743 container = box = widget;
1744
1745 widget = gtk_grid_new ();
1746 gtk_grid_set_row_spacing (GTK_GRID (widget), 6);
1747 gtk_grid_set_column_spacing (GTK_GRID (widget), 6);
1748 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1749 gtk_widget_show (widget);
1750
1751 container = widget;
1752
1753 widget = gtk_combo_box_text_new ();
1754 gtk_widget_set_hexpand (widget, TRUE);
1755 gtk_grid_attach (GTK_GRID (container), widget, 1, 0, 1, 1);
1756 editor->priv->combo_box = widget;
1757 gtk_widget_show (widget);
1758
1759 widget = gtk_label_new_with_mnemonic (_("_Account:"));
1760 gtk_label_set_mnemonic_widget (
1761 GTK_LABEL (widget), editor->priv->combo_box);
1762 gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
1763 gtk_grid_attach (GTK_GRID (container), widget, 0, 0, 1, 1);
1764 gtk_widget_show (widget);
1765
1766 widget = gtk_entry_new ();
1767 gtk_entry_set_icon_from_icon_name (
1768 GTK_ENTRY (widget),
1769 GTK_ENTRY_ICON_SECONDARY, "edit-clear");
1770 gtk_entry_set_icon_tooltip_text (
1771 GTK_ENTRY (widget),
1772 GTK_ENTRY_ICON_SECONDARY, _("Clear Search"));
1773 gtk_entry_set_icon_sensitive (
1774 GTK_ENTRY (widget),
1775 GTK_ENTRY_ICON_SECONDARY, FALSE);
1776 gtk_widget_set_hexpand (widget, TRUE);
1777 gtk_grid_attach (GTK_GRID (container), widget, 1, 1, 1, 1);
1778 editor->priv->entry = widget;
1779 gtk_widget_show (widget);
1780
1781 g_signal_connect (
1782 widget, "changed",
1783 G_CALLBACK (subscription_editor_entry_changed_cb), editor);
1784
1785 g_signal_connect (
1786 widget, "icon-release",
1787 G_CALLBACK (subscription_editor_icon_release_cb), editor);
1788
1789 widget = gtk_label_new_with_mnemonic (_("Sho_w items that contain:"));
1790 gtk_label_set_mnemonic_widget (
1791 GTK_LABEL (widget), editor->priv->entry);
1792 gtk_misc_set_alignment (GTK_MISC (widget), 1.0, 0.5);
1793 gtk_grid_attach (GTK_GRID (container), widget, 0, 1, 1, 1);
1794 gtk_widget_show (widget);
1795
1796 container = box;
1797
1798 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
1799 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
1800 gtk_widget_show (widget);
1801
1802 container = widget;
1803
1804 widget = gtk_notebook_new ();
1805 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (widget), FALSE);
1806 gtk_notebook_set_show_border (GTK_NOTEBOOK (widget), FALSE);
1807 gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0);
1808 editor->priv->notebook = widget;
1809 gtk_widget_show (widget);
1810
1811 e_binding_bind_property (
1812 editor->priv->combo_box, "active",
1813 editor->priv->notebook, "page",
1814 G_BINDING_BIDIRECTIONAL |
1815 G_BINDING_SYNC_CREATE);
1816
1817 e_signal_connect_notify (
1818 widget, "notify::sensitive",
1819 G_CALLBACK (emse_notebook_sensitive_changed_cb), editor);
1820
1821 widget = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
1822 gtk_box_set_spacing (GTK_BOX (widget), 6);
1823 gtk_button_box_set_layout (
1824 GTK_BUTTON_BOX (widget), GTK_BUTTONBOX_START);
1825 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
1826 gtk_widget_show (widget);
1827
1828 container = box = widget;
1829
1830 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1831 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
1832 gtk_widget_show (widget);
1833
1834 container = widget;
1835
1836 tooltip = _("Subscribe to the selected folder");
1837 widget = gtk_button_new_with_mnemonic (_("Su_bscribe"));
1838 gtk_widget_set_sensitive (widget, FALSE);
1839 gtk_widget_set_tooltip_text (widget, tooltip);
1840 editor->priv->subscribe_button = widget;
1841 gtk_widget_show (widget);
1842
1843 g_signal_connect_swapped (
1844 widget, "clicked",
1845 G_CALLBACK (subscription_editor_subscribe), editor);
1846
1847 widget = gtk_button_new ();
1848 gtk_button_set_image (
1849 GTK_BUTTON (widget),
1850 gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
1851 editor->priv->subscribe_arrow = widget;
1852 gtk_widget_show (widget);
1853
1854 g_signal_connect_swapped (
1855 widget, "clicked",
1856 G_CALLBACK (subscription_editor_subscribe_popup_cb), editor);
1857
1858 if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) {
1859 gtk_box_pack_start (
1860 GTK_BOX (container),
1861 editor->priv->subscribe_button, TRUE, TRUE, 0);
1862 gtk_box_pack_start (
1863 GTK_BOX (container),
1864 editor->priv->subscribe_arrow, FALSE, FALSE, 0);
1865 } else {
1866 gtk_box_pack_start (
1867 GTK_BOX (container),
1868 editor->priv->subscribe_arrow, FALSE, FALSE, 0);
1869 gtk_box_pack_start (
1870 GTK_BOX (container),
1871 editor->priv->subscribe_button, TRUE, TRUE, 0);
1872 }
1873
1874 container = box;
1875
1876 widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 2);
1877 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
1878 gtk_widget_show (widget);
1879
1880 container = widget;
1881
1882 tooltip = _("Unsubscribe from the selected folder");
1883 widget = gtk_button_new_with_mnemonic (_("_Unsubscribe"));
1884 gtk_widget_set_sensitive (widget, FALSE);
1885 gtk_widget_set_tooltip_text (widget, tooltip);
1886 editor->priv->unsubscribe_button = widget;
1887 gtk_widget_show (widget);
1888
1889 g_signal_connect_swapped (
1890 widget, "clicked",
1891 G_CALLBACK (subscription_editor_unsubscribe), editor);
1892
1893 widget = gtk_button_new ();
1894 gtk_button_set_image (
1895 GTK_BUTTON (widget),
1896 gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE));
1897 editor->priv->unsubscribe_arrow = widget;
1898 gtk_widget_show (widget);
1899
1900 g_signal_connect_swapped (
1901 widget, "clicked",
1902 G_CALLBACK (subscription_editor_unsubscribe_popup_cb), editor);
1903
1904 if (gtk_widget_get_direction (container) == GTK_TEXT_DIR_LTR) {
1905 gtk_box_pack_start (
1906 GTK_BOX (container),
1907 editor->priv->unsubscribe_button, TRUE, TRUE, 0);
1908 gtk_box_pack_start (
1909 GTK_BOX (container),
1910 editor->priv->unsubscribe_arrow, FALSE, FALSE, 0);
1911 } else {
1912 gtk_box_pack_start (
1913 GTK_BOX (container),
1914 editor->priv->unsubscribe_arrow, FALSE, FALSE, 0);
1915 gtk_box_pack_start (
1916 GTK_BOX (container),
1917 editor->priv->unsubscribe_button, TRUE, TRUE, 0);
1918 }
1919
1920 container = box;
1921
1922 tooltip = _("Collapse all folders");
1923 widget = gtk_button_new_with_mnemonic (_("C_ollapse All"));
1924 gtk_widget_set_tooltip_text (widget, tooltip);
1925 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1926 editor->priv->collapse_all_button = widget;
1927 gtk_widget_show (widget);
1928
1929 g_signal_connect_swapped (
1930 widget, "clicked",
1931 G_CALLBACK (subscription_editor_collapse_all), editor);
1932
1933 tooltip = _("Expand all folders");
1934 widget = gtk_button_new_with_mnemonic (_("E_xpand All"));
1935 gtk_widget_set_tooltip_text (widget, tooltip);
1936 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1937 editor->priv->expand_all_button = widget;
1938 gtk_widget_show (widget);
1939
1940 g_signal_connect_swapped (
1941 widget, "clicked",
1942 G_CALLBACK (subscription_editor_expand_all), editor);
1943
1944 tooltip = _("Refresh the folder list");
1945 widget = e_dialog_button_new_with_icon ("view-refresh", _("_Refresh"));
1946 gtk_widget_set_tooltip_text (widget, tooltip);
1947 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1948 gtk_widget_set_sensitive (widget, FALSE);
1949 editor->priv->refresh_button = widget;
1950 gtk_widget_show (widget);
1951
1952 g_signal_connect_swapped (
1953 widget, "clicked",
1954 G_CALLBACK (subscription_editor_refresh), editor);
1955
1956 tooltip = _("Stop the current operation");
1957 widget = e_dialog_button_new_with_icon ("process-stop", _("_Stop"));
1958 gtk_widget_set_tooltip_text (widget, tooltip);
1959 gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0);
1960 gtk_widget_set_sensitive (widget, FALSE);
1961 editor->priv->stop_button = widget;
1962 gtk_widget_show (widget);
1963
1964 g_signal_connect_swapped (
1965 widget, "clicked",
1966 G_CALLBACK (subscription_editor_stop), editor);
1967 }
1968
1969 GtkWidget *
em_subscription_editor_new(GtkWindow * parent,EMailSession * session,CamelStore * initial_store)1970 em_subscription_editor_new (GtkWindow *parent,
1971 EMailSession *session,
1972 CamelStore *initial_store)
1973 {
1974 g_return_val_if_fail (GTK_IS_WINDOW (parent), NULL);
1975 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
1976
1977 return g_object_new (
1978 EM_TYPE_SUBSCRIPTION_EDITOR,
1979 "session", session,
1980 "store", initial_store,
1981 "transient-for", parent,
1982 NULL);
1983 }
1984
1985 EMailSession *
em_subscription_editor_get_session(EMSubscriptionEditor * editor)1986 em_subscription_editor_get_session (EMSubscriptionEditor *editor)
1987 {
1988 g_return_val_if_fail (EM_IS_SUBSCRIPTION_EDITOR (editor), NULL);
1989
1990 return editor->priv->session;
1991 }
1992
1993 CamelStore *
em_subscription_editor_get_store(EMSubscriptionEditor * editor)1994 em_subscription_editor_get_store (EMSubscriptionEditor *editor)
1995 {
1996 g_return_val_if_fail (EM_IS_SUBSCRIPTION_EDITOR (editor), NULL);
1997
1998 if (editor->priv->active == NULL)
1999 return NULL;
2000
2001 return editor->priv->active->store;
2002 }
2003