Bug 795870 - Add a way to initiate refresh of account sources
[evolution.git] / src / shell / e-shell-content.c
blob5dc46c5132c1d6671b8b551fce3af38a59893e43
1 /*
2 * e-shell-content.c
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.
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.
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/>.
17 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 /**
22 * SECTION: e-shell-content
23 * @short_description: the right side of the main window
24 * @include: shell/e-shell-content.h
25 **/
27 #include "evolution-config.h"
29 #include "e-shell-content.h"
31 #include <glib/gi18n.h>
32 #include <libebackend/libebackend.h>
34 #include "e-shell-backend.h"
35 #include "e-shell-searchbar.h"
36 #include "e-shell-view.h"
37 #include "e-shell-window-actions.h"
39 #define E_SHELL_CONTENT_GET_PRIVATE(obj) \
40 (G_TYPE_INSTANCE_GET_PRIVATE \
41 ((obj), E_TYPE_SHELL_CONTENT, EShellContentPrivate))
43 struct _EShellContentPrivate {
45 gpointer shell_view; /* weak pointer */
47 GtkWidget *alert_bar;
48 GtkWidget *searchbar; /* not referenced */
50 /* Custom search rules. */
51 gchar *user_filename;
54 enum {
55 PROP_0,
56 PROP_ALERT_BAR,
57 PROP_SHELL_VIEW
60 /* Forward Declarations */
61 static void e_shell_content_alert_sink_init
62 (EAlertSinkInterface *iface);
64 G_DEFINE_TYPE_WITH_CODE (
65 EShellContent,
66 e_shell_content,
67 GTK_TYPE_BIN,
68 G_IMPLEMENT_INTERFACE (
69 E_TYPE_ALERT_SINK, e_shell_content_alert_sink_init)
70 G_IMPLEMENT_INTERFACE (
71 E_TYPE_EXTENSIBLE, NULL));
73 static void
74 shell_content_dialog_rule_changed (GtkWidget *dialog,
75 EFilterRule *rule)
77 gboolean sensitive;
79 sensitive = (rule != NULL) && (rule->parts != NULL);
81 gtk_dialog_set_response_sensitive (
82 GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive);
83 gtk_dialog_set_response_sensitive (
84 GTK_DIALOG (dialog), GTK_RESPONSE_APPLY, sensitive);
87 static void
88 shell_content_set_shell_view (EShellContent *shell_content,
89 EShellView *shell_view)
91 g_return_if_fail (shell_content->priv->shell_view == NULL);
93 shell_content->priv->shell_view = shell_view;
95 g_object_add_weak_pointer (
96 G_OBJECT (shell_view),
97 &shell_content->priv->shell_view);
100 static void
101 shell_content_set_property (GObject *object,
102 guint property_id,
103 const GValue *value,
104 GParamSpec *pspec)
106 switch (property_id) {
107 case PROP_SHELL_VIEW:
108 shell_content_set_shell_view (
109 E_SHELL_CONTENT (object),
110 g_value_get_object (value));
111 return;
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
117 static void
118 shell_content_get_property (GObject *object,
119 guint property_id,
120 GValue *value,
121 GParamSpec *pspec)
123 switch (property_id) {
124 case PROP_ALERT_BAR:
125 g_value_set_object (
126 value, e_shell_content_get_alert_bar (
127 E_SHELL_CONTENT (object)));
128 return;
130 case PROP_SHELL_VIEW:
131 g_value_set_object (
132 value, e_shell_content_get_shell_view (
133 E_SHELL_CONTENT (object)));
134 return;
137 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
140 static void
141 shell_content_dispose (GObject *object)
143 EShellContentPrivate *priv;
145 priv = E_SHELL_CONTENT_GET_PRIVATE (object);
147 if (priv->shell_view != NULL) {
148 g_object_remove_weak_pointer (
149 G_OBJECT (priv->shell_view), &priv->shell_view);
150 priv->shell_view = NULL;
153 if (priv->alert_bar) {
154 gtk_widget_unparent (priv->alert_bar);
155 g_clear_object (&priv->alert_bar);
158 /* Chain up to parent's dispose() method. */
159 G_OBJECT_CLASS (e_shell_content_parent_class)->dispose (object);
162 static void
163 shell_content_finalize (GObject *object)
165 EShellContentPrivate *priv;
167 priv = E_SHELL_CONTENT_GET_PRIVATE (object);
169 g_free (priv->user_filename);
171 /* Chain up to parent's finalize() method. */
172 G_OBJECT_CLASS (e_shell_content_parent_class)->finalize (object);
175 static void
176 shell_content_constructed (GObject *object)
178 EShellContent *shell_content;
179 EShellBackend *shell_backend;
180 EShellView *shell_view;
181 GtkWidget *widget;
182 const gchar *config_dir;
184 shell_content = E_SHELL_CONTENT (object);
185 shell_view = e_shell_content_get_shell_view (shell_content);
186 shell_backend = e_shell_view_get_shell_backend (shell_view);
188 widget = e_alert_bar_new ();
189 gtk_widget_set_parent (widget, GTK_WIDGET (shell_content));
190 shell_content->priv->alert_bar = g_object_ref_sink (widget);
191 /* EAlertBar controls its own visibility. */
193 /* XXX Regenerate the filename for custom saved search as done
194 * in shell_view_init_search_context(). ERuleContext ought
195 * to remember the filename when loading rules so you don't
196 * have to keep passing it in when saving rules. */
197 config_dir = e_shell_backend_get_config_dir (shell_backend);
198 shell_content->priv->user_filename =
199 g_build_filename (config_dir, "searches.xml", NULL);
201 e_extensible_load_extensions (E_EXTENSIBLE (object));
203 /* Chain up to parent's constructed() method. */
204 G_OBJECT_CLASS (e_shell_content_parent_class)->constructed (object);
207 static void
208 shell_content_get_preferred_width (GtkWidget *widget,
209 gint *minimum,
210 gint *natural)
212 EShellContentPrivate *priv;
213 GtkWidget *child;
215 priv = E_SHELL_CONTENT_GET_PRIVATE (widget);
217 *minimum = *natural = 0;
219 child = gtk_bin_get_child (GTK_BIN (widget));
220 gtk_widget_get_preferred_width (child, minimum, natural);
222 if (gtk_widget_get_visible (priv->alert_bar)) {
223 gint child_minimum;
224 gint child_natural;
226 gtk_widget_get_preferred_width (
227 priv->alert_bar, &child_minimum, &child_natural);
229 *minimum = MAX (*minimum, child_minimum);
230 *natural = MAX (*natural, child_natural);
233 if (priv->searchbar != NULL) {
234 gint child_minimum;
235 gint child_natural;
237 gtk_widget_get_preferred_width (
238 priv->searchbar, &child_minimum, &child_natural);
240 *minimum = MAX (*minimum, child_minimum);
241 *natural = MAX (*natural, child_natural);
245 static void
246 shell_content_get_preferred_height (GtkWidget *widget,
247 gint *minimum,
248 gint *natural)
250 EShellContentPrivate *priv;
251 GtkWidget *child;
253 priv = E_SHELL_CONTENT_GET_PRIVATE (widget);
255 child = gtk_bin_get_child (GTK_BIN (widget));
256 gtk_widget_get_preferred_height (child, minimum, natural);
258 if (gtk_widget_get_visible (priv->alert_bar)) {
259 gint child_minimum;
260 gint child_natural;
262 gtk_widget_get_preferred_height (
263 priv->alert_bar, &child_minimum, &child_natural);
265 *minimum += child_minimum;
266 *natural += child_natural;
269 if (priv->searchbar != NULL) {
270 gint child_minimum;
271 gint child_natural;
273 gtk_widget_get_preferred_height (
274 priv->searchbar, &child_minimum, &child_natural);
276 *minimum += child_minimum;
277 *natural += child_natural;
281 static void
282 shell_content_size_allocate (GtkWidget *widget,
283 GtkAllocation *allocation)
285 EShellContentPrivate *priv;
286 GtkAllocation child_allocation;
287 GtkRequisition child_requisition;
288 GtkWidget *child;
289 gint remaining_height;
291 priv = E_SHELL_CONTENT_GET_PRIVATE (widget);
293 remaining_height = allocation->height;
294 gtk_widget_set_allocation (widget, allocation);
296 child_allocation.x = allocation->x;
297 child_allocation.y = allocation->y;
298 child_allocation.width = allocation->width;
300 child_requisition.height = 0;
302 /* Alert bar gets to be as tall as it wants (if visible). */
304 child = priv->alert_bar;
305 child_allocation.y += child_requisition.height;
307 if (gtk_widget_get_visible (child))
308 gtk_widget_get_preferred_height_for_width (
309 child, child_allocation.width,
310 &child_requisition.height, NULL);
311 else
312 child_requisition.height = 0;
314 remaining_height -= child_requisition.height;
315 child_allocation.height = child_requisition.height;
317 if (child_allocation.height > 0)
318 gtk_widget_size_allocate (child, &child_allocation);
320 /* Search bar gets to be as tall as it wants (if we have one). */
322 child = priv->searchbar;
323 child_allocation.y += child_requisition.height;
325 if (child != NULL)
326 gtk_widget_get_preferred_size (child, &child_requisition, NULL);
327 else
328 child_requisition.height = 0;
330 remaining_height -= child_requisition.height;
331 child_allocation.height = child_requisition.height;
333 if (child != NULL)
334 gtk_widget_size_allocate (child, &child_allocation);
336 /* The GtkBin child gets whatever vertical space is left. */
338 child_allocation.y += child_requisition.height;
339 child_allocation.height = remaining_height;
341 child = gtk_bin_get_child (GTK_BIN (widget));
342 if (child != NULL)
343 gtk_widget_size_allocate (child, &child_allocation);
346 static void
347 shell_content_remove (GtkContainer *container,
348 GtkWidget *widget)
350 GtkContainerClass *container_class;
351 EShellContentPrivate *priv;
353 priv = E_SHELL_CONTENT_GET_PRIVATE (container);
355 if (widget == priv->alert_bar) {
356 gtk_widget_unparent (priv->alert_bar);
357 g_clear_object (&priv->alert_bar);
358 return;
361 if (widget == priv->searchbar) {
362 gtk_widget_unparent (priv->searchbar);
363 priv->searchbar = NULL;
364 return;
367 /* Chain up to parent's remove() method. */
368 container_class = GTK_CONTAINER_CLASS (e_shell_content_parent_class);
369 container_class->remove (container, widget);
372 static void
373 shell_content_forall (GtkContainer *container,
374 gboolean include_internals,
375 GtkCallback callback,
376 gpointer callback_data)
378 EShellContentPrivate *priv;
380 priv = E_SHELL_CONTENT_GET_PRIVATE (container);
382 if (priv->alert_bar != NULL)
383 callback (priv->alert_bar, callback_data);
385 if (priv->searchbar != NULL)
386 callback (priv->searchbar, callback_data);
388 /* Chain up to parent's forall() method. */
389 GTK_CONTAINER_CLASS (e_shell_content_parent_class)->forall (
390 container, include_internals, callback, callback_data);
393 static void
394 shell_content_submit_alert (EAlertSink *alert_sink,
395 EAlert *alert)
397 GtkWidget *alert_bar;
399 alert_bar = e_shell_content_get_alert_bar (E_SHELL_CONTENT (alert_sink));
401 e_alert_bar_submit_alert (E_ALERT_BAR (alert_bar), alert);
404 static void
405 e_shell_content_class_init (EShellContentClass *class)
407 GObjectClass *object_class;
408 GtkWidgetClass *widget_class;
409 GtkContainerClass *container_class;
411 g_type_class_add_private (class, sizeof (EShellContentPrivate));
413 object_class = G_OBJECT_CLASS (class);
414 object_class->set_property = shell_content_set_property;
415 object_class->get_property = shell_content_get_property;
416 object_class->dispose = shell_content_dispose;
417 object_class->finalize = shell_content_finalize;
418 object_class->constructed = shell_content_constructed;
420 widget_class = GTK_WIDGET_CLASS (class);
421 widget_class->get_preferred_width = shell_content_get_preferred_width;
422 widget_class->get_preferred_height = shell_content_get_preferred_height;
423 widget_class->size_allocate = shell_content_size_allocate;
425 container_class = GTK_CONTAINER_CLASS (class);
426 container_class->remove = shell_content_remove;
427 container_class->forall = shell_content_forall;
430 * EShellContent:alert-bar
432 * Displays informational and error messages.
434 g_object_class_install_property (
435 object_class,
436 PROP_ALERT_BAR,
437 g_param_spec_object (
438 "alert-bar",
439 "Alert Bar",
440 "Displays informational and error messages",
441 E_TYPE_ALERT_BAR,
442 G_PARAM_READABLE |
443 G_PARAM_STATIC_STRINGS));
446 * EShellContent:shell-view
448 * The #EShellView to which the content widget belongs.
450 g_object_class_install_property (
451 object_class,
452 PROP_SHELL_VIEW,
453 g_param_spec_object (
454 "shell-view",
455 NULL,
456 NULL,
457 E_TYPE_SHELL_VIEW,
458 G_PARAM_READWRITE |
459 G_PARAM_CONSTRUCT_ONLY |
460 G_PARAM_STATIC_STRINGS));
463 static void
464 e_shell_content_alert_sink_init (EAlertSinkInterface *iface)
466 iface->submit_alert = shell_content_submit_alert;
469 static void
470 e_shell_content_init (EShellContent *shell_content)
472 shell_content->priv = E_SHELL_CONTENT_GET_PRIVATE (shell_content);
474 gtk_widget_set_has_window (GTK_WIDGET (shell_content), FALSE);
478 * e_shell_content_new:
479 * @shell_view: an #EShellView
481 * Creates a new #EShellContent instance belonging to @shell_view.
483 * Returns: a new #EShellContent instance
485 GtkWidget *
486 e_shell_content_new (EShellView *shell_view)
488 g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL);
490 return g_object_new (
491 E_TYPE_SHELL_CONTENT, "shell-view", shell_view, NULL);
495 * e_shell_content_set_searchbar:
496 * @shell_content: an #EShellContent
497 * @searchbar: a #GtkWidget, or %NULL
499 * Packs @searchbar at the top of @shell_content.
501 void
502 e_shell_content_set_searchbar (EShellContent *shell_content,
503 GtkWidget *searchbar)
505 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
507 if (searchbar != NULL)
508 g_return_if_fail (GTK_IS_WIDGET (searchbar));
510 if (shell_content->priv->searchbar != NULL)
511 gtk_container_remove (
512 GTK_CONTAINER (shell_content),
513 shell_content->priv->searchbar);
515 shell_content->priv->searchbar = searchbar;
517 if (searchbar != NULL)
518 gtk_widget_set_parent (searchbar, GTK_WIDGET (shell_content));
520 gtk_widget_queue_resize (GTK_WIDGET (shell_content));
524 * e_shell_content_check_state:
525 * @shell_content: an #EShellContent
527 * #EShellContent subclasses should implement the
528 * <structfield>check_state</structfield> method in #EShellContentClass
529 * to return a set of flags describing the current content selection.
530 * Subclasses are responsible for defining their own flags. This is
531 * primarily used to assist shell views with updating actions (see
532 * e_shell_view_update_actions()).
534 * Returns: a set of flags describing the current @shell_content selection
536 guint32
537 e_shell_content_check_state (EShellContent *shell_content)
539 EShellContentClass *shell_content_class;
541 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), 0);
543 shell_content_class = E_SHELL_CONTENT_GET_CLASS (shell_content);
544 g_return_val_if_fail (shell_content_class != NULL, 0);
545 g_return_val_if_fail (shell_content_class->check_state != NULL, 0);
547 return shell_content_class->check_state (shell_content);
551 * e_shell_content_focus_search_results:
552 * @shell_content: an #EShellContent
554 * #EShellContent subclasses should implement the
555 * <structfield>focus_search_results</structfield> method in
556 * #EShellContentClass to direct input focus to the widget
557 * displaying search results. This is usually called during
558 * e_shell_view_execute_search().
560 void
561 e_shell_content_focus_search_results (EShellContent *shell_content)
563 EShellContentClass *shell_content_class;
565 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
567 shell_content_class = E_SHELL_CONTENT_GET_CLASS (shell_content);
568 g_return_if_fail (shell_content_class != NULL);
570 if (shell_content_class->focus_search_results != NULL)
571 shell_content_class->focus_search_results (shell_content);
575 * e_shell_content_get_alert_bar:
576 * @shell_content: an #EShellContent
578 * Returns the #EAlertBar used to display informational and error messages.
580 * Returns: the #EAlertBar for @shell_content
582 GtkWidget *
583 e_shell_content_get_alert_bar (EShellContent *shell_content)
585 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL);
587 return shell_content->priv->alert_bar;
591 * e_shell_content_get_shell_view:
592 * @shell_content: an #EShellContent
594 * Returns the #EShellView that was passed to e_shell_content_new().
596 * Returns: the #EShellView to which @shell_content belongs
598 EShellView *
599 e_shell_content_get_shell_view (EShellContent *shell_content)
601 g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL);
603 return E_SHELL_VIEW (shell_content->priv->shell_view);
606 void
607 e_shell_content_run_advanced_search_dialog (EShellContent *shell_content)
609 EShellView *shell_view;
610 EShellWindow *shell_window;
611 GtkWidget *content_area;
612 GtkWidget *dialog;
613 GtkWidget *widget;
614 EFilterRule *rule;
615 ERuleContext *context;
616 const gchar *user_filename;
617 gulong handler_id;
618 gint response;
619 EAlert *alert = NULL;
621 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
623 shell_view = e_shell_content_get_shell_view (shell_content);
624 shell_window = e_shell_view_get_shell_window (shell_view);
625 user_filename = shell_content->priv->user_filename;
627 rule = e_shell_view_get_search_rule (shell_view);
629 if (rule == NULL)
630 rule = e_filter_rule_new ();
631 else
632 rule = e_filter_rule_clone (rule);
634 context = E_SHELL_VIEW_GET_CLASS (shell_view)->search_context;
635 widget = e_filter_rule_get_widget (rule, context);
636 e_filter_rule_set_source (rule, E_FILTER_SOURCE_INCOMING);
638 dialog = gtk_dialog_new_with_buttons (
639 _("Advanced Search"), GTK_WINDOW (shell_window),
640 GTK_DIALOG_DESTROY_WITH_PARENT,
641 _("_Cancel"), GTK_RESPONSE_CANCEL,
642 _("_Save"), GTK_RESPONSE_APPLY,
643 _("_OK"), GTK_RESPONSE_OK, NULL);
645 gtk_container_set_border_width (GTK_CONTAINER (dialog), 7);
646 gtk_container_set_border_width (GTK_CONTAINER (widget), 3);
647 gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 300);
649 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
650 gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
652 handler_id = g_signal_connect_swapped (
653 rule, "changed", G_CALLBACK (
654 shell_content_dialog_rule_changed), dialog);
656 shell_content_dialog_rule_changed (dialog, rule);
658 run:
659 response = gtk_dialog_run (GTK_DIALOG (dialog));
661 if (response != GTK_RESPONSE_OK && response != GTK_RESPONSE_APPLY)
662 goto exit;
664 if (!e_filter_rule_validate (rule, &alert)) {
665 e_alert_run_dialog (GTK_WINDOW (dialog), alert);
666 g_object_unref (alert);
667 alert = NULL;
668 goto run;
671 e_shell_view_custom_search (shell_view, rule);
673 if (response == GTK_RESPONSE_APPLY) {
674 if (!e_rule_context_find_rule (context, rule->name, rule->source))
675 e_rule_context_add_rule (context, rule);
676 e_rule_context_save (context, user_filename);
677 goto run;
680 exit:
681 g_signal_handler_disconnect (rule, handler_id);
683 g_object_unref (rule);
684 gtk_widget_destroy (dialog);
687 void
688 e_shell_content_run_edit_searches_dialog (EShellContent *shell_content)
690 EShellView *shell_view;
691 ERuleContext *context;
692 ERuleEditor *editor;
693 const gchar *user_filename;
695 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
697 shell_view = e_shell_content_get_shell_view (shell_content);
698 context = E_SHELL_VIEW_GET_CLASS (shell_view)->search_context;
699 user_filename = shell_content->priv->user_filename;
701 editor = e_rule_editor_new (
702 context, E_FILTER_SOURCE_INCOMING, _("Searches"));
703 gtk_window_set_title (GTK_WINDOW (editor), _("Searches"));
705 if (gtk_dialog_run (GTK_DIALOG (editor)) == GTK_RESPONSE_OK)
706 e_rule_context_save (context, user_filename);
708 gtk_widget_destroy (GTK_WIDGET (editor));
711 void
712 e_shell_content_run_save_search_dialog (EShellContent *shell_content)
714 EShellView *shell_view;
715 EShellWindow *shell_window;
716 GtkWidget *content_area;
717 GtkWidget *dialog;
718 GtkWidget *widget;
719 EFilterRule *rule;
720 ERuleContext *context;
721 const gchar *user_filename;
722 gchar *search_name;
723 gulong handler_id;
724 gint response;
725 EAlert *alert = NULL;
727 g_return_if_fail (E_IS_SHELL_CONTENT (shell_content));
729 shell_view = e_shell_content_get_shell_view (shell_content);
730 shell_window = e_shell_view_get_shell_window (shell_view);
731 user_filename = shell_content->priv->user_filename;
733 rule = e_shell_view_get_search_rule (shell_view);
734 g_return_if_fail (E_IS_FILTER_RULE (rule));
735 rule = e_filter_rule_clone (rule);
737 search_name = e_shell_view_get_search_name (shell_view);
738 e_filter_rule_set_name (rule, search_name);
739 g_free (search_name);
741 context = E_SHELL_VIEW_GET_CLASS (shell_view)->search_context;
742 widget = e_filter_rule_get_widget (rule, context);
743 e_filter_rule_set_source (rule, E_FILTER_SOURCE_INCOMING);
745 dialog = gtk_dialog_new_with_buttons (
746 _("Save Search"), GTK_WINDOW (shell_window),
747 GTK_DIALOG_DESTROY_WITH_PARENT,
748 _("_Cancel"), GTK_RESPONSE_CANCEL,
749 _("_OK"), GTK_RESPONSE_OK, NULL);
751 gtk_container_set_border_width (GTK_CONTAINER (dialog), 7);
752 gtk_container_set_border_width (GTK_CONTAINER (widget), 3);
753 gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 300);
755 content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
756 gtk_box_pack_start (GTK_BOX (content_area), widget, TRUE, TRUE, 0);
758 handler_id = g_signal_connect_swapped (
759 rule, "changed", G_CALLBACK (
760 shell_content_dialog_rule_changed), dialog);
762 shell_content_dialog_rule_changed (dialog, rule);
764 run:
765 response = gtk_dialog_run (GTK_DIALOG (dialog));
767 if (response != GTK_RESPONSE_OK)
768 goto exit;
770 if (!e_filter_rule_validate (rule, &alert)) {
771 e_alert_run_dialog (GTK_WINDOW (dialog), alert);
772 g_object_unref (alert);
773 alert = NULL;
774 goto run;
777 /* XXX This function steals the rule reference, so
778 * counteract that by referencing it again. */
779 e_rule_context_add_rule (context, g_object_ref (rule));
781 e_rule_context_save (context, user_filename);
783 exit:
784 g_signal_handler_disconnect (rule, handler_id);
786 g_object_unref (rule);
787 gtk_widget_destroy (dialog);