diff options
Diffstat (limited to 'shell/e-shell-content.c')
-rw-r--r-- | shell/e-shell-content.c | 1559 |
1 files changed, 1559 insertions, 0 deletions
diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c new file mode 100644 index 0000000000..2fc9569bd2 --- /dev/null +++ b/shell/e-shell-content.c @@ -0,0 +1,1559 @@ +/* + * e-shell-content.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-shell-content.h" + +#include <glib/gi18n.h> + +#include "e-util/e-binding.h" +#include "e-util/e-util.h" +#include "filter/rule-editor.h" +#include "widgets/misc/e-action-combo-box.h" +#include "widgets/misc/e-hinted-entry.h" + +#include "e-shell-backend.h" +#include "e-shell-view.h" +#include "e-shell-window-actions.h" + +#define E_SHELL_CONTENT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SHELL_CONTENT, EShellContentPrivate)) + +#define STATE_KEY_SEARCH_FILTER "SearchFilter" +#define STATE_KEY_SEARCH_SCOPE "SearchScope" +#define STATE_KEY_SEARCH_TEXT "SearchText" + +struct _EShellContentPrivate { + + gpointer shell_view; /* weak pointer */ + + RuleContext *search_context; + FilterRule *search_rule; + gchar *system_filename; + gchar *user_filename; + + /* Container for the following widgets */ + GtkWidget *search_bar; + + /* Search bar widgets */ + GtkWidget *filter_label; + GtkWidget *filter_combo_box; + GtkWidget *search_label; + GtkWidget *search_entry; + GtkWidget *scope_label; + GtkWidget *scope_combo_box; +}; + +enum { + PROP_0, + PROP_FILTER_ACTION, + PROP_FILTER_VALUE, + PROP_FILTER_VISIBLE, + PROP_SEARCH_CONTEXT, + PROP_SEARCH_HINT, + PROP_SEARCH_RULE, + PROP_SEARCH_TEXT, + PROP_SEARCH_VISIBLE, + PROP_SCOPE_ACTION, + PROP_SCOPE_VALUE, + PROP_SCOPE_VISIBLE, + PROP_SHELL_VIEW +}; + +static gpointer parent_class; + +static void +shell_content_dialog_rule_changed (GtkWidget *dialog, + FilterRule *rule) +{ + gboolean sensitive; + + sensitive = (rule != NULL) && (rule->parts != NULL); + + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (dialog), GTK_RESPONSE_OK, sensitive); + gtk_dialog_set_response_sensitive ( + GTK_DIALOG (dialog), GTK_RESPONSE_APPLY, sensitive); +} + +static void +action_search_execute_cb (GtkAction *action, + EShellContent *shell_content) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkWidget *widget; + const gchar *search_text; + + /* EShellView subclasses are responsible for actually + * executing the search. This is all cosmetic stuff. */ + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + if (!e_shell_view_is_active (shell_view)) + return; + + widget = shell_content->priv->search_entry; + + search_text = e_shell_content_get_search_text (shell_content); + + if (search_text != NULL && *search_text != '\0') { + GtkStyle *style; + const GdkColor *color; + + style = gtk_widget_get_style (widget); + color = &style->base[GTK_STATE_SELECTED]; + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, color); + + style = gtk_widget_get_style (widget); + color = &style->text[GTK_STATE_SELECTED]; + gtk_widget_modify_text (widget, GTK_STATE_NORMAL, color); + } else { + /* Text color will be updated when we move the focus. */ + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, NULL); + } + + action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window); + gtk_action_set_sensitive (action, TRUE); + + action = E_SHELL_WINDOW_ACTION_SEARCH_SAVE (shell_window); + gtk_action_set_sensitive (action, TRUE); + + /* Direct the focus away from the search entry, so that a + * focus-in event is required before the text can be changed. + * This will reset the entry to the appropriate visual state. */ + widget = gtk_bin_get_child (GTK_BIN (shell_content)); + if (GTK_IS_PANED (widget)) + widget = gtk_paned_get_child1 (GTK_PANED (widget)); + gtk_widget_grab_focus (widget); +} + +static void +shell_content_entry_activated_cb (EShellContent *shell_content, + GtkWidget *entry) +{ + EShellWindow *shell_window; + EShellView *shell_view; + GtkAction *action; + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + /* Verify the shell view is active before proceeding. */ + if (!e_shell_view_is_active (shell_view)) + return; + + action = E_SHELL_WINDOW_ACTION_SEARCH_EXECUTE (shell_window); + gtk_action_activate (action); +} + +static void +shell_content_entry_icon_press_cb (EShellContent *shell_content, + GtkEntryIconPosition icon_pos, + GdkEvent *event) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkAction *action; + + /* Show the search options menu when the icon is pressed. */ + + if (icon_pos != GTK_ENTRY_ICON_PRIMARY) + return; + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window); + gtk_action_activate (action); +} + +static void +shell_content_entry_icon_release_cb (EShellContent *shell_content, + GtkEntryIconPosition icon_pos, + GdkEvent *event) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkAction *action; + + /* Clear the search when the icon is pressed and released. */ + + if (icon_pos != GTK_ENTRY_ICON_SECONDARY) + return; + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window); + gtk_action_activate (action); +} + +static gboolean +shell_content_entry_key_press_cb (EShellContent *shell_content, + GdkEventKey *key_event, + GtkWidget *entry) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkAction *action; + guint mask; + + mask = gtk_accelerator_get_default_mod_mask (); + if ((key_event->state & mask) != GDK_MOD1_MASK) + return FALSE; + + if (key_event->keyval != GDK_Down) + return FALSE; + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + + action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window); + gtk_action_activate (action); + + return TRUE; +} + +static void +shell_content_init_search_context (EShellContent *shell_content) +{ + EShellContentClass *shell_content_class; + EShellView *shell_view; + EShellViewClass *shell_view_class; + EShellBackend *shell_backend; + RuleContext *context; + FilterRule *rule; + FilterPart *part; + gchar *system_filename; + gchar *user_filename; + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_backend = e_shell_view_get_shell_backend (shell_view); + shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + g_return_if_fail (shell_view_class->search_rules != NULL); + + shell_content_class = E_SHELL_CONTENT_GET_CLASS (shell_content); + g_return_if_fail (shell_content_class->new_search_context != NULL); + + /* The basename for built-in searches is specified in the + * shell view class. All built-in search rules live in the + * same directory. */ + system_filename = g_build_filename ( + EVOLUTION_RULEDIR, shell_view_class->search_rules, NULL); + + /* The filename for custom saved searches is always of + * the form "$(shell_backend_data_dir)/searches.xml". */ + user_filename = g_build_filename ( + e_shell_backend_get_data_dir (shell_backend), + "searches.xml", NULL); + + context = shell_content_class->new_search_context (); + rule_context_add_part_set ( + context, "partset", FILTER_TYPE_PART, + rule_context_add_part, rule_context_next_part); + rule_context_add_rule_set ( + context, "ruleset", FILTER_TYPE_RULE, + rule_context_add_rule, rule_context_next_rule); + rule_context_load (context, system_filename, user_filename); + + /* XXX Not sure why this is necessary. */ + g_object_set_data_full ( + G_OBJECT (context), "system", + g_strdup (system_filename), g_free); + g_object_set_data_full ( + G_OBJECT (context), "user", + g_strdup (user_filename), g_free); + + rule = filter_rule_new (); + part = rule_context_next_part (context, NULL); + if (part == NULL) + g_warning ( + "Could not load %s search: no parts", + e_shell_view_get_name (shell_view)); + else + filter_rule_add_part (rule, filter_part_clone (part)); + + shell_content->priv->search_context = context; + shell_content->priv->system_filename = system_filename; + shell_content->priv->user_filename = user_filename; +} + +static void +shell_content_set_shell_view (EShellContent *shell_content, + EShellView *shell_view) +{ + g_return_if_fail (shell_content->priv->shell_view == NULL); + + shell_content->priv->shell_view = shell_view; + + g_object_add_weak_pointer ( + G_OBJECT (shell_view), + &shell_content->priv->shell_view); +} + +static void +shell_content_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILTER_ACTION: + e_shell_content_set_filter_action ( + E_SHELL_CONTENT (object), + g_value_get_object (value)); + return; + + case PROP_FILTER_VALUE: + e_shell_content_set_filter_value ( + E_SHELL_CONTENT (object), + g_value_get_int (value)); + return; + + case PROP_FILTER_VISIBLE: + e_shell_content_set_filter_visible ( + E_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + + case PROP_SEARCH_HINT: + e_shell_content_set_search_hint ( + E_SHELL_CONTENT (object), + g_value_get_string (value)); + return; + + case PROP_SEARCH_RULE: + e_shell_content_set_search_rule ( + E_SHELL_CONTENT (object), + g_value_get_object (value)); + return; + + case PROP_SEARCH_TEXT: + e_shell_content_set_search_text ( + E_SHELL_CONTENT (object), + g_value_get_string (value)); + return; + + case PROP_SEARCH_VISIBLE: + e_shell_content_set_search_visible ( + E_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + + case PROP_SCOPE_ACTION: + e_shell_content_set_scope_action ( + E_SHELL_CONTENT (object), + g_value_get_object (value)); + return; + + case PROP_SCOPE_VALUE: + e_shell_content_set_scope_value ( + E_SHELL_CONTENT (object), + g_value_get_int (value)); + return; + + case PROP_SCOPE_VISIBLE: + e_shell_content_set_scope_visible ( + E_SHELL_CONTENT (object), + g_value_get_boolean (value)); + return; + + case PROP_SHELL_VIEW: + shell_content_set_shell_view ( + E_SHELL_CONTENT (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +shell_content_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILTER_ACTION: + g_value_set_object ( + value, e_shell_content_get_filter_action ( + E_SHELL_CONTENT (object))); + return; + + case PROP_FILTER_VALUE: + g_value_set_int ( + value, e_shell_content_get_filter_value ( + E_SHELL_CONTENT (object))); + return; + + case PROP_FILTER_VISIBLE: + g_value_set_boolean ( + value, e_shell_content_get_filter_visible ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SEARCH_CONTEXT: + g_value_set_object ( + value, e_shell_content_get_search_context ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SEARCH_HINT: + g_value_set_string ( + value, e_shell_content_get_search_hint ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SEARCH_RULE: + g_value_set_object ( + value, e_shell_content_get_search_rule ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SEARCH_TEXT: + g_value_set_string ( + value, e_shell_content_get_search_text ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SEARCH_VISIBLE: + g_value_set_boolean ( + value, e_shell_content_get_search_visible ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SCOPE_ACTION: + g_value_set_object ( + value, e_shell_content_get_scope_action ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SCOPE_VALUE: + g_value_set_int ( + value, e_shell_content_get_scope_value ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SCOPE_VISIBLE: + g_value_set_boolean ( + value, e_shell_content_get_scope_visible ( + E_SHELL_CONTENT (object))); + return; + + case PROP_SHELL_VIEW: + g_value_set_object ( + value, e_shell_content_get_shell_view ( + E_SHELL_CONTENT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +shell_content_dispose (GObject *object) +{ + EShellContentPrivate *priv; + + priv = E_SHELL_CONTENT_GET_PRIVATE (object); + + if (priv->shell_view != NULL) { + g_object_remove_weak_pointer ( + G_OBJECT (priv->shell_view), &priv->shell_view); + priv->shell_view = NULL; + } + + if (priv->filter_label != NULL) { + g_object_unref (priv->filter_label); + priv->filter_label = NULL; + } + + if (priv->filter_combo_box != NULL) { + g_object_unref (priv->filter_combo_box); + priv->filter_combo_box = NULL; + } + + if (priv->search_context != NULL) { + g_object_unref (priv->search_context); + priv->search_context = NULL; + } + + if (priv->search_label != NULL) { + g_object_unref (priv->search_label); + priv->search_label = NULL; + } + + if (priv->search_entry != NULL) { + g_object_unref (priv->search_entry); + priv->search_entry = NULL; + } + + if (priv->scope_label != NULL) { + g_object_unref (priv->scope_label); + priv->scope_label = NULL; + } + + if (priv->scope_combo_box != NULL) { + g_object_unref (priv->scope_combo_box); + priv->scope_combo_box = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +shell_content_finalize (GObject *object) +{ + EShellContentPrivate *priv; + + priv = E_SHELL_CONTENT_GET_PRIVATE (object); + + g_free (priv->system_filename); + g_free (priv->user_filename); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +shell_content_constructed (GObject *object) +{ + EShellView *shell_view; + EShellWindow *shell_window; + EShellContent *shell_content; + GtkSizeGroup *size_group; + GtkAction *action; + GtkWidget *widget; + + shell_content = E_SHELL_CONTENT (object); + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + size_group = e_shell_view_get_size_group (shell_view); + + widget = shell_content->priv->search_entry; + + action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window); + e_binding_new ( + G_OBJECT (action), "sensitive", + G_OBJECT (widget), "secondary-icon-sensitive"); + e_binding_new ( + G_OBJECT (action), "stock-id", + G_OBJECT (widget), "secondary-icon-stock"); + e_binding_new ( + G_OBJECT (action), "tooltip", + G_OBJECT (widget), "secondary-icon-tooltip-text"); + + action = E_SHELL_WINDOW_ACTION_SEARCH_EXECUTE (shell_window); + g_signal_connect ( + action, "activate", + G_CALLBACK (action_search_execute_cb), shell_content); + + action = E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS (shell_window); + e_binding_new ( + G_OBJECT (action), "sensitive", + G_OBJECT (widget), "primary-icon-sensitive"); + e_binding_new ( + G_OBJECT (action), "stock-id", + G_OBJECT (widget), "primary-icon-stock"); + e_binding_new ( + G_OBJECT (action), "tooltip", + G_OBJECT (widget), "primary-icon-tooltip-text"); + + widget = shell_content->priv->search_bar; + gtk_size_group_add_widget (size_group, widget); + + shell_content_init_search_context (shell_content); +} + +static void +shell_content_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + EShellContentPrivate *priv; + GtkRequisition child_requisition; + GtkWidget *child; + + priv = E_SHELL_CONTENT_GET_PRIVATE (widget); + + requisition->width = 0; + requisition->height = 0; + + child = gtk_bin_get_child (GTK_BIN (widget)); + gtk_widget_size_request (child, requisition); + + child = priv->search_bar; + gtk_widget_size_request (child, &child_requisition); + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; +} + +static void +shell_content_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + EShellContentPrivate *priv; + GtkAllocation child_allocation; + GtkRequisition child_requisition; + GtkWidget *child; + + priv = E_SHELL_CONTENT_GET_PRIVATE (widget); + + widget->allocation = *allocation; + + child = priv->search_bar; + gtk_widget_size_request (child, &child_requisition); + + child_allocation.x = allocation->x; + child_allocation.y = allocation->y; + child_allocation.width = allocation->width; + child_allocation.height = child_requisition.height; + + gtk_widget_size_allocate (child, &child_allocation); + + child_allocation.y += child_requisition.height; + child_allocation.height = + allocation->height - child_requisition.height; + + child = gtk_bin_get_child (GTK_BIN (widget)); + if (child != NULL) + gtk_widget_size_allocate (child, &child_allocation); +} + +static void +shell_content_remove (GtkContainer *container, + GtkWidget *widget) +{ + EShellContentPrivate *priv; + + priv = E_SHELL_CONTENT_GET_PRIVATE (container); + + /* Look in the internal widgets first. */ + + if (widget == priv->search_bar) { + gtk_widget_unparent (priv->search_bar); + gtk_widget_queue_resize (GTK_WIDGET (container)); + return; + } + + /* Chain up to parent's remove() method. */ + GTK_CONTAINER_CLASS (parent_class)->remove (container, widget); +} + +static void +shell_content_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + EShellContentPrivate *priv; + + priv = E_SHELL_CONTENT_GET_PRIVATE (container); + + if (include_internals) + callback (priv->search_bar, callback_data); + + /* Chain up to parent's forall() method. */ + GTK_CONTAINER_CLASS (parent_class)->forall ( + container, include_internals, callback, callback_data); +} + +static void +shell_content_class_init (EShellContentClass *class) +{ + GObjectClass *object_class; + GtkWidgetClass *widget_class; + GtkContainerClass *container_class; + + parent_class = g_type_class_peek_parent (class); + g_type_class_add_private (class, sizeof (EShellContentPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = shell_content_set_property; + object_class->get_property = shell_content_get_property; + object_class->dispose = shell_content_dispose; + object_class->finalize = shell_content_finalize; + object_class->constructed = shell_content_constructed; + + widget_class = GTK_WIDGET_CLASS (class); + widget_class->size_request = shell_content_size_request; + widget_class->size_allocate = shell_content_size_allocate; + + container_class = GTK_CONTAINER_CLASS (class); + container_class->remove = shell_content_remove; + container_class->forall = shell_content_forall; + + class->new_search_context = rule_context_new; + + g_object_class_install_property ( + object_class, + PROP_FILTER_ACTION, + g_param_spec_object ( + "filter-action", + NULL, + NULL, + GTK_TYPE_RADIO_ACTION, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_FILTER_VALUE, + g_param_spec_int ( + "filter-value", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_FILTER_VISIBLE, + g_param_spec_boolean ( + "filter-visible", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_SEARCH_CONTEXT, + g_param_spec_object ( + "search-context", + NULL, + NULL, + RULE_TYPE_CONTEXT, + G_PARAM_READABLE)); + + g_object_class_install_property ( + object_class, + PROP_SEARCH_HINT, + g_param_spec_string ( + "search-hint", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SEARCH_RULE, + g_param_spec_object ( + "search-rule", + NULL, + NULL, + FILTER_TYPE_RULE, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SEARCH_TEXT, + g_param_spec_string ( + "search-text", + NULL, + NULL, + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SEARCH_VISIBLE, + g_param_spec_boolean ( + "search-visible", + NULL, + NULL, + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_SCOPE_ACTION, + g_param_spec_object ( + "scope-action", + NULL, + NULL, + GTK_TYPE_RADIO_ACTION, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SCOPE_VALUE, + g_param_spec_int ( + "scope-value", + NULL, + NULL, + G_MININT, + G_MAXINT, + 0, + G_PARAM_READWRITE)); + + g_object_class_install_property ( + object_class, + PROP_SCOPE_VISIBLE, + g_param_spec_boolean ( + "scope-visible", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT)); + + g_object_class_install_property ( + object_class, + PROP_SHELL_VIEW, + g_param_spec_object ( + "shell-view", + NULL, + NULL, + E_TYPE_SHELL_VIEW, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY)); +} + +static void +shell_content_init (EShellContent *shell_content) +{ + GtkBox *box; + GtkLabel *label; + GtkWidget *widget; + + shell_content->priv = E_SHELL_CONTENT_GET_PRIVATE (shell_content); + + GTK_WIDGET_SET_FLAGS (shell_content, GTK_NO_WINDOW); + + /*** Build the Search Bar ***/ + + widget = gtk_hbox_new (FALSE, 24); + gtk_widget_set_parent (widget, GTK_WIDGET (shell_content)); + shell_content->priv->search_bar = g_object_ref_sink (widget); + gtk_widget_show (widget); + + /* Filter Combo Widgets */ + + box = GTK_BOX (shell_content->priv->search_bar); + + widget = gtk_hbox_new (FALSE, 3); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + gtk_widget_show (widget); + + box = GTK_BOX (widget); + + /* Translators: The "Show:" label precedes a combo box that + * allows the user to filter the current view. Examples of + * items that appear in the combo box are "Unread Messages", + * "Important Messages", or "Active Appointments". */ + widget = gtk_label_new_with_mnemonic (_("Sho_w:")); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + shell_content->priv->filter_label = g_object_ref (widget); + gtk_widget_show (widget); + + label = GTK_LABEL (widget); + + widget = e_action_combo_box_new (); + gtk_label_set_mnemonic_widget (label, widget); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + shell_content->priv->filter_combo_box = g_object_ref (widget); + gtk_widget_show (widget); + + /* Search Entry Widgets */ + + box = GTK_BOX (shell_content->priv->search_bar); + + widget = gtk_hbox_new (FALSE, 3); + gtk_box_pack_start (box, widget, TRUE, TRUE, 0); + gtk_widget_show (widget); + + box = GTK_BOX (widget); + + /* Translators: This is part of the quick search interface. + * example: Search: [_______________] in [ Current Folder ] */ + widget = gtk_label_new_with_mnemonic (_("Sear_ch:")); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + shell_content->priv->search_label = g_object_ref (widget); + gtk_widget_show (widget); + + label = GTK_LABEL (widget); + + widget = e_hinted_entry_new (); + gtk_label_set_mnemonic_widget (label, widget); + gtk_box_pack_start (box, widget, TRUE, TRUE, 0); + shell_content->priv->search_entry = g_object_ref (widget); + gtk_widget_show (widget); + + g_signal_connect_swapped ( + widget, "activate", + G_CALLBACK (shell_content_entry_activated_cb), + shell_content); + + g_signal_connect_swapped ( + widget, "icon-press", + G_CALLBACK (shell_content_entry_icon_press_cb), + shell_content); + + g_signal_connect_swapped ( + widget, "icon-release", + G_CALLBACK (shell_content_entry_icon_release_cb), + shell_content); + + g_signal_connect_swapped ( + widget, "key-press-event", + G_CALLBACK (shell_content_entry_key_press_cb), + shell_content); + + /* Scope Combo Widgets */ + + /* Translators: This is part of the quick search interface. + * example: Search: [_______________] in [ Current Folder ] */ + widget = gtk_label_new_with_mnemonic (_("i_n")); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + shell_content->priv->scope_label = g_object_ref (widget); + gtk_widget_show (widget); + + label = GTK_LABEL (widget); + + widget = e_action_combo_box_new (); + gtk_label_set_mnemonic_widget (label, widget); + gtk_box_pack_start (box, widget, FALSE, FALSE, 0); + shell_content->priv->scope_combo_box = g_object_ref (widget); + gtk_widget_show (widget); +} + +GType +e_shell_content_get_type (void) +{ + static GType type = 0; + + if (G_UNLIKELY (type == 0)) { + static const GTypeInfo type_info = { + sizeof (EShellContentClass), + (GBaseInitFunc) NULL, + (GBaseFinalizeFunc) NULL, + (GClassInitFunc) shell_content_class_init, + (GClassFinalizeFunc) NULL, + NULL, /* class_data */ + sizeof (EShellContent), + 0, /* n_preallocs */ + (GInstanceInitFunc) shell_content_init, + NULL /* value_table */ + }; + + type = g_type_register_static ( + GTK_TYPE_BIN, "EShellContent", &type_info, 0); + } + + return type; +} + +/** + * e_shell_content_new: + * @shell_view: an #EShellView + * + * Creates a new #EShellContent instance belonging to @shell_view. + * + * Returns: a new #EShellContent instance + **/ +GtkWidget * +e_shell_content_new (EShellView *shell_view) +{ + g_return_val_if_fail (E_IS_SHELL_VIEW (shell_view), NULL); + + return g_object_new ( + E_TYPE_SHELL_CONTENT, "shell-view", shell_view, NULL); +} + +/** + * e_shell_content_check_state: + * @shell_content: an #EShellContent + * + * #EShellContent subclasses should implement the + * <structfield>check_state</structfield> method in #EShellContentClass + * to return a set of flags describing the current content selection. + * Subclasses are responsible for defining their own flags. This is + * primarily used to assist shell views with updating actions (see + * e_shell_view_update_actions()). + * + * Returns: a set of flags describing the current @shell_content selection + **/ +guint32 +e_shell_content_check_state (EShellContent *shell_content) +{ + EShellContentClass *shell_content_class; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), 0); + + shell_content_class = E_SHELL_CONTENT_GET_CLASS (shell_content); + g_return_val_if_fail (shell_content_class->check_state != NULL, 0); + + return shell_content_class->check_state (shell_content); +} + +/** + * e_shell_content_get_shell_view: + * @shell_content: an #EShellContent + * + * Returns the #EShellView that was passed to e_shell_content_new(). + * + * Returns: the #EShellView to which @shell_content belongs + **/ +EShellView * +e_shell_content_get_shell_view (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + return E_SHELL_VIEW (shell_content->priv->shell_view); +} + +GtkRadioAction * +e_shell_content_get_filter_action (EShellContent *shell_content) +{ + EActionComboBox *combo_box; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + return e_action_combo_box_get_action (combo_box); +} + +void +e_shell_content_set_filter_action (EShellContent *shell_content, + GtkRadioAction *filter_action) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + e_action_combo_box_set_action (combo_box, filter_action); + + g_object_notify (G_OBJECT (shell_content), "filter-action"); +} + +gint +e_shell_content_get_filter_value (EShellContent *shell_content) +{ + EActionComboBox *combo_box; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), 0); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + return e_action_combo_box_get_current_value (combo_box); +} + +void +e_shell_content_set_filter_value (EShellContent *shell_content, + gint filter_value) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + e_action_combo_box_set_current_value (combo_box, filter_value); + + g_object_notify (G_OBJECT (shell_content), "filter-value"); +} + +gboolean +e_shell_content_get_filter_visible (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), FALSE); + + return GTK_WIDGET_VISIBLE (shell_content->priv->filter_combo_box); +} + +void +e_shell_content_set_filter_visible (EShellContent *shell_content, + gboolean filter_visible) +{ + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + if (filter_visible) { + gtk_widget_show (shell_content->priv->filter_label); + gtk_widget_show (shell_content->priv->filter_combo_box); + } else { + gtk_widget_hide (shell_content->priv->filter_label); + gtk_widget_hide (shell_content->priv->filter_combo_box); + } +} + +void +e_shell_content_add_filter_separator_before (EShellContent *shell_content, + gint action_value) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + e_action_combo_box_add_separator_before (combo_box, action_value); +} + +void +e_shell_content_add_filter_separator_after (EShellContent *shell_content, + gint action_value) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->filter_combo_box); + + e_action_combo_box_add_separator_after (combo_box, action_value); +} + +RuleContext * +e_shell_content_get_search_context (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + return shell_content->priv->search_context; +} + +const gchar * +e_shell_content_get_search_hint (EShellContent *shell_content) +{ + EHintedEntry *entry; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + entry = E_HINTED_ENTRY (shell_content->priv->search_entry); + + return e_hinted_entry_get_hint (entry); +} + +void +e_shell_content_set_search_hint (EShellContent *shell_content, + const gchar *search_hint) +{ + EHintedEntry *entry; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + entry = E_HINTED_ENTRY (shell_content->priv->search_entry); + + e_hinted_entry_set_hint (entry, search_hint); + + g_object_notify (G_OBJECT (shell_content), "search-hint"); +} + +FilterRule * +e_shell_content_get_search_rule (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + return shell_content->priv->search_rule; +} + +void +e_shell_content_set_search_rule (EShellContent *shell_content, + FilterRule *search_rule) +{ + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + if (search_rule != NULL) { + g_return_if_fail (IS_FILTER_RULE (search_rule)); + g_object_ref (search_rule); + } + + if (shell_content->priv->search_rule != NULL) + g_object_unref (shell_content->priv->search_rule); + + shell_content->priv->search_rule = search_rule; + + g_object_notify (G_OBJECT (shell_content), "search-rule"); +} + +const gchar * +e_shell_content_get_search_text (EShellContent *shell_content) +{ + EHintedEntry *entry; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + entry = E_HINTED_ENTRY (shell_content->priv->search_entry); + + return e_hinted_entry_get_text (entry); +} + +void +e_shell_content_set_search_text (EShellContent *shell_content, + const gchar *search_text) +{ + EHintedEntry *entry; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + entry = E_HINTED_ENTRY (shell_content->priv->search_entry); + + e_hinted_entry_set_text (entry, search_text); + + g_object_notify (G_OBJECT (shell_content), "search-text"); +} + +gboolean +e_shell_content_get_search_visible (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), FALSE); + + return GTK_WIDGET_VISIBLE (shell_content->priv->search_entry); +} + +void +e_shell_content_set_search_visible (EShellContent *shell_content, + gboolean search_visible) +{ + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + if (search_visible) { + gtk_widget_show (shell_content->priv->search_label); + gtk_widget_show (shell_content->priv->search_entry); + } else { + gtk_widget_hide (shell_content->priv->search_label); + gtk_widget_hide (shell_content->priv->search_entry); + } +} + +GtkRadioAction * +e_shell_content_get_scope_action (EShellContent *shell_content) +{ + EActionComboBox *combo_box; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->scope_combo_box); + + return e_action_combo_box_get_action (combo_box); +} + +void +e_shell_content_set_scope_action (EShellContent *shell_content, + GtkRadioAction *scope_action) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->scope_combo_box); + + e_action_combo_box_set_action (combo_box, scope_action); + + g_object_notify (G_OBJECT (shell_content), "scope-action"); +} + +gint +e_shell_content_get_scope_value (EShellContent *shell_content) +{ + EActionComboBox *combo_box; + + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), 0); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->scope_combo_box); + + return e_action_combo_box_get_current_value (combo_box); +} + +void +e_shell_content_set_scope_value (EShellContent *shell_content, + gint scope_value) +{ + EActionComboBox *combo_box; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + combo_box = E_ACTION_COMBO_BOX (shell_content->priv->scope_combo_box); + + e_action_combo_box_set_current_value (combo_box, scope_value); + + g_object_notify (G_OBJECT (shell_content), "scope-value"); +} + +gboolean +e_shell_content_get_scope_visible (EShellContent *shell_content) +{ + g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), FALSE); + + return GTK_WIDGET_VISIBLE (shell_content->priv->scope_combo_box); +} + +void +e_shell_content_set_scope_visible (EShellContent *shell_content, + gboolean scope_visible) +{ + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + if (scope_visible) { + gtk_widget_show (shell_content->priv->scope_label); + gtk_widget_show (shell_content->priv->scope_combo_box); + } else { + gtk_widget_hide (shell_content->priv->scope_label); + gtk_widget_hide (shell_content->priv->scope_combo_box); + } + + g_object_notify (G_OBJECT (shell_content), "scope-visible"); +} + +void +e_shell_content_run_advanced_search_dialog (EShellContent *shell_content) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkAction *action; + GtkWidget *dialog; + GtkWidget *widget; + FilterRule *rule; + RuleContext *context; + const gchar *user_filename; + gint response; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + user_filename = shell_content->priv->user_filename; + + rule = e_shell_content_get_search_rule (shell_content); + + if (rule == NULL) + rule = filter_rule_new (); + else + rule = filter_rule_clone (rule); + + context = e_shell_content_get_search_context (shell_content); + widget = filter_rule_get_widget (rule, context); + filter_rule_set_source (rule, FILTER_SOURCE_INCOMING); + + dialog = gtk_dialog_new_with_buttons ( + _("Advanced Search"), GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_APPLY, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 7); + gtk_container_set_border_width (GTK_CONTAINER (widget), 3); + gtk_window_set_default_size (GTK_WINDOW (dialog), 600, 300); + + gtk_box_pack_start ( + GTK_BOX (GTK_DIALOG (dialog)->vbox), widget, TRUE, TRUE, 0); + + g_signal_connect_swapped ( + rule, "changed", G_CALLBACK ( + shell_content_dialog_rule_changed), dialog); + + shell_content_dialog_rule_changed (dialog, rule); + +run: + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response != GTK_RESPONSE_OK && response != GTK_RESPONSE_APPLY) + goto exit; + + if (!filter_rule_validate (rule)) + goto run; + + e_shell_content_set_search_rule (shell_content, rule); + + action = E_SHELL_WINDOW_ACTION_SEARCH_EXECUTE (shell_window); + gtk_action_activate (action); + + if (response == GTK_RESPONSE_APPLY) { + if (!rule_context_find_rule (context, rule->name, rule->source)) + rule_context_add_rule (context, rule); + rule_context_save (context, user_filename); + goto run; + } + +exit: + g_object_unref (rule); + gtk_widget_destroy (dialog); +} + +void +e_shell_content_run_edit_searches_dialog (EShellContent *shell_content) +{ + RuleContext *context; + RuleEditor *editor; + const gchar *user_filename; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + context = e_shell_content_get_search_context (shell_content); + user_filename = shell_content->priv->user_filename; + + editor = rule_editor_new ( + context, FILTER_SOURCE_INCOMING, _("Searches")); + gtk_window_set_title (GTK_WINDOW (editor), _("Searches")); + + if (gtk_dialog_run (GTK_DIALOG (editor)) == GTK_RESPONSE_OK) + rule_context_save (context, user_filename); + + gtk_widget_destroy (GTK_WIDGET (editor)); +} + +void +e_shell_content_run_save_search_dialog (EShellContent *shell_content) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GtkWidget *dialog; + GtkWidget *widget; + FilterRule *rule; + RuleContext *context; + const gchar *search_text; + const gchar *user_filename; + gchar *search_name; + gint response; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + user_filename = shell_content->priv->user_filename; + + rule = e_shell_content_get_search_rule (shell_content); + g_return_if_fail (IS_FILTER_RULE (rule)); + rule = filter_rule_clone (rule); + + search_text = e_shell_content_get_search_text (shell_content); + if (search_text == NULL || *search_text == '\0') + search_text = "''"; + + search_name = g_strdup_printf ("%s %s", rule->name, search_text); + filter_rule_set_name (rule, search_name); + g_free (search_name); + + context = e_shell_content_get_search_context (shell_content); + widget = filter_rule_get_widget (rule, context); + filter_rule_set_source (rule, FILTER_SOURCE_INCOMING); + + dialog = gtk_dialog_new_with_buttons ( + _("Save Search"), GTK_WINDOW (shell_window), + GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); + + gtk_container_set_border_width (GTK_CONTAINER (dialog), 7); + gtk_container_set_border_width (GTK_CONTAINER (widget), 3); + gtk_window_set_default_size (GTK_WINDOW (dialog), 500, 300); + + gtk_box_pack_start ( + GTK_BOX (GTK_DIALOG (dialog)->vbox), widget, TRUE, TRUE, 0); + + g_signal_connect_swapped ( + rule, "changed", G_CALLBACK ( + shell_content_dialog_rule_changed), dialog); + + shell_content_dialog_rule_changed (dialog, rule); + +run: + response = gtk_dialog_run (GTK_DIALOG (dialog)); + + if (response != GTK_RESPONSE_OK) + goto exit; + + if (!filter_rule_validate (rule)) + goto run; + + rule_context_add_rule (context, rule); + rule_context_save (context, user_filename); + +exit: + g_object_unref (rule); + gtk_widget_destroy (dialog); +} + +void +e_shell_content_restore_state (EShellContent *shell_content, + const gchar *group_name) +{ + EShellView *shell_view; + EShellWindow *shell_window; + GKeyFile *key_file; + GtkAction *action; + GtkWidget *widget; + const gchar *key; + gchar *string; + + g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); + g_return_if_fail (group_name != NULL); + + shell_view = e_shell_content_get_shell_view (shell_content); + shell_window = e_shell_view_get_shell_window (shell_view); + key_file = e_shell_view_get_state_key_file (shell_view); + + /* Changing the combo boxes triggers searches, so block + * the search action until the state is fully restored. */ + action = e_shell_window_get_action (shell_window, "search-execute"); + gtk_action_block_activate (action); + + key = STATE_KEY_SEARCH_FILTER; + string = g_key_file_get_string (key_file, group_name, key, NULL); + if (string != NULL && *string != '\0') + action = e_shell_window_get_action (shell_window, string); + else + action = NULL; + if (action != NULL) + gtk_action_activate (action); + else { + /* Pick the first combo box item. */ + widget = shell_content->priv->filter_combo_box; + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + } + g_free (string); + + key = STATE_KEY_SEARCH_SCOPE; + string = g_key_file_get_string (key_file, group_name, key, NULL); + if (string != NULL && *string != '\0') + action = e_shell_window_get_action (shell_window, string); + else + action = NULL; + if (action != NULL) + gtk_action_activate (action); + else { + /* Pick the first combo box item. */ + widget = shell_content->priv->scope_combo_box; + gtk_combo_box_set_active (GTK_COMBO_BOX (widget), 0); + } + g_free (string); + + key = STATE_KEY_SEARCH_TEXT; + string = g_key_file_get_string (key_file, group_name, key, NULL); + e_shell_content_set_search_text (shell_content, string); + g_free (string); + + action = e_shell_window_get_action (shell_window, "search-execute"); + gtk_action_unblock_activate (action); + + /* Now execute the search. */ + gtk_action_activate (action); +} |