evolution-3.6.4/mail/em-vfolder-editor-rule.c

No issues found

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