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 }