diff options
author | Matthew Barnes <mbarnes@src.gnome.org> | 2008-09-23 05:03:28 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@src.gnome.org> | 2008-09-23 05:03:28 +0800 |
commit | 73c370019c4de89d4c901ee8c25cc0cbb55992fb (patch) | |
tree | 26609470a1148d3bd364cbce5d5ab2f41e0e23d4 | |
parent | 6b2295c93a40f6010d94399666a8e099aded8e85 (diff) | |
download | gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar.gz gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar.bz2 gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar.lz gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar.xz gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.tar.zst gsoc2013-evolution-73c370019c4de89d4c901ee8c25cc0cbb55992fb.zip |
Search UI is kinda sorta working. Still some outstanding issues.
svn path=/branches/kill-bonobo/; revision=36427
25 files changed, 829 insertions, 231 deletions
diff --git a/addressbook/gui/component/addressbook-component.c b/addressbook/gui/component/addressbook-component.c index 5c3ad0999f..49e138e0de 100644 --- a/addressbook/gui/component/addressbook-component.c +++ b/addressbook/gui/component/addressbook-component.c @@ -92,25 +92,7 @@ addressbook_component_class_init (AddressbookComponentClass *class) static void addressbook_component_init (AddressbookComponent *component) { - static int first = TRUE; - #ifdef ENABLE_SMIME smime_component_init (); #endif - - if (first) { - EImportClass *klass; - - first = FALSE; - e_plugin_hook_register_type(eab_popup_hook_get_type()); - e_plugin_hook_register_type(eab_menu_hook_get_type()); - e_plugin_hook_register_type(eab_config_hook_get_type()); - - klass = g_type_class_ref(e_import_get_type()); - e_import_class_add_importer(klass, evolution_ldif_importer_peek(), NULL, NULL); - e_import_class_add_importer(klass, evolution_vcard_importer_peek(), NULL, NULL); - e_import_class_add_importer(klass, evolution_csv_outlook_importer_peek(), NULL, NULL); - e_import_class_add_importer(klass, evolution_csv_mozilla_importer_peek(), NULL, NULL); - e_import_class_add_importer(klass, evolution_csv_evolution_importer_peek(), NULL, NULL); - } } diff --git a/addressbook/gui/component/e-book-shell-module.c b/addressbook/gui/component/e-book-shell-module.c index 1b3e778ce8..480320185e 100644 --- a/addressbook/gui/component/e-book-shell-module.c +++ b/addressbook/gui/component/e-book-shell-module.c @@ -30,6 +30,10 @@ #include <e-shell-module.h> #include <e-shell-window.h> +#include <e-util/e-import.h> +#include <addressbook/importers/evolution-addressbook-importers.h> + +#include <eab-config.h> #include <eab-gui-util.h> #include <e-book-shell-view.h> #include <addressbook-config.h> @@ -178,6 +182,30 @@ book_module_ensure_sources (EShellModule *shell_module) } static void +book_module_init_importers (void) +{ + EImportClass *import_class; + EImportImporter *importer; + + import_class = g_type_class_ref (e_import_get_type ()); + + importer = evolution_ldif_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = evolution_vcard_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = evolution_csv_outlook_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = evolution_csv_mozilla_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); + + importer = evolution_csv_evolution_importer_peek (); + e_import_class_add_importer (import_class, importer, NULL, NULL); +} + +static void book_module_book_loaded_cb (EBook *book, EBookStatus status, gpointer user_data) @@ -401,8 +429,11 @@ e_shell_module_init (GTypeModule *type_module) e_shell_module_set_info (shell_module, &module_info); + book_module_init_importers (); book_module_ensure_sources (shell_module); + e_plugin_hook_register_type (eab_config_get_type ()); + g_signal_connect_swapped ( shell, "handle-uri", G_CALLBACK (book_module_handle_uri), shell_module); diff --git a/addressbook/gui/component/e-book-shell-view-actions.c b/addressbook/gui/component/e-book-shell-view-actions.c index 89ca2c22a8..e27474aac0 100644 --- a/addressbook/gui/component/e-book-shell-view-actions.c +++ b/addressbook/gui/component/e-book-shell-view-actions.c @@ -22,7 +22,7 @@ #include <e-util/e-error.h> #include <e-util/e-util.h> -#include <e-util/gconf-bridge.h> +#include <filter/filter-rule.h> #include <addressbook-config.h> @@ -333,12 +333,14 @@ static void action_contact_preview_cb (GtkToggleAction *action, EBookShellView *book_shell_view) { - GtkWidget *widget; + GtkWidget *child; + GtkWidget *paned; gboolean visible; - widget = book_shell_view->priv->preview; + paned = book_shell_view->priv->paned; + child = gtk_paned_get_child2 (GTK_PANED (paned)); visible = gtk_toggle_action_get_active (action); - g_object_set (widget, "visible", visible, NULL); + g_object_set (child, "visible", visible, NULL); } static void @@ -426,10 +428,12 @@ action_search_execute_cb (GtkAction *action, EShellContent *shell_content; GString *string; EAddressbookView *view; + EAddressbookModel *model; EABContactDisplay *display; - const gchar *search_format; - const gchar *search_text; - gchar *search_query; + FilterRule *rule; + const gchar *format; + const gchar *text; + gchar *query; gint value; shell_view = E_SHELL_VIEW (book_shell_view); @@ -437,41 +441,40 @@ action_search_execute_cb (GtkAction *action, return; shell_content = e_shell_view_get_shell_content (shell_view); - search_text = e_shell_content_get_search_text (shell_content); + text = e_shell_content_get_search_text (shell_content); shell_window = e_shell_view_get_shell_window (shell_view); action = ACTION (CONTACT_SEARCH_ANY_FIELD_CONTAINS); value = gtk_radio_action_get_current_value ( GTK_RADIO_ACTION (action)); - if (search_text == NULL || *search_text == '\0') { - search_text = "\"\""; + if (text == NULL || *text == '\0') { + text = ""; value = CONTACT_SEARCH_ANY_FIELD_CONTAINS; } switch (value) { case CONTACT_SEARCH_NAME_CONTAINS: - search_format = "(contains \"full_name\" %s)"; + format = "(contains \"full_name\" %s)"; break; case CONTACT_SEARCH_EMAIL_BEGINS_WITH: - search_format = "(beginswith \"email\" %s)"; + format = "(beginswith \"email\" %s)"; break; default: - search_text = "\"\""; + text = ""; /* fall through */ case CONTACT_SEARCH_ANY_FIELD_CONTAINS: - search_format = - "(contains \"x-evolution-any-field\" %s)"; + format = "(contains \"x-evolution-any-field\" %s)"; break; } /* Build the query. */ string = g_string_new (""); - e_sexp_encode_string (string, search_text); - search_query = g_strdup_printf (search_format, string->str); + e_sexp_encode_string (string, text); + query = g_strdup_printf (format, string->str); g_string_free (string, TRUE); /* Filter by category. */ @@ -487,15 +490,23 @@ action_search_execute_cb (GtkAction *action, temp = g_strdup_printf ( "(and (is \"category_list\" \"%s\") %s)", - category_name, search_query); - g_free (search_query); - search_query = temp; + category_name, query); + g_free (query); + query = temp; } + /* XXX This is wrong. We need to programmatically construct a + * FilterRule, tell it to build code, and pass the resulting + * expression string to EAddressbookModel. */ + rule = filter_rule_new (); + e_shell_content_set_search_rule (shell_content, rule); + g_object_unref (rule); + /* Submit the query. */ view = e_book_shell_view_get_current_view (book_shell_view); - g_object_set (view, "query", search_query, NULL); - g_free (search_query); + model = e_addressbook_view_get_model (view); + e_addressbook_model_set_query (model, query); + g_free (query); display = EAB_CONTACT_DISPLAY (book_shell_view->priv->preview); eab_contact_display_set_contact (display, NULL); @@ -731,7 +742,7 @@ e_book_shell_view_actions_init (EBookShellView *book_shell_view) EShellView *shell_view; EShellWindow *shell_window; GtkActionGroup *action_group; - GtkUIManager *manager; + GtkUIManager *ui_manager; GConfBridge *bridge; GtkAction *action; GObject *object; @@ -740,11 +751,12 @@ e_book_shell_view_actions_init (EBookShellView *book_shell_view) shell_view = E_SHELL_VIEW (book_shell_view); shell_window = e_shell_view_get_shell_window (shell_view); - manager = e_shell_window_get_ui_manager (shell_window); + ui_manager = e_shell_window_get_ui_manager (shell_window); domain = GETTEXT_PACKAGE; - e_load_ui_definition (manager, "evolution-contacts.ui"); + e_load_ui_definition (ui_manager, "evolution-contacts.ui"); + /* Contact Actions */ action_group = book_shell_view->priv->contact_actions; gtk_action_group_set_translation_domain (action_group, domain); gtk_action_group_add_actions ( @@ -758,7 +770,12 @@ e_book_shell_view_actions_init (EBookShellView *book_shell_view) G_N_ELEMENTS (contact_search_entries), CONTACT_SEARCH_NAME_CONTAINS, NULL, NULL); - gtk_ui_manager_insert_action_group (manager, action_group, 0); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + + /* Filter Actions (empty) */ + action_group = book_shell_view->priv->filter_actions; + gtk_action_group_set_translation_domain (action_group, domain); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); /* Bind GObject properties to GConf keys. */ @@ -894,3 +911,60 @@ e_book_shell_view_actions_update (EBookShellView *book_shell_view) sensitive = FALSE; gtk_action_set_sensitive (action, sensitive); } + +void +e_book_shell_view_update_search_filter (EBookShellView *book_shell_view) +{ + EShellContent *shell_content; + EShellView *shell_view; + GtkActionGroup *action_group; + GtkRadioAction *action; + GList *list, *iter; + GSList *group = NULL; + gint ii; + + shell_view = E_SHELL_VIEW (book_shell_view); + shell_content = e_shell_view_get_shell_content (shell_view); + action_group = book_shell_view->priv->filter_actions; + + /* XXX Annoying that GTK+ doesn't provide a function for this. + * http://bugzilla.gnome.org/show_bug.cgi?id=550485 */ + list = gtk_action_group_list_actions (action_group); + for (iter = list; iter != NULL; iter = iter->next) + gtk_action_group_remove_action (action_group, iter->data); + g_list_free (list); + + action = gtk_radio_action_new ( + "category-any", _("Any Category"), NULL, NULL, -1); + + /* Activating the action executes a new search. */ + g_signal_connect ( + action, "activate", + G_CALLBACK (action_search_execute_cb), book_shell_view); + + gtk_radio_action_set_group (action, group); + group = gtk_radio_action_get_group (action); + + list = e_categories_get_list (); + for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) { + const gchar *category_name = iter->data; + gchar *action_name; + + action_name = g_strdup_printf ("category-%d", ii); + action = gtk_radio_action_new ( + action_name, category_name, NULL, NULL, ii); + g_free (action_name); + + /* Activating the action executes a new search. */ + g_signal_connect ( + action, "activate", G_CALLBACK ( + action_search_execute_cb), book_shell_view); + + gtk_radio_action_set_group (action, group); + group = gtk_radio_action_get_group (action); + } + g_list_free (list); + + /* Use any action in the group; doesn't matter which. */ + e_shell_content_set_filter_action (shell_content, action); +} diff --git a/addressbook/gui/component/e-book-shell-view-actions.h b/addressbook/gui/component/e-book-shell-view-actions.h index a809eeacf9..8119d7440c 100644 --- a/addressbook/gui/component/e-book-shell-view-actions.h +++ b/addressbook/gui/component/e-book-shell-view-actions.h @@ -84,5 +84,7 @@ /* Action Groups */ #define E_SHELL_WINDOW_ACTION_GROUP_CONTACTS(window) \ E_SHELL_WINDOW_ACTION_GROUP ((window), "contacts") +#define E_SHELL_WINDOW_ACTION_GROUP_CONTACTS_FILTER(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "contacts-filter") #endif /* E_BOOK_SHELL_VIEW_ACTIONS_H */ diff --git a/addressbook/gui/component/e-book-shell-view-private.c b/addressbook/gui/component/e-book-shell-view-private.c index 68d8425ef8..4dd88e7758 100644 --- a/addressbook/gui/component/e-book-shell-view-private.c +++ b/addressbook/gui/component/e-book-shell-view-private.c @@ -262,12 +262,6 @@ book_shell_view_show_popup_menu (GdkEventButton *event, return TRUE; } -static void -book_shell_view_categories_changed_cb (EBookShellView *book_shell_view) -{ - e_book_shell_view_update_search_filter (book_shell_view); -} - static gboolean book_shell_view_selector_button_press_event_cb (EShellView *shell_view, GdkEventButton *event) @@ -384,6 +378,7 @@ e_book_shell_view_private_init (EBookShellView *book_shell_view, priv->source_list = g_object_ref (source_list); priv->contact_actions = gtk_action_group_new ("contacts"); + priv->filter_actions = gtk_action_group_new ("contacts-filter"); priv->uid_to_view = uid_to_view; priv->uid_to_editor = uid_to_editor; @@ -476,7 +471,7 @@ e_book_shell_view_private_constructed (EBookShellView *book_shell_view) book_shell_view); e_categories_register_change_listener ( - G_CALLBACK (book_shell_view_categories_changed_cb), + G_CALLBACK (e_book_shell_view_update_search_filter), book_shell_view); e_book_shell_view_actions_init (book_shell_view); @@ -500,6 +495,7 @@ e_book_shell_view_private_dispose (EBookShellView *book_shell_view) DISPOSE (priv->source_list); DISPOSE (priv->contact_actions); + DISPOSE (priv->filter_actions); DISPOSE (priv->paned); DISPOSE (priv->notebook); @@ -543,41 +539,3 @@ e_book_shell_view_editor_weak_notify (EditorUidClosure *closure, hash_table = closure->view->priv->uid_to_editor; g_hash_table_remove (hash_table, closure->uid); } - -void -e_book_shell_view_update_search_filter (EBookShellView *book_shell_view) -{ - EShellContent *shell_content; - EShellView *shell_view; - GtkRadioAction *action; - GList *list, *iter; - GSList *group = NULL; - gint ii; - - shell_view = E_SHELL_VIEW (book_shell_view); - shell_content = e_shell_view_get_shell_content (shell_view); - - action = gtk_radio_action_new ( - "category-any", _("Any Category"), NULL, NULL, -1); - - gtk_radio_action_set_group (action, group); - group = gtk_radio_action_get_group (action); - - list = e_categories_get_list (); - for (iter = list, ii = 0; iter != NULL; iter = iter->next, ii++) { - const gchar *category_name = iter->data; - gchar *action_name; - - action_name = g_strdup_printf ("category-%d", ii); - action = gtk_radio_action_new ( - action_name, category_name, NULL, NULL, ii); - g_free (action_name); - - gtk_radio_action_set_group (action, group); - group = gtk_radio_action_get_group (action); - } - g_list_free (list); - - /* Use any action in the group; doesn't matter which. */ - e_shell_content_set_filter_action (shell_content, action); -} diff --git a/addressbook/gui/component/e-book-shell-view-private.h b/addressbook/gui/component/e-book-shell-view-private.h index 24af21539b..f4a9d25b91 100644 --- a/addressbook/gui/component/e-book-shell-view-private.h +++ b/addressbook/gui/component/e-book-shell-view-private.h @@ -86,6 +86,7 @@ struct _EBookShellViewPrivate { /*** UI Management ***/ GtkActionGroup *contact_actions; + GtkActionGroup *filter_actions; /*** Other Stuff ***/ diff --git a/addressbook/gui/component/e-book-shell-view.c b/addressbook/gui/component/e-book-shell-view.c index 9a90b1d1f5..c2b8450b79 100644 --- a/addressbook/gui/component/e-book-shell-view.c +++ b/addressbook/gui/component/e-book-shell-view.c @@ -20,6 +20,8 @@ #include "e-book-shell-view-private.h" +#define SEARCH_OPTIONS_PATH "/contact-search-options" + enum { PROP_0, PROP_SOURCE_LIST @@ -166,6 +168,7 @@ book_shell_view_class_init (EBookShellViewClass *class, shell_view_class->icon_name = "x-office-address-book"; shell_view_class->type_module = type_module; shell_view_class->changed = book_shell_view_changed; + shell_view_class->search_options_path = SEARCH_OPTIONS_PATH; shell_view_class->new_shell_sidebar = e_book_shell_sidebar_new; g_object_class_install_property ( diff --git a/doc/reference/shell/tmpl/e-shell-content.sgml b/doc/reference/shell/tmpl/e-shell-content.sgml index 3172527ad6..bb62b33070 100644 --- a/doc/reference/shell/tmpl/e-shell-content.sgml +++ b/doc/reference/shell/tmpl/e-shell-content.sgml @@ -53,12 +53,12 @@ EShellContent </para> -<!-- ##### ARG EShellContent:search-action ##### --> +<!-- ##### ARG EShellContent:search-context ##### --> <para> </para> -<!-- ##### ARG EShellContent:search-context ##### --> +<!-- ##### ARG EShellContent:search-rule ##### --> <para> </para> diff --git a/doc/reference/shell/tmpl/eshell-unused.sgml b/doc/reference/shell/tmpl/eshell-unused.sgml index 448bdb8fcf..b18730bd1e 100644 --- a/doc/reference/shell/tmpl/eshell-unused.sgml +++ b/doc/reference/shell/tmpl/eshell-unused.sgml @@ -406,6 +406,12 @@ intelligent @minor: @revision: +<!-- ##### ARG EShellContent:search-action ##### --> +<para> + +</para> + + <!-- ##### STRUCT EShellContentPrivate ##### --> <para> diff --git a/e-util/e-util.c b/e-util/e-util.c index cec2ec1b69..0055407068 100644 --- a/e-util/e-util.c +++ b/e-util/e-util.c @@ -107,10 +107,10 @@ e_display_help (GtkWindow *parent, /** * e_load_ui_definition: - * @manager: a #GtkUIManager + * @ui_manager: a #GtkUIManager * @basename: basename of the UI definition file * - * Loads a UI definition into @manager from Evolution's UI directory. + * Loads a UI definition into @ui_manager from Evolution's UI directory. * Failure here is fatal, since the application can't function without * its UI definitions. * @@ -118,18 +118,19 @@ e_display_help (GtkWindow *parent, * unmerge the UI with gtk_ui_manager_remove_ui(). **/ guint -e_load_ui_definition (GtkUIManager *manager, +e_load_ui_definition (GtkUIManager *ui_manager, const gchar *basename) { gchar *filename; guint merge_id; GError *error = NULL; - g_return_val_if_fail (GTK_IS_UI_MANAGER (manager), 0); + g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), 0); g_return_val_if_fail (basename != NULL, 0); filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL); - merge_id = gtk_ui_manager_add_ui_from_file (manager, filename, &error); + merge_id = gtk_ui_manager_add_ui_from_file ( + ui_manager, filename, &error); g_free (filename); if (error != NULL) diff --git a/e-util/e-util.h b/e-util/e-util.h index d1a76c8ea6..a6aaa19a99 100644 --- a/e-util/e-util.h +++ b/e-util/e-util.h @@ -44,7 +44,7 @@ typedef enum { const gchar * e_get_user_data_dir (void); void e_display_help (GtkWindow *parent, const gchar *link_id); -guint e_load_ui_definition (GtkUIManager *manager, +guint e_load_ui_definition (GtkUIManager *ui_manager, const gchar *basename); char * e_str_without_underscores (const char *s); diff --git a/shell/Makefile.am b/shell/Makefile.am index 3afa351426..0603f603fa 100644 --- a/shell/Makefile.am +++ b/shell/Makefile.am @@ -17,7 +17,7 @@ INCLUDES = \ -DEVOLUTION_GLADEDIR=\""$(gladedir)"\" \ -DEVOLUTION_HELPDIR=\""$(evolutionhelpdir)"\" \ -DEVOLUTION_MODULEDIR=\""$(moduledir)"\" \ - -DEVOLUTION_RULEDIR=\""$(ruledir)"\" \ + -DEVOLUTION_RULEDIR=\""$(privdatadir)"\" \ -DEVOLUTION_UIDIR=\""$(evolutionuidir)"\" \ -DEVOLUTION_TOOLSDIR=\""$(privlibexecdir)"\" \ -DPREFIX=\""$(prefix)"\" \ diff --git a/shell/e-shell-content.c b/shell/e-shell-content.c index 95cdc2d1cc..c16344ac15 100644 --- a/shell/e-shell-content.c +++ b/shell/e-shell-content.c @@ -22,6 +22,7 @@ #include <glib/gi18n.h> +#include <filter/rule-editor.h> #include <widgets/misc/e-action-combo-box.h> #include <widgets/misc/e-icon-entry.h> @@ -38,7 +39,9 @@ struct _EShellContentPrivate { gpointer shell_view; /* weak pointer */ RuleContext *search_context; - FilterRule *current_query; + FilterRule *search_rule; + gchar *system_filename; + gchar *user_filename; /* Container for the following widgets */ GtkWidget *search_bar; @@ -52,9 +55,6 @@ struct _EShellContentPrivate { GtkWidget *scope_combo_box; GtkStateType search_state; - - GtkRadioAction *search_action; - GtkWidget *search_popup_menu; }; enum { @@ -62,8 +62,8 @@ enum { PROP_FILTER_ACTION, PROP_FILTER_VALUE, PROP_FILTER_VISIBLE, - PROP_SEARCH_ACTION, PROP_SEARCH_CONTEXT, + PROP_SEARCH_RULE, PROP_SEARCH_TEXT, PROP_SEARCH_VALUE, PROP_SEARCH_VISIBLE, @@ -76,42 +76,68 @@ enum { static gpointer parent_class; static void -shell_content_entry_activated_cb (EShellContent *shell_content, - GtkWidget *entry) +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) { - EShellWindow *shell_window; EShellView *shell_view; - GtkAction *action; + EShellWindow *shell_window; + EIconEntry *icon_entry; + GtkWidget *child; + GtkStateType visual_state; + 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); - /* 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); + icon_entry = E_ICON_ENTRY (shell_content->priv->search_entry); + search_text = e_shell_content_get_search_text (shell_content); + + if (search_text != NULL && *search_text != '\0') + visual_state = GTK_STATE_SELECTED; + else + visual_state = GTK_STATE_NORMAL; + + e_icon_entry_set_visual_state (icon_entry, visual_state); + + 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. */ + gtk_widget_grab_focus (gtk_bin_get_child (GTK_BIN (shell_content))); } static void -shell_content_entry_changed_cb (EShellContent *shell_content, - GtkWidget *entry) +shell_content_entry_activated_cb (EShellContent *shell_content, + GtkWidget *entry) { EShellWindow *shell_window; EShellView *shell_view; - GtkStateType state; GtkAction *action; - gboolean sensitive; - const gchar *text; - - text = gtk_entry_get_text (GTK_ENTRY (entry)); - state = shell_content->priv->search_state; - - if (text != NULL && *text != '\0') - sensitive = (state != GTK_STATE_INSENSITIVE); - else - sensitive = (state == GTK_STATE_SELECTED); shell_view = e_shell_content_get_shell_view (shell_content); shell_window = e_shell_view_get_shell_window (shell_view); @@ -120,8 +146,8 @@ shell_content_entry_changed_cb (EShellContent *shell_content, if (!e_shell_view_is_active (shell_view)) return; - action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window); - gtk_action_set_sensitive (action, sensitive); + action = E_SHELL_WINDOW_ACTION_SEARCH_EXECUTE (shell_window); + gtk_action_activate (action); } static gboolean @@ -129,11 +155,16 @@ shell_content_entry_focus_in_cb (EShellContent *shell_content, GdkEventFocus *focus_event, GtkWidget *entry) { - if (shell_content->priv->search_state == GTK_STATE_INSENSITIVE) { + EIconEntry *icon_entry; + GtkStateType visual_state; + + icon_entry = E_ICON_ENTRY (shell_content->priv->search_entry); + visual_state = e_icon_entry_get_visual_state (icon_entry); + + if (visual_state == GTK_STATE_INSENSITIVE) gtk_entry_set_text (GTK_ENTRY (entry), ""); - gtk_widget_modify_text (entry, GTK_STATE_NORMAL, NULL); - shell_content->priv->search_state = GTK_STATE_NORMAL; - } + + e_icon_entry_set_visual_state (icon_entry, GTK_STATE_NORMAL); return FALSE; } @@ -152,8 +183,25 @@ shell_content_entry_key_press_cb (EShellContent *shell_content, GdkEventKey *key_event, GtkWidget *entry) { - /* FIXME */ - return FALSE; + 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 @@ -194,14 +242,14 @@ shell_content_init_search_context (EShellContent *shell_content) rule_context_add_rule, rule_context_next_rule); rule_context_load (context, system_filename, user_filename); - /* Ownership of the strings is passed to the rule context. - * XXX Not sure why this is necessary. */ + /* XXX Not sure why this is necessary. */ g_object_set_data_full ( - G_OBJECT (context), "system", system_filename, g_free); + G_OBJECT (context), "system", + g_strdup (system_filename), g_free); g_object_set_data_full ( - G_OBJECT (context), "user", user_filename, g_free); + G_OBJECT (context), "user", + g_strdup (user_filename), g_free); - /* XXX I don't really understand what this does. */ rule = filter_rule_new (); part = rule_context_next_part (context, NULL); if (part == NULL) @@ -212,6 +260,8 @@ shell_content_init_search_context (EShellContent *shell_content) 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 @@ -252,8 +302,8 @@ shell_content_set_property (GObject *object, g_value_get_boolean (value)); return; - case PROP_SEARCH_ACTION: - e_shell_content_set_search_action ( + case PROP_SEARCH_RULE: + e_shell_content_set_search_rule ( E_SHELL_CONTENT (object), g_value_get_object (value)); return; @@ -329,15 +379,15 @@ shell_content_get_property (GObject *object, E_SHELL_CONTENT (object))); return; - case PROP_SEARCH_ACTION: + case PROP_SEARCH_CONTEXT: g_value_set_object ( - value, e_shell_content_get_search_action ( + value, e_shell_content_get_search_context ( E_SHELL_CONTENT (object))); return; - case PROP_SEARCH_CONTEXT: + case PROP_SEARCH_RULE: g_value_set_object ( - value, e_shell_content_get_search_context ( + value, e_shell_content_get_search_rule ( E_SHELL_CONTENT (object))); return; @@ -435,16 +485,51 @@ shell_content_dispose (GObject *object) priv->scope_combo_box = NULL; } - if (priv->search_action != NULL) { - g_object_unref (priv->search_action); - priv->search_action = 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; + EIconEntry *icon_entry; + GtkAction *action; + + 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); + icon_entry = E_ICON_ENTRY (shell_content->priv->search_entry); + + action = E_SHELL_WINDOW_ACTION_SEARCH_CLEAR (shell_window); + e_icon_entry_add_action_end (icon_entry, action); + + 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_icon_entry_add_action_start (icon_entry, action); +} + +static void shell_content_realize (GtkWidget *widget) { EShellContent *shell_content; @@ -569,6 +654,8 @@ shell_content_class_init (EShellContentClass *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->realize = shell_content_realize; @@ -614,23 +701,23 @@ shell_content_class_init (EShellContentClass *class) g_object_class_install_property ( object_class, - PROP_SEARCH_ACTION, + PROP_SEARCH_CONTEXT, g_param_spec_object ( - "search-action", + "search-context", NULL, NULL, - GTK_TYPE_RADIO_ACTION, - G_PARAM_READWRITE)); + RULE_TYPE_CONTEXT, + G_PARAM_READABLE)); g_object_class_install_property ( object_class, - PROP_SEARCH_CONTEXT, + PROP_SEARCH_RULE, g_param_spec_object ( - "search-context", + "search-rule", NULL, NULL, - RULE_TYPE_CONTEXT, - G_PARAM_READABLE)); + FILTER_TYPE_RULE, + G_PARAM_READWRITE)); g_object_class_install_property ( object_class, @@ -793,9 +880,6 @@ shell_content_init (EShellContent *shell_content) widget, "activate", G_CALLBACK (shell_content_entry_activated_cb), shell_content); g_signal_connect_swapped ( - widget, "changed", - G_CALLBACK (shell_content_entry_changed_cb), shell_content); - g_signal_connect_swapped ( widget, "focus-in-event", G_CALLBACK (shell_content_entry_focus_in_cb), shell_content); g_signal_connect_swapped ( @@ -924,35 +1008,38 @@ e_shell_content_set_filter_visible (EShellContent *shell_content, } } -GtkRadioAction * -e_shell_content_get_search_action (EShellContent *shell_content) +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; +} + +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_action; + return shell_content->priv->search_rule; } void -e_shell_content_set_search_action (EShellContent *shell_content, - GtkRadioAction *search_action) +e_shell_content_set_search_rule (EShellContent *shell_content, + FilterRule *search_rule) { g_return_if_fail (E_IS_SHELL_CONTENT (shell_content)); - if (search_action != NULL) { - g_return_if_fail (GTK_IS_RADIO_ACTION (search_action)); - g_object_ref (search_action); + if (search_rule != NULL) { + g_return_if_fail (IS_FILTER_RULE (search_rule)); + g_object_ref (search_rule); } - shell_content->priv->search_action = search_action; - g_object_notify (G_OBJECT (shell_content), "search-action"); -} - -RuleContext * -e_shell_content_get_search_context (EShellContent *shell_content) -{ - g_return_val_if_fail (E_IS_SHELL_CONTENT (shell_content), NULL); + if (shell_content->priv->search_rule != NULL) + g_object_unref (shell_content->priv->search_rule); - return shell_content->priv->search_context; + shell_content->priv->search_rule = search_rule; + g_object_notify (G_OBJECT (shell_content), "search-rule"); } const gchar * @@ -1108,3 +1195,173 @@ e_shell_content_set_scope_visible (EShellContent *shell_content, 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); +} diff --git a/shell/e-shell-content.h b/shell/e-shell-content.h index 6f4c83338a..1763bf4b22 100644 --- a/shell/e-shell-content.h +++ b/shell/e-shell-content.h @@ -21,7 +21,8 @@ #ifndef E_SHELL_CONTENT_H #define E_SHELL_CONTENT_H -#include <gtk/gtk.h> +#include <e-shell-common.h> +#include <filter/filter-rule.h> #include <filter/rule-context.h> /* Standard GObject macros */ @@ -81,13 +82,11 @@ gboolean e_shell_content_get_filter_visible void e_shell_content_set_filter_visible (EShellContent *shell_content, gboolean filter_visible); -GtkRadioAction *e_shell_content_get_search_action - (EShellContent *shell_content); -void e_shell_content_set_search_action - (EShellContent *shell_content, - GtkRadioAction *search_action); RuleContext * e_shell_content_get_search_context (EShellContent *shell_content); +FilterRule * e_shell_content_get_search_rule (EShellContent *shell_content); +void e_shell_content_set_search_rule (EShellContent *shell_content, + FilterRule *search_rule); const gchar * e_shell_content_get_search_text (EShellContent *shell_content); void e_shell_content_set_search_text (EShellContent *shell_content, const gchar *search_text); @@ -116,6 +115,12 @@ void e_shell_content_save_search_dialog const gchar * e_shell_content_get_view_id (EShellContent *shell_content); void e_shell_content_set_view_id (EShellContent *shell_content, const gchar *view_id); +void e_shell_content_run_advanced_search_dialog + (EShellContent *shell_content); +void e_shell_content_run_edit_searches_dialog + (EShellContent *shell_content); +void e_shell_content_run_save_search_dialog + (EShellContent *shell_content); G_END_DECLS diff --git a/shell/e-shell-sidebar.h b/shell/e-shell-sidebar.h index a6a67a40cc..9a27cba6b5 100644 --- a/shell/e-shell-sidebar.h +++ b/shell/e-shell-sidebar.h @@ -21,7 +21,7 @@ #ifndef E_SHELL_SIDEBAR_H #define E_SHELL_SIDEBAR_H -#include <gtk/gtk.h> +#include <e-shell-common.h> /* Standard GObject macros */ #define E_TYPE_SHELL_SIDEBAR \ diff --git a/shell/e-shell-switcher.h b/shell/e-shell-switcher.h index 67201279d2..499372f81e 100644 --- a/shell/e-shell-switcher.h +++ b/shell/e-shell-switcher.h @@ -21,7 +21,7 @@ #ifndef E_SHELL_SWITCHER_H #define E_SHELL_SWITCHER_H -#include <gtk/gtk.h> +#include <e-shell-common.h> /* Standard GObject macros */ #define E_TYPE_SHELL_SWITCHER \ diff --git a/shell/e-shell-view.h b/shell/e-shell-view.h index 32fd4333f0..92382a9a49 100644 --- a/shell/e-shell-view.h +++ b/shell/e-shell-view.h @@ -72,6 +72,9 @@ struct _EShellViewClass { /* A unique instance is created for each subclass. */ GalViewCollection *view_collection; + /* Path to the search entry popup menu. */ + const gchar *search_options_path; + /* Factory Methods */ GtkWidget * (*new_shell_content) (EShellView *shell_view); GtkWidget * (*new_shell_sidebar) (EShellView *shell_view); diff --git a/shell/e-shell-window-actions.c b/shell/e-shell-window-actions.c index f03c775b3d..5375774702 100644 --- a/shell/e-shell-window-actions.c +++ b/shell/e-shell-window-actions.c @@ -674,6 +674,29 @@ action_contents_cb (GtkAction *action, } static void +action_custom_rule_cb (GtkAction *action, + EShellWindow *shell_window) +{ + FilterRule *rule; + EShellView *shell_view; + EShellContent *shell_content; + const gchar *view_name; + + rule = g_object_get_data (G_OBJECT (action), "rule"); + g_return_if_fail (rule != NULL); + + view_name = e_shell_window_get_active_view (shell_window); + shell_view = e_shell_window_get_view (shell_window, view_name); + shell_content = e_shell_view_get_shell_content (shell_view); + + rule = g_object_get_data (G_OBJECT (action), "rule"); + g_return_if_fail (IS_FILTER_RULE (rule)); + + e_shell_content_set_search_rule (shell_content, rule); + gtk_action_activate (ACTION (SEARCH_EXECUTE)); +} + +static void action_faq_cb (GtkAction *action, EShellWindow *shell_window) { @@ -841,65 +864,85 @@ static void action_search_advanced_cb (GtkAction *action, EShellWindow *shell_window) { + EShellView *shell_view; + EShellContent *shell_content; + const gchar *view_name; + + view_name = e_shell_window_get_active_view (shell_window); + shell_view = e_shell_window_get_view (shell_window, view_name); + shell_content = e_shell_view_get_shell_content (shell_view); + + e_shell_content_run_advanced_search_dialog (shell_content); + e_shell_window_update_search_menu (shell_window); } static void action_search_clear_cb (GtkAction *action, EShellWindow *shell_window) { - EShellContent *shell_content; EShellView *shell_view; + EShellContent *shell_content; const gchar *view_name; view_name = e_shell_window_get_active_view (shell_window); shell_view = e_shell_window_get_view (shell_window, view_name); shell_content = e_shell_view_get_shell_content (shell_view); - e_shell_content_set_search_text (shell_content, ""); + + e_shell_content_set_search_rule (shell_content, NULL); + e_shell_content_set_search_text (shell_content, NULL); + + gtk_action_activate (ACTION (SEARCH_EXECUTE)); + + e_shell_window_update_search_menu (shell_window); } static void action_search_edit_cb (GtkAction *action, EShellWindow *shell_window) { - EShellContent *shell_content; EShellView *shell_view; - RuleContext *context; - RuleEditor *editor; - const gchar *filename; + EShellContent *shell_content; const gchar *view_name; view_name = e_shell_window_get_active_view (shell_window); shell_view = e_shell_window_get_view (shell_window, view_name); shell_content = e_shell_view_get_shell_content (shell_view); - context = e_shell_content_get_search_context (shell_content); - g_return_if_fail (context != NULL); - - /* XXX I don't know why the RuleContext can't just store - * system and user file names properly. Fix this? */ - filename = g_object_get_data (G_OBJECT (context), "user"); - g_return_if_fail (filename != NULL); - 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, filename); - - gtk_widget_destroy (GTK_WIDGET (editor)); + e_shell_content_run_edit_searches_dialog (shell_content); + e_shell_window_update_search_menu (shell_window); } static void -action_search_execute_cb (GtkAction *action, +action_search_options_cb (GtkAction *action, EShellWindow *shell_window) { - gtk_action_set_sensitive (action, FALSE); + EShellView *shell_view; + EShellViewClass *shell_view_class; + const gchar *view_name; + const gchar *widget_path; + + view_name = e_shell_window_get_active_view (shell_window); + shell_view = e_shell_window_get_view (shell_window, view_name); + shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + + widget_path = shell_view_class->search_options_path; + e_shell_window_show_popup_menu (shell_window, widget_path, NULL); } static void action_search_save_cb (GtkAction *action, EShellWindow *shell_window) { + EShellView *shell_view; + EShellContent *shell_content; + const gchar *view_name; + + view_name = e_shell_window_get_active_view (shell_window); + shell_view = e_shell_window_get_view (shell_window, view_name); + shell_content = e_shell_view_get_shell_content (shell_view); + + e_shell_content_run_save_search_dialog (shell_content); + e_shell_window_update_search_menu (shell_window); } static void @@ -1172,7 +1215,14 @@ static GtkActionEntry shell_entries[] = { N_("_Find Now"), NULL, N_("Execute the current search parameters"), - G_CALLBACK (action_search_execute_cb) }, + NULL }, /* Handled by EShellContent and subclasses. */ + + { "search-options", + GTK_STOCK_FIND, + NULL, + NULL, + N_("Click here to change the search type"), + G_CALLBACK (action_search_options_cb) }, { "search-save", NULL, @@ -1507,6 +1557,11 @@ e_shell_window_actions_init (EShellWindow *shell_window) gtk_action_group_set_translation_domain (action_group, domain); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + /* Custom Rule Actions (empty) */ + action_group = shell_window->priv->custom_rule_actions; + gtk_action_group_set_translation_domain (action_group, domain); + gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); + /* Switcher Actions (empty) */ action_group = shell_window->priv->switcher_actions; gtk_action_group_set_translation_domain (action_group, domain); @@ -1805,3 +1860,93 @@ e_shell_window_update_view_menu (EShellWindow *shell_window) action = ACTION (GAL_SAVE_CUSTOM_VIEW); gtk_action_set_visible (action, visible); } + +void +e_shell_window_update_search_menu (EShellWindow *shell_window) +{ + EShellContent *shell_content; + EShellView *shell_view; + EShellViewClass *shell_view_class; + RuleContext *context; + FilterRule *rule; + GtkUIManager *ui_manager; + GtkActionGroup *action_group; + GList *list, *iter; + const gchar *source; + const gchar *view_name; + gboolean sensitive; + guint merge_id; + gint ii = 0; + + ui_manager = e_shell_window_get_ui_manager (shell_window); + view_name = e_shell_window_get_active_view (shell_window); + shell_view = e_shell_window_get_view (shell_window, view_name); + shell_content = e_shell_view_get_shell_content (shell_view); + context = e_shell_content_get_search_context (shell_content); + shell_view_class = E_SHELL_VIEW_GET_CLASS (shell_view); + source = FILTER_SOURCE_INCOMING; + + /* Update sensitivity of search actions. */ + + sensitive = (e_shell_content_get_search_rule (shell_content) != NULL); + gtk_action_set_sensitive (ACTION (SEARCH_CLEAR), sensitive); + gtk_action_set_sensitive (ACTION (SEARCH_SAVE), sensitive); + + sensitive = (shell_view_class->search_options_path != NULL); + gtk_action_set_sensitive (ACTION (SEARCH_OPTIONS), sensitive); + + /* Add custom rules to the Search menu. */ + + action_group = shell_window->priv->custom_rule_actions; + merge_id = shell_window->priv->custom_rule_merge_id; + + /* Unmerge the previous menu. */ + gtk_ui_manager_remove_ui (ui_manager, merge_id); + + /* XXX Annoying that GTK+ doesn't provide a function for this. + * http://bugzilla.gnome.org/show_bug.cgi?id=550485 */ + list = gtk_action_group_list_actions (action_group); + for (iter = list; iter != NULL; iter = iter->next) + gtk_action_group_remove_action (action_group, iter->data); + g_list_free (list); + + rule = rule_context_next_rule (context, NULL, source); + while (rule != NULL) { + GtkAction *action; + gchar *action_name; + gchar *action_label; + + action_name = g_strdup_printf ("custom-rule-%d", ii++); + if (ii < 10) + action_label = g_strdup_printf ( + "_%d. %s", ii, rule->name); + else + action_label = g_strdup (rule->name); + + action = gtk_action_new ( + action_name, action_label, + _("Execute these search parameters"), NULL); + + g_object_set_data_full ( + G_OBJECT (action), + "rule", g_object_ref (rule), + (GDestroyNotify) g_object_unref); + + g_signal_connect ( + action, "activate", + G_CALLBACK (action_custom_rule_cb), shell_window); + + gtk_action_group_add_action (action_group, action); + + gtk_ui_manager_add_ui ( + ui_manager, merge_id, + "/main-menu/search-menu/custom-rules", + action_name, action_name, + GTK_UI_MANAGER_AUTO, FALSE); + + g_free (action_name); + g_free (action_label); + + rule = rule_context_next_rule (context, rule, source); + } +} diff --git a/shell/e-shell-window-actions.h b/shell/e-shell-window-actions.h index 2e3595f0f7..858c8541ce 100644 --- a/shell/e-shell-window-actions.h +++ b/shell/e-shell-window-actions.h @@ -64,6 +64,8 @@ E_SHELL_WINDOW_ACTION ((window), "search-edit") #define E_SHELL_WINDOW_ACTION_SEARCH_EXECUTE(window) \ E_SHELL_WINDOW_ACTION ((window), "search-execute") +#define E_SHELL_WINDOW_ACTION_SEARCH_OPTIONS(window) \ + E_SHELL_WINDOW_ACTION ((window), "search-options") #define E_SHELL_WINDOW_ACTION_SEARCH_SAVE(window) \ E_SHELL_WINDOW_ACTION ((window), "search-save") #define E_SHELL_WINDOW_ACTION_SEND_RECEIVE(window) \ @@ -88,6 +90,8 @@ E_SHELL_WINDOW_ACTION ((window), "work-online") /* Action Groups */ +#define E_SHELL_WINDOW_ACTION_GROUP_CUSTOM_RULES(window) \ + E_SHELL_WINDOW_ACTION_GROUP ((window), "custom-rules") #define E_SHELL_WINDOW_ACTION_GROUP_GAL_VIEW(window) \ E_SHELL_WINDOW_ACTION_GROUP ((window), "gal-view") #define E_SHELL_WINDOW_ACTION_GROUP_NEW_ITEM(window) \ diff --git a/shell/e-shell-window-private.c b/shell/e-shell-window-private.c index 8bfdefc536..782616cea4 100644 --- a/shell/e-shell-window-private.c +++ b/shell/e-shell-window-private.c @@ -191,11 +191,15 @@ e_shell_window_private_init (EShellWindow *shell_window) priv->gal_view_actions = gtk_action_group_new ("gal-view"); priv->new_item_actions = gtk_action_group_new ("new-item"); priv->new_source_actions = gtk_action_group_new ("new-source"); + priv->custom_rule_actions = gtk_action_group_new ("custom-rules"); priv->switcher_actions = gtk_action_group_new ("switcher"); priv->loaded_views = loaded_views; priv->active_view = "unknown"; merge_id = gtk_ui_manager_new_merge_id (priv->ui_manager); + priv->custom_rule_merge_id = merge_id; + + merge_id = gtk_ui_manager_new_merge_id (priv->ui_manager); priv->gal_view_merge_id = merge_id; e_shell_window_actions_init (shell_window); @@ -369,6 +373,7 @@ e_shell_window_private_dispose (EShellWindow *shell_window) DISPOSE (priv->gal_view_actions); DISPOSE (priv->new_item_actions); DISPOSE (priv->new_source_actions); + DISPOSE (priv->custom_rule_actions); DISPOSE (priv->switcher_actions); g_hash_table_remove_all (priv->loaded_views); @@ -426,6 +431,7 @@ e_shell_window_switch_to_view (EShellWindow *shell_window, e_shell_window_update_title (shell_window); e_shell_window_update_new_menu (shell_window); e_shell_window_update_view_menu (shell_window); + e_shell_window_update_search_menu (shell_window); /* Notify all loaded views. */ list = g_hash_table_get_values (shell_window->priv->loaded_views); diff --git a/shell/e-shell-window-private.h b/shell/e-shell-window-private.h index e496a9834e..ed784b2ac7 100644 --- a/shell/e-shell-window-private.h +++ b/shell/e-shell-window-private.h @@ -28,7 +28,6 @@ #include <e-util/e-util.h> #include <e-util/gconf-bridge.h> -#include <filter/rule-editor.h> #include <widgets/misc/e-menu-tool-button.h> #include <widgets/misc/e-online-button.h> #include <widgets/misc/e-search-bar.h> @@ -73,7 +72,9 @@ struct _EShellWindowPrivate { GtkActionGroup *gal_view_actions; GtkActionGroup *new_item_actions; GtkActionGroup *new_source_actions; + GtkActionGroup *custom_rule_actions; GtkActionGroup *switcher_actions; + guint custom_rule_merge_id; guint gal_view_merge_id; /*** Shell Views ***/ @@ -118,6 +119,8 @@ void e_shell_window_update_icon (EShellWindow *shell_window); void e_shell_window_update_title (EShellWindow *shell_window); void e_shell_window_update_new_menu (EShellWindow *shell_window); void e_shell_window_update_view_menu (EShellWindow *shell_window); +void e_shell_window_update_search_menu + (EShellWindow *shell_window); G_END_DECLS diff --git a/ui/evolution-contacts.ui b/ui/evolution-contacts.ui index 7d2379467a..ee97af7169 100644 --- a/ui/evolution-contacts.ui +++ b/ui/evolution-contacts.ui @@ -74,4 +74,11 @@ <menuitem action='contact-clipboard-paste'/> <menuitem action='contact-delete'/> </popup> + <popup name='contact-search-options'> + <menuitem action='contact-search-name-contains'/> + <menuitem action='contact-search-email-begins-with'/> + <menuitem action='contact-search-any-field-contains'/> + <separator/> + <menuitem action='search-advanced'/> + </popup> </ui> diff --git a/ui/evolution-shell.ui b/ui/evolution-shell.ui index 633703f24a..e2f33f8307 100644 --- a/ui/evolution-shell.ui +++ b/ui/evolution-shell.ui @@ -56,6 +56,10 @@ <separator/> <menuitem action='search-save'/> <menuitem action='search-edit'/> + <separator/> + <placeholder name='search-actions'/> + <separator/> + <placeholder name='custom-rules'/> </menu> <menu action='help-menu'> <menuitem action='contents'/> diff --git a/widgets/misc/e-icon-entry.c b/widgets/misc/e-icon-entry.c index 1c9ed12d96..86e42488a7 100644 --- a/widgets/misc/e-icon-entry.c +++ b/widgets/misc/e-icon-entry.c @@ -39,10 +39,16 @@ ((obj), E_TYPE_ICON_ENTRY, EIconEntryPrivate)) struct _EIconEntryPrivate { + GtkStateType visual_state; GtkWidget *entry; GtkWidget *hbox; }; +enum { + PROP_0, + PROP_VISUAL_STATE +}; + static gpointer parent_class; static void @@ -64,6 +70,7 @@ icon_entry_create_proxy (GtkAction *action) { GtkWidget *proxy; GtkWidget *widget; + gchar *tooltip; proxy = gtk_event_box_new (); gtk_event_box_set_visible_window (GTK_EVENT_BOX (proxy), FALSE); @@ -74,6 +81,10 @@ icon_entry_create_proxy (GtkAction *action) gtk_container_add (GTK_CONTAINER (proxy), widget); gtk_widget_show (widget); + g_object_get (action, "tooltip", &tooltip, NULL); + gtk_widget_set_tooltip_text (proxy, tooltip); + g_free (tooltip); + g_signal_connect_swapped ( proxy, "button-press-event", G_CALLBACK (gtk_action_activate), action); @@ -122,55 +133,90 @@ icon_entry_get_borders (GtkWidget *widget, static void icon_entry_paint (GtkWidget *widget, - GdkEventExpose *event) + GdkEventExpose *event) { EIconEntry *entry = E_ICON_ENTRY (widget); GtkWidget *entry_widget = entry->priv->entry; int x = 0, y = 0, width, height, focus_width; gboolean interior_focus; - gtk_widget_style_get (entry_widget, - "interior-focus", &interior_focus, - "focus-line-width", &focus_width, - NULL); + gtk_widget_style_get ( + entry_widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, NULL); gdk_drawable_get_size (widget->window, &width, &height); - if (GTK_WIDGET_HAS_FOCUS (entry_widget) && !interior_focus) - { + if (GTK_WIDGET_HAS_FOCUS (entry_widget) && !interior_focus) { x += focus_width; y += focus_width; width -= 2 * focus_width; height -= 2 * focus_width; } - gtk_paint_flat_box (entry_widget->style, widget->window, - GTK_WIDGET_STATE (entry_widget), GTK_SHADOW_NONE, - NULL, entry_widget, "entry_bg", - /* FIXME: was 0, 0 in gtk_entry_expose, but I think this is correct: */ - x, y, width, height); + gtk_paint_flat_box ( + entry_widget->style, widget->window, + GTK_WIDGET_STATE (entry_widget), GTK_SHADOW_NONE, + NULL, entry_widget, "entry_bg", + /* FIXME: was 0, 0 in gtk_entry_expose, but I think this is correct: */ + x, y, width, height); - gtk_paint_shadow (entry_widget->style, widget->window, - GTK_STATE_NORMAL, GTK_SHADOW_IN, - NULL, entry_widget, "entry", - x, y, width, height); + gtk_paint_shadow ( + entry_widget->style, widget->window, + GTK_STATE_NORMAL, GTK_SHADOW_IN, + NULL, entry_widget, "entry", + x, y, width, height); - if (GTK_WIDGET_HAS_FOCUS (entry_widget) && !interior_focus) - { + if (GTK_WIDGET_HAS_FOCUS (entry_widget) && !interior_focus) { x -= focus_width; y -= focus_width; width += 2 * focus_width; height += 2 * focus_width; - gtk_paint_focus (entry_widget->style, widget->window, - GTK_WIDGET_STATE (entry_widget), - NULL, entry_widget, "entry", - /* FIXME: was 0, 0 in gtk_entry_draw_frame, but I think this is correct: */ - x, y, width, height); + gtk_paint_focus ( + entry_widget->style, widget->window, + GTK_WIDGET_STATE (entry_widget), + NULL, entry_widget, "entry", + /* FIXME: was 0, 0 in gtk_entry_draw_frame, but I think this is correct: */ + x, y, width, height); } } static void +icon_entry_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_VISUAL_STATE: + e_icon_entry_set_visual_state ( + E_ICON_ENTRY (object), + g_value_get_enum (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +icon_entry_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_VISUAL_STATE: + g_value_set_enum ( + value, e_icon_entry_get_visual_state ( + E_ICON_ENTRY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void icon_entry_dispose (GObject *object) { EIconEntryPrivate *priv; @@ -324,6 +370,8 @@ icon_entry_class_init (EIconEntryClass *class) g_type_class_add_private (class, sizeof (EIconEntryPrivate)); object_class = G_OBJECT_CLASS (class); + object_class->set_property = icon_entry_set_property; + object_class->get_property = icon_entry_get_property; object_class->dispose = icon_entry_dispose; widget_class = GTK_WIDGET_CLASS (class); @@ -331,6 +379,17 @@ icon_entry_class_init (EIconEntryClass *class) widget_class->size_request = icon_entry_size_request; widget_class->size_allocate = icon_entry_size_allocate; widget_class->expose_event = icon_entry_expose; + + g_object_class_install_property ( + object_class, + PROP_VISUAL_STATE, + g_param_spec_enum ( + "visual-state", + NULL, + NULL, + GTK_TYPE_STATE_TYPE, + GTK_STATE_NORMAL, + G_PARAM_READWRITE)); } static void @@ -340,6 +399,7 @@ icon_entry_init (EIconEntry *icon_entry) GtkWidget *container; icon_entry->priv = E_ICON_ENTRY_GET_PRIVATE (icon_entry); + icon_entry->priv->visual_state = GTK_STATE_NORMAL; GTK_WIDGET_UNSET_FLAGS (icon_entry, GTK_NO_WINDOW); @@ -438,3 +498,46 @@ e_icon_entry_add_action_end (EIconEntry *icon_entry, proxy = icon_entry_create_proxy (action); gtk_box_pack_end (box, proxy, FALSE, FALSE, 2); } + +GtkStateType +e_icon_entry_get_visual_state (EIconEntry *icon_entry) +{ + g_return_val_if_fail (E_IS_ICON_ENTRY (icon_entry), GTK_STATE_NORMAL); + + return icon_entry->priv->visual_state; +} + +void +e_icon_entry_set_visual_state (EIconEntry *icon_entry, + GtkStateType visual_state) +{ + GtkWidget *widget; + const GdkColor *base_color; + const GdkColor *text_color; + + g_return_if_fail (E_IS_ICON_ENTRY (icon_entry)); + + if (visual_state == GTK_STATE_NORMAL) { + base_color = NULL; + text_color = NULL; + } else { + GtkStyle *style; + + style = gtk_widget_get_default_style (); + base_color = &style->base[visual_state]; + text_color = &style->text[visual_state]; + } + + widget = GTK_WIDGET (icon_entry); + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, base_color); + + widget = icon_entry->priv->entry; + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, base_color); + gtk_widget_modify_text (widget, GTK_STATE_NORMAL, text_color); + + widget = icon_entry->priv->hbox; + gtk_widget_modify_base (widget, GTK_STATE_NORMAL, base_color); + + icon_entry->priv->visual_state = visual_state; + g_object_notify (G_OBJECT (icon_entry), "visual-state"); +} diff --git a/widgets/misc/e-icon-entry.h b/widgets/misc/e-icon-entry.h index 110720ab19..65aff61978 100644 --- a/widgets/misc/e-icon-entry.h +++ b/widgets/misc/e-icon-entry.h @@ -82,6 +82,9 @@ void e_icon_entry_add_action_start (EIconEntry *entry, GtkAction *action); void e_icon_entry_add_action_end (EIconEntry *entry, GtkAction *action); +GtkStateType e_icon_entry_get_visual_state (EIconEntry *entry); +void e_icon_entry_set_visual_state (EIconEntry *entry, + GtkStateType visual_state); G_END_DECLS |