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