1 /*
2  * This program is free software; you can redistribute it and/or modify it
3  * under the terms of the GNU Lesser General Public License as published by
4  * the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful, but
7  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
8  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
9  * for more details.
10  *
11  * You should have received a copy of the GNU Lesser General Public License
12  * along with this program; if not, see <http://www.gnu.org/licenses/>.
13  *
14  *
15  * Authors:
16  *		Not Zed <notzed@lostzed.mmc.com.au>
17  *      Jeffrey Stedfast <fejj@ximian.com>
18  *
19  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
20  *
21  */
22 
23 #include "evolution-config.h"
24 
25 #include <string.h>
26 
27 #include <gtk/gtk.h>
28 #include <glib/gi18n.h>
29 
30 #include <shell/e-shell.h>
31 
32 #include <e-util/e-util.h>
33 #include <e-util/e-util-private.h>
34 
35 #include "em-folder-selector.h"
36 #include "em-folder-tree.h"
37 #include "em-utils.h"
38 #include "em-vfolder-editor-context.h"
39 
40 #include "em-vfolder-editor-rule.h"
41 
42 #define EM_VFOLDER_EDITOR_RULE_GET_PRIVATE(obj) \
43 	(G_TYPE_INSTANCE_GET_PRIVATE \
44 	((obj), EM_TYPE_VFOLDER_EDITOR_RULE, EMVFolderEditorRulePrivate))
45 
46 #define EM_VFOLDER_EDITOR_RULE_GET_PRIVATE(obj) \
47 	(G_TYPE_INSTANCE_GET_PRIVATE \
48 	((obj), EM_TYPE_VFOLDER_EDITOR_RULE, EMVFolderEditorRulePrivate))
49 
50 struct _EMVFolderEditorRulePrivate {
51 	EMailSession *session;
52 };
53 
54 enum {
55 	PROP_0,
56 	PROP_SESSION
57 };
58 
59 static GtkWidget *get_widget (EFilterRule *fr, ERuleContext *f);
60 
G_DEFINE_TYPE(EMVFolderEditorRule,em_vfolder_editor_rule,EM_TYPE_VFOLDER_RULE)61 G_DEFINE_TYPE (
62 	EMVFolderEditorRule,
63 	em_vfolder_editor_rule,
64 	EM_TYPE_VFOLDER_RULE)
65 
66 static void
67 vfolder_editor_rule_set_session (EMVFolderEditorRule *rule,
68                           EMailSession *session)
69 {
70 	if (session == NULL) {
71 		EShell *shell;
72 		EShellBackend *shell_backend;
73 		EMailBackend *backend;
74 
75 		shell = e_shell_get_default ();
76 		shell_backend = e_shell_get_backend_by_name (shell, "mail");
77 
78 		backend = E_MAIL_BACKEND (shell_backend);
79 		session = e_mail_backend_get_session (backend);
80 	}
81 
82 	g_return_if_fail (E_IS_MAIL_SESSION (session));
83 	g_return_if_fail (rule->priv->session == NULL);
84 
85 	rule->priv->session = g_object_ref (session);
86 }
87 
88 static void
vfolder_editor_rule_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)89 vfolder_editor_rule_set_property (GObject *object,
90                            guint property_id,
91                            const GValue *value,
92                            GParamSpec *pspec)
93 {
94 	switch (property_id) {
95 		case PROP_SESSION:
96 			vfolder_editor_rule_set_session (
97 				EM_VFOLDER_EDITOR_RULE (object),
98 				g_value_get_object (value));
99 			return;
100 	}
101 
102 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
103 }
104 
105 static void
vfolder_editor_rule_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)106 vfolder_editor_rule_get_property (GObject *object,
107                            guint property_id,
108                            GValue *value,
109                            GParamSpec *pspec)
110 {
111 	switch (property_id) {
112 		case PROP_SESSION:
113 			g_value_set_object (
114 				value,
115 				em_vfolder_editor_rule_get_session (
116 				EM_VFOLDER_EDITOR_RULE (object)));
117 			return;
118 	}
119 
120 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
121 }
122 
123 static void
vfolder_editor_rule_dispose(GObject * object)124 vfolder_editor_rule_dispose (GObject *object)
125 {
126 	EMVFolderEditorRulePrivate *priv;
127 
128 	priv = EM_VFOLDER_EDITOR_RULE_GET_PRIVATE (object);
129 	g_clear_object (&priv->session);
130 
131 	/* Chain up to parent's dispose() method. */
132 	G_OBJECT_CLASS (em_vfolder_editor_rule_parent_class)->dispose (object);
133 }
134 
135 static void
vfolder_editor_rule_finalize(GObject * object)136 vfolder_editor_rule_finalize (GObject *object)
137 {
138 	/* EMVFolderEditorRule *rule = EM_VFOLDER_EDITOR_RULE (object); */
139 
140 	/* Chain up to parent's finalize() method. */
141 	G_OBJECT_CLASS (em_vfolder_editor_rule_parent_class)->finalize (object);
142 }
143 
144 static void
em_vfolder_editor_rule_class_init(EMVFolderEditorRuleClass * class)145 em_vfolder_editor_rule_class_init (EMVFolderEditorRuleClass *class)
146 {
147 	GObjectClass *object_class;
148 	EFilterRuleClass *filter_rule_class;
149 
150 	g_type_class_add_private (class, sizeof (EMVFolderEditorRulePrivate));
151 
152 	object_class = G_OBJECT_CLASS (class);
153 	object_class->set_property = vfolder_editor_rule_set_property;
154 	object_class->get_property = vfolder_editor_rule_get_property;
155 	object_class->dispose = vfolder_editor_rule_dispose;
156 	object_class->finalize = vfolder_editor_rule_finalize;
157 
158 	filter_rule_class = E_FILTER_RULE_CLASS (class);
159 	filter_rule_class->get_widget = get_widget;
160 
161 	g_object_class_install_property (
162 		object_class,
163 		PROP_SESSION,
164 		g_param_spec_object (
165 			"session",
166 			NULL,
167 			NULL,
168 			E_TYPE_MAIL_SESSION,
169 			G_PARAM_READWRITE |
170 			G_PARAM_CONSTRUCT_ONLY));
171 }
172 
173 static void
em_vfolder_editor_rule_init(EMVFolderEditorRule * rule)174 em_vfolder_editor_rule_init (EMVFolderEditorRule *rule)
175 {
176 	rule->priv = EM_VFOLDER_EDITOR_RULE_GET_PRIVATE (rule);
177 }
178 
179 EFilterRule *
em_vfolder_editor_rule_new(EMailSession * session)180 em_vfolder_editor_rule_new (EMailSession *session)
181 {
182 	g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
183 
184 	return g_object_new (
185 		EM_TYPE_VFOLDER_EDITOR_RULE, "session", session, NULL);
186 }
187 
188 EMailSession *
em_vfolder_editor_rule_get_session(EMVFolderEditorRule * rule)189 em_vfolder_editor_rule_get_session (EMVFolderEditorRule *rule)
190 {
191 	g_return_val_if_fail (EM_IS_VFOLDER_RULE (rule), NULL);
192 
193 	return rule->priv->session;
194 }
195 
196 enum {
197 	BUTTON_ADD,
198 	BUTTON_REMOVE,
199 	BUTTON_LAST
200 };
201 
202 struct _source_data {
203 	ERuleContext *rc;
204 	EMVFolderRule *vr;
205 	GtkListStore *model;
206 	GtkTreeView *tree_view;
207 	GtkWidget *source_selector;
208 	GtkWidget *buttons[BUTTON_LAST];
209 };
210 
211 static void
source_data_free(gpointer ptr)212 source_data_free (gpointer ptr)
213 {
214 	struct _source_data *sd = ptr;
215 
216 	if (sd) {
217 		g_clear_object (&sd->model);
218 		g_free (sd);
219 	}
220 }
221 
222 static void
set_sensitive(struct _source_data * data)223 set_sensitive (struct _source_data *data)
224 {
225 	GtkTreeSelection *selection;
226 
227 	selection = gtk_tree_view_get_selection (data->tree_view);
228 
229 	gtk_widget_set_sensitive (
230 		GTK_WIDGET (data->buttons[BUTTON_ADD]), TRUE);
231 	gtk_widget_set_sensitive (
232 		GTK_WIDGET (data->buttons[BUTTON_REMOVE]),
233 		selection && gtk_tree_selection_count_selected_rows (selection) > 0);
234 }
235 
236 static void
selection_changed_cb(GtkTreeSelection * selection,struct _source_data * data)237 selection_changed_cb (GtkTreeSelection *selection,
238                       struct _source_data *data)
239 {
240 	set_sensitive (data);
241 }
242 
243 static void
select_source_with_changed(GtkWidget * widget,struct _source_data * data)244 select_source_with_changed (GtkWidget *widget,
245                             struct _source_data *data)
246 {
247 	em_vfolder_rule_with_t with;
248 
249 	with = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
250 	if (with > EM_VFOLDER_RULE_WITH_LOCAL)
251 		with = 0;
252 
253 	with = 3 - with;
254 
255 	gtk_widget_set_sensitive (data->source_selector, !with);
256 
257 	em_vfolder_rule_set_with (data->vr, with);
258 }
259 
260 static void
autoupdate_toggled_cb(GtkToggleButton * toggle,struct _source_data * data)261 autoupdate_toggled_cb (GtkToggleButton *toggle,
262                        struct _source_data *data)
263 {
264 	em_vfolder_rule_set_autoupdate (data->vr, gtk_toggle_button_get_active (toggle));
265 }
266 
267 static void
include_subfolders_toggled_cb(GtkCellRendererToggle * cell_renderer,const gchar * path_string,struct _source_data * data)268 include_subfolders_toggled_cb (GtkCellRendererToggle *cell_renderer,
269                                const gchar *path_string,
270                                struct _source_data *data)
271 {
272 	GtkTreeModel *model;
273 	GtkTreePath *path;
274 	GtkTreeIter iter;
275 
276 	gtk_cell_renderer_toggle_set_active (
277 		cell_renderer,
278 		!gtk_cell_renderer_toggle_get_active (cell_renderer));
279 
280 	model = gtk_tree_view_get_model (data->tree_view);
281 	path = gtk_tree_path_new_from_string (path_string);
282 
283 	if (gtk_tree_model_get_iter (model, &iter, path)) {
284 		gchar *source = NULL;
285 
286 		gtk_list_store_set (
287 			GTK_LIST_STORE (model), &iter,
288 			2, gtk_cell_renderer_toggle_get_active (cell_renderer),
289 			-1);
290 
291 		gtk_tree_model_get (model, &iter, 1, &source, -1);
292 		if (source) {
293 			em_vfolder_rule_source_set_include_subfolders (
294 				data->vr, source,
295 				gtk_cell_renderer_toggle_get_active (cell_renderer));
296 			g_free (source);
297 		}
298 	}
299 
300 	gtk_tree_path_free (path);
301 }
302 
303 static void
vfr_folder_response(EMFolderSelector * selector,gint button,struct _source_data * data)304 vfr_folder_response (EMFolderSelector *selector,
305                      gint button,
306                      struct _source_data *data)
307 {
308 	EMFolderTreeModel *model;
309 	EMFolderTree *folder_tree;
310 	CamelSession *session;
311 	GList *selected_uris;
312 
313 	folder_tree = em_folder_selector_get_folder_tree (selector);
314 	model = em_folder_selector_get_model (selector);
315 	session = CAMEL_SESSION (em_folder_tree_model_get_session (model));
316 
317 	selected_uris = em_folder_tree_get_selected_uris (folder_tree);
318 
319 	if (button == GTK_RESPONSE_OK && selected_uris != NULL) {
320 		GList *uris_iter;
321 		GHashTable *known_uris;
322 		GtkTreeIter iter;
323 		GtkTreeSelection *selection;
324 		gboolean changed = FALSE;
325 
326 		selection = gtk_tree_view_get_selection (data->tree_view);
327 		gtk_tree_selection_unselect_all (selection);
328 
329 		known_uris = g_hash_table_new_full (
330 			(GHashFunc) g_str_hash,
331 			(GEqualFunc) g_str_equal,
332 			(GDestroyNotify) g_free,
333 			(GDestroyNotify) NULL);
334 
335 		if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (data->model), &iter)) {
336 			GtkTreeModel *model = GTK_TREE_MODEL (data->model);
337 			do {
338 				gchar *known = NULL;
339 
340 				gtk_tree_model_get (model, &iter, 1, &known, -1);
341 
342 				if (known)
343 					g_hash_table_add (known_uris, known);
344 			} while (gtk_tree_model_iter_next (model, &iter));
345 		}
346 
347 		for (uris_iter = selected_uris; uris_iter != NULL; uris_iter = uris_iter->next) {
348 			const gchar *uri = uris_iter->data;
349 			gchar *markup;
350 
351 			if (uri == NULL)
352 				continue;
353 
354 			if (g_hash_table_contains (known_uris, uri))
355 				continue;
356 
357 			g_hash_table_add (known_uris, g_strdup (uri));
358 
359 			changed = TRUE;
360 			g_queue_push_tail (em_vfolder_rule_get_sources (data->vr), g_strdup (uri));
361 
362 			markup = e_mail_folder_uri_to_markup (session, uri, NULL);
363 
364 			gtk_list_store_append (data->model, &iter);
365 			gtk_list_store_set (data->model, &iter, 0, markup, 1, uri, -1);
366 			g_free (markup);
367 
368 			/* select all newly added folders */
369 			gtk_tree_selection_select_iter (selection, &iter);
370 		}
371 
372 		g_hash_table_destroy (known_uris);
373 		if (changed)
374 			em_vfolder_rule_sources_changed (data->vr);
375 
376 		set_sensitive (data);
377 	}
378 
379 	gtk_widget_destroy (GTK_WIDGET (selector));
380 	g_list_free_full (selected_uris, g_free);
381 }
382 
383 static void
source_add(GtkWidget * widget,struct _source_data * data)384 source_add (GtkWidget *widget,
385             struct _source_data *data)
386 {
387 	EMFolderTree *folder_tree;
388 	EMFolderTreeModel *model;
389 	EMFolderSelector *selector;
390 	GtkTreeSelection *selection;
391 	GtkWidget *dialog;
392 	gpointer parent;
393 
394 	parent = gtk_widget_get_toplevel (widget);
395 	parent = gtk_widget_is_toplevel (parent) ? parent : NULL;
396 
397 	model = em_folder_tree_model_get_default ();
398 
399 	dialog = em_folder_selector_new (parent, model);
400 
401 	gtk_window_set_title (GTK_WINDOW (dialog), _("Add Folder"));
402 
403 	selector = EM_FOLDER_SELECTOR (dialog);
404 	em_folder_selector_set_can_create (selector, TRUE);
405 	em_folder_selector_set_default_button_label (selector, _("_Add"));
406 
407 	folder_tree = em_folder_selector_get_folder_tree (selector);
408 
409 	em_folder_tree_set_excluded (folder_tree, EMFT_EXCLUDE_NOSELECT);
410 
411 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree));
412 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
413 
414 	g_signal_connect (
415 		dialog, "response",
416 		G_CALLBACK (vfr_folder_response), data);
417 
418 	gtk_widget_show (dialog);
419 }
420 
421 static void
source_remove(GtkWidget * widget,struct _source_data * data)422 source_remove (GtkWidget *widget,
423                struct _source_data *data)
424 {
425 	GtkTreeSelection *selection;
426 	const gchar *source, *prev_source;
427 	GtkTreePath *path;
428 	GtkTreeIter iter;
429 	GHashTable *to_remove;
430 	gint index = 0, first_selected = -1, removed;
431 	gint n;
432 
433 	selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->tree_view));
434 	to_remove = g_hash_table_new (g_direct_hash, g_direct_equal);
435 
436 	source = NULL;
437 	while ((source = em_vfolder_rule_next_source (data->vr, source))) {
438 		path = gtk_tree_path_new ();
439 		gtk_tree_path_append_index (path, index);
440 
441 		if (gtk_tree_selection_path_is_selected (selection, path)) {
442 			g_hash_table_add (to_remove, GINT_TO_POINTER (index));
443 
444 			if (first_selected == -1)
445 				first_selected = index;
446 		}
447 
448 		index++;
449 
450 		gtk_tree_path_free (path);
451 	}
452 
453 	/* do not depend on selection when removing */
454 	gtk_tree_selection_unselect_all (selection);
455 
456 	index = 0;
457 	source = NULL;
458 	removed = 0;
459 	prev_source = NULL;
460 	while ((source = em_vfolder_rule_next_source (data->vr, source))) {
461 		if (g_hash_table_contains (to_remove, GINT_TO_POINTER (index + removed))) {
462 			path = gtk_tree_path_new ();
463 			gtk_tree_path_append_index (path, index);
464 			gtk_tree_model_get_iter (
465 				GTK_TREE_MODEL (data->model), &iter, path);
466 
467 			em_vfolder_rule_remove_source (data->vr, source);
468 			gtk_list_store_remove (data->model, &iter);
469 			gtk_tree_path_free (path);
470 
471 			/* try again from the previous source */
472 			removed++;
473 			source = prev_source;
474 		} else {
475 			index++;
476 			prev_source = source;
477 		}
478 	}
479 
480 	g_hash_table_destroy (to_remove);
481 
482 	/* now select the next rule */
483 	n = gtk_tree_model_iter_n_children (
484 		GTK_TREE_MODEL (data->model), NULL);
485 	index = first_selected >= n ? n - 1 : first_selected;
486 
487 	if (index >= 0) {
488 		path = gtk_tree_path_new ();
489 		gtk_tree_path_append_index (path, index);
490 		if (gtk_tree_model_get_iter (GTK_TREE_MODEL (data->model), &iter, path)) {
491 			gtk_tree_selection_select_iter (selection, &iter);
492 			gtk_tree_view_set_cursor (data->tree_view, path, NULL, FALSE);
493 		}
494 		gtk_tree_path_free (path);
495 	}
496 
497 	set_sensitive (data);
498 }
499 
500 static GtkWidget *
get_widget(EFilterRule * fr,ERuleContext * rc)501 get_widget (EFilterRule *fr,
502             ERuleContext *rc)
503 {
504 	EMVFolderRule *vr = (EMVFolderRule *) fr;
505 	EMailSession *session;
506 	GtkWidget *widget, *frame, *label, *combobox, *hgrid, *vgrid, *tree_view, *scrolled_window;
507 	GtkWidget *autoupdate;
508 	GtkListStore *model;
509 	GtkCellRenderer *renderer;
510 	GtkTreeViewColumn *column;
511 	struct _source_data *data;
512 	const gchar *source;
513 	gchar *tmp;
514 	GtkTreeIter iter;
515 	GtkTreeSelection *selection;
516 
517 	widget = E_FILTER_RULE_CLASS (em_vfolder_editor_rule_parent_class)->
518 		get_widget (fr, rc);
519 
520 	data = g_malloc0 (sizeof (*data));
521 	data->rc = rc;
522 	data->vr = vr;
523 
524 	frame = gtk_grid_new ();
525 	gtk_orientable_set_orientation (GTK_ORIENTABLE (frame), GTK_ORIENTATION_VERTICAL);
526 	gtk_grid_set_row_spacing (GTK_GRID (frame), 6);
527 
528 	g_object_set_data_full (G_OBJECT (frame), "data", data, source_data_free);
529 
530 	tmp = g_strdup_printf ("<b>%s</b>", _("Search Folder Sources"));
531 	label = gtk_label_new (tmp);
532 	g_free (tmp);
533 	g_object_set (
534 		G_OBJECT (label),
535 		"use-markup", TRUE,
536 		"xalign", 0.0,
537 		NULL);
538 
539 	gtk_container_add (GTK_CONTAINER (frame), label);
540 
541 	hgrid = gtk_grid_new ();
542 	gtk_orientable_set_orientation (GTK_ORIENTABLE (hgrid), GTK_ORIENTATION_HORIZONTAL);
543 	gtk_container_add (GTK_CONTAINER (frame), hgrid);
544 
545 	label = gtk_label_new ("    ");
546 	gtk_container_add (GTK_CONTAINER (hgrid), label);
547 
548 	vgrid = gtk_grid_new ();
549 	g_object_set (
550 		G_OBJECT (vgrid),
551 		"orientation", GTK_ORIENTATION_VERTICAL,
552 		"border-width", 6,
553 		"row-spacing", 6,
554 		NULL);
555 	gtk_container_add (GTK_CONTAINER (hgrid), vgrid);
556 
557 	hgrid = gtk_grid_new ();
558 	gtk_orientable_set_orientation (GTK_ORIENTABLE (hgrid), GTK_ORIENTATION_HORIZONTAL);
559 	gtk_grid_set_column_spacing (GTK_GRID (hgrid), 6);
560 	gtk_container_add (GTK_CONTAINER (vgrid), hgrid);
561 
562 	autoupdate = gtk_check_button_new_with_mnemonic (_("Automatically update on any _source folder change"));
563 	gtk_container_add (GTK_CONTAINER (hgrid), autoupdate);
564 
565 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (autoupdate), em_vfolder_rule_get_autoupdate (vr));
566 	g_signal_connect (autoupdate, "toggled", G_CALLBACK (autoupdate_toggled_cb), data);
567 
568 	hgrid = gtk_grid_new ();
569 	gtk_orientable_set_orientation (GTK_ORIENTABLE (hgrid), GTK_ORIENTATION_HORIZONTAL);
570 	gtk_grid_set_column_spacing (GTK_GRID (hgrid), 6);
571 	gtk_container_add (GTK_CONTAINER (vgrid), hgrid);
572 
573 	combobox = gtk_combo_box_text_new ();
574 	gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combobox), NULL, _("All local folders"));
575 	gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combobox), NULL, _("All active remote folders"));
576 	gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combobox), NULL, _("All local and active remote folders"));
577 	gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (combobox), NULL, _("Specific folders"));
578 	gtk_container_add (GTK_CONTAINER (hgrid), combobox);
579 
580 	hgrid = gtk_grid_new ();
581 	gtk_orientable_set_orientation (GTK_ORIENTABLE (hgrid), GTK_ORIENTATION_HORIZONTAL);
582 	gtk_grid_set_column_spacing (GTK_GRID (hgrid), 6);
583 	gtk_container_add (GTK_CONTAINER (vgrid), hgrid);
584 
585 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
586 	g_object_set (
587 		G_OBJECT (scrolled_window),
588 		"hscrollbar-policy", GTK_POLICY_AUTOMATIC,
589 		"vscrollbar-policy", GTK_POLICY_AUTOMATIC,
590 		"shadow-type", GTK_SHADOW_IN,
591 		"halign", GTK_ALIGN_FILL,
592 		"hexpand", TRUE,
593 		"valign", GTK_ALIGN_FILL,
594 		"vexpand", TRUE,
595 		NULL);
596 	gtk_container_add (GTK_CONTAINER (hgrid), scrolled_window);
597 
598 	model = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN);
599 	renderer = gtk_cell_renderer_text_new ();
600 	tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
601 	gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
602 	gtk_tree_view_insert_column_with_attributes (
603 		GTK_TREE_VIEW (tree_view),
604 		-1, "column", renderer, "markup", 0, NULL);
605 
606 	renderer = gtk_cell_renderer_toggle_new ();
607 	column = gtk_tree_view_column_new_with_attributes (
608 		"include subfolders", renderer, "active", 2, NULL);
609 	g_signal_connect (renderer, "toggled", G_CALLBACK (include_subfolders_toggled_cb), data);
610 
611 	renderer = gtk_cell_renderer_text_new ();
612 	g_object_set (
613 		G_OBJECT (renderer),
614 		"editable", FALSE,
615 		"text", _("include subfolders"),
616 		NULL);
617 	gtk_tree_view_column_pack_start (column, renderer, TRUE);
618 	gtk_tree_view_insert_column (GTK_TREE_VIEW (tree_view), column, -1);
619 
620 	column = gtk_tree_view_get_column (GTK_TREE_VIEW (tree_view), 0);
621 	gtk_tree_view_column_set_expand (column, TRUE);
622 
623 	gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), tree_view);
624 
625 	vgrid = gtk_grid_new ();
626 	g_object_set (
627 		G_OBJECT (vgrid),
628 		"orientation", GTK_ORIENTATION_VERTICAL,
629 		"border-width", 6,
630 		"row-spacing", 6,
631 		NULL);
632 	gtk_container_add (GTK_CONTAINER (hgrid), vgrid);
633 
634 	data->buttons[BUTTON_ADD] = e_dialog_button_new_with_icon ("list-add", _("_Add"));
635 	g_signal_connect (
636 		data->buttons[BUTTON_ADD], "clicked",
637 		G_CALLBACK (source_add), data);
638 
639 	data->buttons[BUTTON_REMOVE] = e_dialog_button_new_with_icon ("list-remove", _("_Remove"));
640 	g_signal_connect (
641 		data->buttons[BUTTON_REMOVE], "clicked",
642 		G_CALLBACK (source_remove), data);
643 
644 	gtk_container_add (GTK_CONTAINER (vgrid), data->buttons[BUTTON_ADD]);
645 	gtk_container_add (GTK_CONTAINER (vgrid), data->buttons[BUTTON_REMOVE]);
646 
647 	data->tree_view = GTK_TREE_VIEW (tree_view);
648 	data->model = model;
649 
650 	session = em_vfolder_editor_context_get_session (EM_VFOLDER_EDITOR_CONTEXT (rc));
651 
652 	source = NULL;
653 	while ((source = em_vfolder_rule_next_source (vr, source))) {
654 		gchar *markup;
655 
656 		markup = e_mail_folder_uri_to_markup (
657 			CAMEL_SESSION (session), source, NULL);
658 
659 		gtk_list_store_append (data->model, &iter);
660 		gtk_list_store_set (
661 			data->model, &iter,
662 			0, markup,
663 			1, source,
664 			2, em_vfolder_rule_source_get_include_subfolders (vr, source),
665 			-1);
666 		g_free (markup);
667 	}
668 
669 	selection = gtk_tree_view_get_selection (data->tree_view);
670 	gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
671 
672 	g_signal_connect (
673 		selection, "changed",
674 		G_CALLBACK (selection_changed_cb), data);
675 
676 	data->source_selector = hgrid;
677 
678 	gtk_combo_box_set_active (GTK_COMBO_BOX (combobox), 3 - em_vfolder_rule_get_with (vr));
679 	g_signal_connect (
680 		combobox, "changed",
681 		G_CALLBACK (select_source_with_changed), data);
682 	select_source_with_changed (combobox, data);
683 
684 	set_sensitive (data);
685 
686 	gtk_widget_set_valign (frame, GTK_ALIGN_FILL);
687 	gtk_widget_set_vexpand (frame, TRUE);
688 	gtk_widget_show_all (frame);
689 
690 	gtk_container_add (GTK_CONTAINER (widget), frame);
691 
692 	return widget;
693 }
694