diff options
author | Matthew Barnes <mbarnes@redhat.com> | 2010-01-18 06:47:08 +0800 |
---|---|---|
committer | Matthew Barnes <mbarnes@redhat.com> | 2010-01-18 12:54:06 +0800 |
commit | 39ee1b7890e06779b47f0fc11925d12caa206c39 (patch) | |
tree | 563aeba407e45e91cb9986caa159d11dca7e4823 /mail | |
parent | 3e7c7808cc65c22bc40a7d1d30ffa0044097a6ff (diff) | |
download | gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar.gz gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar.bz2 gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar.lz gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar.xz gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.tar.zst gsoc2013-evolution-39ee1b7890e06779b47f0fc11925d12caa206c39.zip |
Give all preview panes a search bar.
Use Shift+Ctrl+F as the accelerator for consistency with the mailer.
Diffstat (limited to 'mail')
-rw-r--r-- | mail/Makefile.am | 4 | ||||
-rw-r--r-- | mail/e-mail-browser.c | 30 | ||||
-rw-r--r-- | mail/e-mail-search-bar.c | 823 | ||||
-rw-r--r-- | mail/e-mail-search-bar.h | 87 | ||||
-rw-r--r-- | mail/e-searching-tokenizer.c | 1195 | ||||
-rw-r--r-- | mail/e-searching-tokenizer.h | 92 |
6 files changed, 10 insertions, 2221 deletions
diff --git a/mail/Makefile.am b/mail/Makefile.am index 2e6a69a7f6..c31b4cd406 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -48,11 +48,9 @@ mailinclude_HEADERS = \ e-mail-migrate.h \ e-mail-reader.h \ e-mail-reader-utils.h \ - e-mail-search-bar.h \ e-mail-sidebar.h \ e-mail-store.h \ e-mail-tag-editor.h \ - e-searching-tokenizer.h \ em-account-editor.h \ em-composer-utils.h \ em-config.h \ @@ -107,11 +105,9 @@ libevolution_mail_la_SOURCES = \ e-mail-migrate.c \ e-mail-reader.c \ e-mail-reader-utils.c \ - e-mail-search-bar.c \ e-mail-sidebar.c \ e-mail-store.c \ e-mail-tag-editor.c \ - e-searching-tokenizer.c \ em-account-editor.c \ em-composer-utils.c \ em-config.c \ diff --git a/mail/e-mail-browser.c b/mail/e-mail-browser.c index ab27694778..b0172ad3c2 100644 --- a/mail/e-mail-browser.c +++ b/mail/e-mail-browser.c @@ -29,10 +29,10 @@ #include "e-util/e-plugin-ui.h" #include "e-util/gconf-bridge.h" #include "shell/e-shell.h" +#include "widgets/misc/e-preview-pane.h" #include "mail/e-mail-reader.h" #include "mail/e-mail-reader-utils.h" -#include "mail/e-mail-search-bar.h" #include "mail/em-folder-tree-model.h" #include "mail/em-format-html-display.h" #include "mail/message-list.h" @@ -388,6 +388,7 @@ mail_browser_constructed (GObject *object) EMailReader *reader; EShellBackend *shell_backend; EShell *shell; + ESearchBar *search_bar; GConfBridge *bridge; GtkAccelGroup *accel_group; GtkActionGroup *action_group; @@ -468,15 +469,6 @@ mail_browser_constructed (GObject *object) priv->statusbar = g_object_ref (widget); gtk_widget_show (widget); - widget = e_mail_search_bar_new (web_view); - gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 0); - priv->search_bar = g_object_ref (widget); - gtk_widget_hide (widget); - - g_signal_connect_swapped ( - widget, "changed", - G_CALLBACK (em_format_redraw), html_display); - widget = gtk_ui_manager_get_widget (ui_manager, "/main-menu"); gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); priv->main_menu = g_object_ref (widget); @@ -487,20 +479,18 @@ mail_browser_constructed (GObject *object) priv->main_toolbar = g_object_ref (widget); gtk_widget_show (widget); - widget = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy ( - GTK_SCROLLED_WINDOW (widget), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type ( - GTK_SCROLLED_WINDOW (widget), GTK_SHADOW_IN); + gtk_widget_show (GTK_WIDGET (web_view)); + + widget = e_preview_pane_new (web_view); gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); gtk_widget_show (widget); - container = widget; + search_bar = e_preview_pane_get_search_bar (E_PREVIEW_PANE (widget)); + priv->search_bar = g_object_ref (search_bar); - widget = GTK_WIDGET (EM_FORMAT_HTML (html_display)->html); - gtk_container_add (GTK_CONTAINER (container), widget); - gtk_widget_show (widget); + g_signal_connect_swapped ( + search_bar, "changed", + G_CALLBACK (em_format_redraw), priv->html_display); /* Bind GObject properties to GConf keys. */ diff --git a/mail/e-mail-search-bar.c b/mail/e-mail-search-bar.c deleted file mode 100644 index c4b552a8cc..0000000000 --- a/mail/e-mail-search-bar.c +++ /dev/null @@ -1,823 +0,0 @@ -/* - * e-mail-search-bar.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-mail-search-bar.h" - -#include <glib/gi18n.h> -#include <gdk/gdkkeysyms.h> -#include <gtkhtml/gtkhtml-search.h> - -#include "e-util/e-binding.h" - -#define E_MAIL_SEARCH_BAR_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_SEARCH_BAR, EMailSearchBarPrivate)) - -struct _EMailSearchBarPrivate { - EWebView *web_view; - GtkWidget *entry; - GtkWidget *case_sensitive_button; - GtkWidget *wrapped_next_box; - GtkWidget *wrapped_prev_box; - GtkWidget *matches_label; - - ESearchingTokenizer *tokenizer; - gchar *active_search; - - guint rerun_search : 1; -}; - -enum { - PROP_0, - PROP_ACTIVE_SEARCH, - PROP_CASE_SENSITIVE, - PROP_TEXT, - PROP_WEB_VIEW -}; - -enum { - CHANGED, - CLEAR, - LAST_SIGNAL -}; - -static gpointer parent_class; -static guint signals[LAST_SIGNAL]; - -static void -mail_search_bar_update_matches (EMailSearchBar *search_bar) -{ - ESearchingTokenizer *tokenizer; - GtkWidget *matches_label; - gint matches; - gchar *text; - - tokenizer = e_mail_search_bar_get_tokenizer (search_bar); - matches_label = search_bar->priv->matches_label; - - matches = e_searching_tokenizer_match_count (tokenizer); - text = g_strdup_printf (_("Matches: %d"), matches); - - gtk_label_set_text (GTK_LABEL (matches_label), text); - gtk_widget_show (matches_label); - - g_free (text); -} - -static void -mail_search_bar_update_tokenizer (EMailSearchBar *search_bar) -{ - ESearchingTokenizer *tokenizer; - gboolean case_sensitive; - gchar *active_search; - - tokenizer = e_mail_search_bar_get_tokenizer (search_bar); - case_sensitive = e_mail_search_bar_get_case_sensitive (search_bar); - - if (GTK_WIDGET_VISIBLE (search_bar)) - active_search = search_bar->priv->active_search; - else - active_search = NULL; - - e_searching_tokenizer_set_primary_case_sensitivity ( - tokenizer, case_sensitive); - e_searching_tokenizer_set_primary_search_string ( - tokenizer, active_search); - - e_mail_search_bar_changed (search_bar); -} - -static void -mail_search_bar_find (EMailSearchBar *search_bar, - gboolean search_forward) -{ - EWebView *web_view; - GtkWidget *widget; - gboolean case_sensitive; - gboolean new_search; - gboolean wrapped = FALSE; - gchar *text; - - web_view = e_mail_search_bar_get_web_view (search_bar); - case_sensitive = e_mail_search_bar_get_case_sensitive (search_bar); - text = e_mail_search_bar_get_text (search_bar); - - if (text == NULL || *text == '\0') { - e_mail_search_bar_clear (search_bar); - g_free (text); - return; - } - - new_search = - (search_bar->priv->active_search == NULL) || - (g_strcmp0 (text, search_bar->priv->active_search) != 0); - - /* XXX On a new search, the HTMLEngine's search state gets - * destroyed when we redraw the message with highlighted - * matches (EMHTMLStream's write() method triggers this, - * but it's really GtkHtml's fault). That's why the first - * match isn't selected automatically. It also causes - * gtk_html_engine_search_next() to return FALSE, which we - * assume to mean the search wrapped. - * - * So to avoid mistakenly thinking the search wrapped when - * it hasn't, we have to trap the first button click after a - * search and re-run the search to recreate the HTMLEngine's - * search state, so that gtk_html_engine_search_next() will - * succeed. */ - if (new_search) { - g_free (search_bar->priv->active_search); - search_bar->priv->active_search = text; - search_bar->priv->rerun_search = TRUE; - mail_search_bar_update_tokenizer (search_bar); - } else if (search_bar->priv->rerun_search) { - gtk_html_engine_search ( - GTK_HTML (web_view), - search_bar->priv->active_search, - case_sensitive, search_forward, FALSE); - search_bar->priv->rerun_search = FALSE; - g_free (text); - } else { - gtk_html_engine_search_set_forward ( - GTK_HTML (web_view), search_forward); - if (!gtk_html_engine_search_next (GTK_HTML (web_view))) - wrapped = TRUE; - g_free (text); - } - - if (new_search || wrapped) - gtk_html_engine_search ( - GTK_HTML (web_view), - search_bar->priv->active_search, - case_sensitive, search_forward, FALSE); - - g_object_notify (G_OBJECT (search_bar), "active-search"); - - /* Update wrapped label visibility. */ - - widget = search_bar->priv->wrapped_next_box; - - if (wrapped && search_forward) - gtk_widget_show (widget); - else - gtk_widget_hide (widget); - - widget = search_bar->priv->wrapped_prev_box; - - if (wrapped && !search_forward) - gtk_widget_show (widget); - else - gtk_widget_hide (widget); -} - -static void -mail_search_bar_changed_cb (EMailSearchBar *search_bar) -{ - g_object_notify (G_OBJECT (search_bar), "text"); -} - -static void -mail_search_bar_find_next_cb (EMailSearchBar *search_bar) -{ - mail_search_bar_find (search_bar, TRUE); -} - -static void -mail_search_bar_find_previous_cb (EMailSearchBar *search_bar) -{ - mail_search_bar_find (search_bar, FALSE); -} - -static void -mail_search_bar_icon_release_cb (EMailSearchBar *search_bar, - GtkEntryIconPosition icon_pos, - GdkEvent *event) -{ - g_return_if_fail (icon_pos == GTK_ENTRY_ICON_SECONDARY); - - e_mail_search_bar_clear (search_bar); - gtk_widget_grab_focus (search_bar->priv->entry); -} - -static void -mail_search_bar_toggled_cb (EMailSearchBar *search_bar) -{ - g_free (search_bar->priv->active_search); - search_bar->priv->active_search = NULL; - - g_object_notify (G_OBJECT (search_bar), "active-search"); - g_object_notify (G_OBJECT (search_bar), "case-sensitive"); -} - -static void -mail_search_bar_set_web_view (EMailSearchBar *search_bar, - EWebView *web_view) -{ - GtkHTML *html; - ESearchingTokenizer *tokenizer; - - g_return_if_fail (search_bar->priv->web_view == NULL); - - search_bar->priv->web_view = g_object_ref (web_view); - - html = GTK_HTML (web_view); - tokenizer = e_mail_search_bar_get_tokenizer (search_bar); - gtk_html_set_tokenizer (html, HTML_TOKENIZER (tokenizer)); -} - -static void -mail_search_bar_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_CASE_SENSITIVE: - e_mail_search_bar_set_case_sensitive ( - E_MAIL_SEARCH_BAR (object), - g_value_get_boolean (value)); - return; - - case PROP_TEXT: - e_mail_search_bar_set_text ( - E_MAIL_SEARCH_BAR (object), - g_value_get_string (value)); - return; - - case PROP_WEB_VIEW: - mail_search_bar_set_web_view ( - E_MAIL_SEARCH_BAR (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -mail_search_bar_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_ACTIVE_SEARCH: - g_value_set_boolean ( - value, e_mail_search_bar_get_active_search ( - E_MAIL_SEARCH_BAR (object))); - return; - - case PROP_CASE_SENSITIVE: - g_value_set_boolean ( - value, e_mail_search_bar_get_case_sensitive ( - E_MAIL_SEARCH_BAR (object))); - return; - - case PROP_TEXT: - g_value_take_string ( - value, e_mail_search_bar_get_text ( - E_MAIL_SEARCH_BAR (object))); - return; - - case PROP_WEB_VIEW: - g_value_set_object ( - value, e_mail_search_bar_get_web_view ( - E_MAIL_SEARCH_BAR (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -mail_search_bar_dispose (GObject *object) -{ - EMailSearchBarPrivate *priv; - - priv = E_MAIL_SEARCH_BAR_GET_PRIVATE (object); - - if (priv->web_view != NULL) { - g_object_unref (priv->web_view); - priv->web_view = NULL; - } - - if (priv->entry != NULL) { - g_object_unref (priv->entry); - priv->entry = NULL; - } - - if (priv->case_sensitive_button != NULL) { - g_object_unref (priv->case_sensitive_button); - priv->case_sensitive_button = NULL; - } - - if (priv->wrapped_next_box != NULL) { - g_object_unref (priv->wrapped_next_box); - priv->wrapped_next_box = NULL; - } - - if (priv->wrapped_prev_box != NULL) { - g_object_unref (priv->wrapped_prev_box); - priv->wrapped_prev_box = NULL; - } - - if (priv->matches_label != NULL) { - g_object_unref (priv->matches_label); - priv->matches_label = NULL; - } - - if (priv->tokenizer != NULL) { - g_object_unref (priv->tokenizer); - priv->tokenizer = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (parent_class)->dispose (object); -} - -static void -mail_search_bar_finalize (GObject *object) -{ - EMailSearchBarPrivate *priv; - - priv = E_MAIL_SEARCH_BAR_GET_PRIVATE (object); - - g_free (priv->active_search); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -mail_search_bar_constructed (GObject *object) -{ - EMailSearchBarPrivate *priv; - - priv = E_MAIL_SEARCH_BAR_GET_PRIVATE (object); - - e_mutual_binding_new ( - object, "case-sensitive", - priv->case_sensitive_button, "active"); -} - -static void -mail_search_bar_show (GtkWidget *widget) -{ - EMailSearchBar *search_bar; - - search_bar = E_MAIL_SEARCH_BAR (widget); - - /* Chain up to parent's show() method. */ - GTK_WIDGET_CLASS (parent_class)->show (widget); - - gtk_widget_grab_focus (search_bar->priv->entry); - - mail_search_bar_update_tokenizer (search_bar); -} - -static void -mail_search_bar_hide (GtkWidget *widget) -{ - EMailSearchBar *search_bar; - - search_bar = E_MAIL_SEARCH_BAR (widget); - - /* Chain up to parent's hide() method. */ - GTK_WIDGET_CLASS (parent_class)->hide (widget); - - mail_search_bar_update_tokenizer (search_bar); -} - -static gboolean -mail_search_bar_key_press_event (GtkWidget *widget, - GdkEventKey *event) -{ - if (event->keyval == GDK_Escape) { - gtk_widget_hide (widget); - return TRUE; - } - - /* Chain up to parent's key_press_event() method. */ - return GTK_WIDGET_CLASS (parent_class)-> - key_press_event (widget, event); -} - -static void -mail_search_bar_clear (EMailSearchBar *search_bar) -{ - g_free (search_bar->priv->active_search); - search_bar->priv->active_search = NULL; - - gtk_entry_set_text (GTK_ENTRY (search_bar->priv->entry), ""); - - gtk_widget_hide (search_bar->priv->wrapped_next_box); - gtk_widget_hide (search_bar->priv->wrapped_prev_box); - gtk_widget_hide (search_bar->priv->matches_label); - - mail_search_bar_update_tokenizer (search_bar); - - g_object_notify (G_OBJECT (search_bar), "active-search"); -} - -static void -mail_search_bar_class_init (EMailSearchBarClass *class) -{ - GObjectClass *object_class; - GtkWidgetClass *widget_class; - - parent_class = g_type_class_peek_parent (class); - g_type_class_add_private (class, sizeof (EMailSearchBarPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = mail_search_bar_set_property; - object_class->get_property = mail_search_bar_get_property; - object_class->dispose = mail_search_bar_dispose; - object_class->finalize = mail_search_bar_finalize; - object_class->constructed = mail_search_bar_constructed; - - widget_class = GTK_WIDGET_CLASS (class); - widget_class->show = mail_search_bar_show; - widget_class->hide = mail_search_bar_hide; - widget_class->key_press_event = mail_search_bar_key_press_event; - - class->clear = mail_search_bar_clear; - - g_object_class_install_property ( - object_class, - PROP_ACTIVE_SEARCH, - g_param_spec_boolean ( - "active-search", - "Active Search", - NULL, - FALSE, - G_PARAM_READABLE)); - - g_object_class_install_property ( - object_class, - PROP_CASE_SENSITIVE, - g_param_spec_boolean ( - "case-sensitive", - "Case Sensitive", - NULL, - FALSE, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_TEXT, - g_param_spec_string ( - "text", - "Search Text", - NULL, - NULL, - G_PARAM_READWRITE)); - - g_object_class_install_property ( - object_class, - PROP_WEB_VIEW, - g_param_spec_object ( - "web-view", - "Web View", - NULL, - E_TYPE_WEB_VIEW, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); - - signals[CHANGED] = g_signal_new ( - "changed", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EMailSearchBarClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[CLEAR] = g_signal_new ( - "clear", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EMailSearchBarClass, clear), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -mail_search_bar_init (EMailSearchBar *search_bar) -{ - GtkWidget *label; - GtkWidget *widget; - GtkWidget *container; - - search_bar->priv = E_MAIL_SEARCH_BAR_GET_PRIVATE (search_bar); - search_bar->priv->tokenizer = e_searching_tokenizer_new (); - - g_signal_connect_swapped ( - search_bar->priv->tokenizer, "match", - G_CALLBACK (mail_search_bar_update_matches), search_bar); - - gtk_box_set_spacing (GTK_BOX (search_bar), 12); - - container = GTK_WIDGET (search_bar); - - widget = gtk_hbox_new (FALSE, 1); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - container = widget; - - widget = gtk_button_new (); - gtk_button_set_image ( - GTK_BUTTON (widget), gtk_image_new_from_stock ( - GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU)); - gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE); - gtk_widget_set_tooltip_text (widget, _("Close the find bar")); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - g_signal_connect_swapped ( - widget, "clicked", - G_CALLBACK (gtk_widget_hide), search_bar); - - widget = gtk_label_new_with_mnemonic (_("Fin_d:")); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 3); - gtk_widget_show (widget); - - label = widget; - - widget = gtk_entry_new (); - gtk_entry_set_icon_from_stock ( - GTK_ENTRY (widget), GTK_ENTRY_ICON_SECONDARY, - GTK_STOCK_CLEAR); - gtk_entry_set_icon_tooltip_text ( - GTK_ENTRY (widget), GTK_ENTRY_ICON_SECONDARY, - _("Clear the search")); - gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget); - gtk_widget_set_size_request (widget, 200, -1); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - search_bar->priv->entry = g_object_ref (widget); - gtk_widget_show (widget); - - e_binding_new ( - search_bar, "active-search", - widget, "secondary-icon-sensitive"); - - g_signal_connect_swapped ( - widget, "activate", - G_CALLBACK (mail_search_bar_find_next_cb), search_bar); - - g_signal_connect_swapped ( - widget, "changed", - G_CALLBACK (mail_search_bar_changed_cb), search_bar); - - g_signal_connect_swapped ( - widget, "icon-release", - G_CALLBACK (mail_search_bar_icon_release_cb), search_bar); - - widget = gtk_button_new_with_mnemonic (_("_Previous")); - gtk_button_set_image ( - GTK_BUTTON (widget), gtk_image_new_from_stock ( - GTK_STOCK_GO_BACK, GTK_ICON_SIZE_MENU)); - gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE); - gtk_widget_set_tooltip_text ( - widget, _("Find the previous occurrence of the phrase")); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - e_binding_new (search_bar, "active-search", widget, "sensitive"); - - g_signal_connect_swapped ( - widget, "clicked", - G_CALLBACK (mail_search_bar_find_previous_cb), search_bar); - - widget = gtk_button_new_with_mnemonic (_("_Next")); - gtk_button_set_image ( - GTK_BUTTON (widget), gtk_image_new_from_stock ( - GTK_STOCK_GO_FORWARD, GTK_ICON_SIZE_MENU)); - gtk_button_set_relief (GTK_BUTTON (widget), GTK_RELIEF_NONE); - gtk_widget_set_tooltip_text ( - widget, _("Find the next occurrence of the phrase")); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - e_binding_new (search_bar, "active-search", widget, "sensitive"); - - g_signal_connect_swapped ( - widget, "clicked", - G_CALLBACK (mail_search_bar_find_next_cb), search_bar); - - widget = gtk_check_button_new_with_mnemonic (_("Mat_ch case")); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - search_bar->priv->case_sensitive_button = g_object_ref (widget); - gtk_widget_show (widget); - - g_signal_connect_swapped ( - widget, "toggled", - G_CALLBACK (mail_search_bar_toggled_cb), search_bar); - - g_signal_connect_swapped ( - widget, "toggled", - G_CALLBACK (mail_search_bar_find_next_cb), search_bar); - - container = GTK_WIDGET (search_bar); - - widget = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); - search_bar->priv->wrapped_next_box = g_object_ref (widget); - gtk_widget_hide (widget); - - container = widget; - - widget = gtk_image_new_from_icon_name ( - "wrapped", GTK_ICON_SIZE_MENU); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - widget = gtk_label_new ( - _("Reached bottom of page, continued from top")); - gtk_label_set_ellipsize ( - GTK_LABEL (widget), PANGO_ELLIPSIZE_END); - gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); - gtk_widget_show (widget); - - container = GTK_WIDGET (search_bar); - - widget = gtk_hbox_new (FALSE, 6); - gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); - search_bar->priv->wrapped_prev_box = g_object_ref (widget); - gtk_widget_hide (widget); - - container = widget; - - widget = gtk_image_new_from_icon_name ( - "wrapped", GTK_ICON_SIZE_MENU); - gtk_box_pack_start (GTK_BOX (container), widget, FALSE, FALSE, 0); - gtk_widget_show (widget); - - widget = gtk_label_new ( - _("Reached top of page, continued from bottom")); - gtk_label_set_ellipsize ( - GTK_LABEL (widget), PANGO_ELLIPSIZE_END); - gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.5); - gtk_box_pack_start (GTK_BOX (container), widget, TRUE, TRUE, 0); - gtk_widget_show (widget); - - container = GTK_WIDGET (search_bar); - - widget = gtk_label_new (NULL); - gtk_box_pack_end (GTK_BOX (container), widget, FALSE, FALSE, 12); - search_bar->priv->matches_label = g_object_ref (widget); - gtk_widget_show (widget); -} - -GType -e_mail_search_bar_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (EMailSearchBarClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) mail_search_bar_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (EMailSearchBar), - 0, /* n_preallocs */ - (GInstanceInitFunc) mail_search_bar_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - GTK_TYPE_HBOX, "EMailSearchBar", &type_info, 0); - } - - return type; -} - -GtkWidget * -e_mail_search_bar_new (EWebView *web_view) -{ - g_return_val_if_fail (E_IS_WEB_VIEW (web_view), NULL); - - return g_object_new ( - E_TYPE_MAIL_SEARCH_BAR, "web-view", web_view, NULL); -} - -void -e_mail_search_bar_clear (EMailSearchBar *search_bar) -{ - g_return_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar)); - - g_signal_emit (search_bar, signals[CLEAR], 0); -} - -void -e_mail_search_bar_changed (EMailSearchBar *search_bar) -{ - g_return_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar)); - - g_signal_emit (search_bar, signals[CHANGED], 0); -} - -EWebView * -e_mail_search_bar_get_web_view (EMailSearchBar *search_bar) -{ - g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), NULL); - - return search_bar->priv->web_view; -} - -ESearchingTokenizer * -e_mail_search_bar_get_tokenizer (EMailSearchBar *search_bar) -{ - g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), NULL); - - return search_bar->priv->tokenizer; -} - -gboolean -e_mail_search_bar_get_active_search (EMailSearchBar *search_bar) -{ - g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), FALSE); - - return (search_bar->priv->active_search != NULL); -} - -gboolean -e_mail_search_bar_get_case_sensitive (EMailSearchBar *search_bar) -{ - GtkToggleButton *button; - - g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), FALSE); - - button = GTK_TOGGLE_BUTTON (search_bar->priv->case_sensitive_button); - - return gtk_toggle_button_get_active (button); -} - -void -e_mail_search_bar_set_case_sensitive (EMailSearchBar *search_bar, - gboolean case_sensitive) -{ - GtkToggleButton *button; - - g_return_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar)); - - button = GTK_TOGGLE_BUTTON (search_bar->priv->case_sensitive_button); - - gtk_toggle_button_set_active (button, case_sensitive); - - g_object_notify (G_OBJECT (search_bar), "case-sensitive"); -} - -gchar * -e_mail_search_bar_get_text (EMailSearchBar *search_bar) -{ - GtkEntry *entry; - const gchar *text; - - g_return_val_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar), NULL); - - entry = GTK_ENTRY (search_bar->priv->entry); - text = gtk_entry_get_text (entry); - - return g_strstrip (g_strdup (text)); -} - -void -e_mail_search_bar_set_text (EMailSearchBar *search_bar, - const gchar *text) -{ - GtkEntry *entry; - - g_return_if_fail (E_IS_MAIL_SEARCH_BAR (search_bar)); - - entry = GTK_ENTRY (search_bar->priv->entry); - - if (text == NULL) - text = ""; - - /* This will trigger a "notify::text" signal. */ - gtk_entry_set_text (entry, text); -} diff --git a/mail/e-mail-search-bar.h b/mail/e-mail-search-bar.h deleted file mode 100644 index 69fa746687..0000000000 --- a/mail/e-mail-search-bar.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * e-mail-search-bar.h - * - * 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) - * - */ - -#ifndef E_MAIL_SEARCH_BAR_H -#define E_MAIL_SEARCH_BAR_H - -#include <gtk/gtk.h> -#include <mail/e-searching-tokenizer.h> -#include <misc/e-web-view.h> - -/* Standard GObject macros */ -#define E_TYPE_MAIL_SEARCH_BAR \ - (e_mail_search_bar_get_type ()) -#define E_MAIL_SEARCH_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_MAIL_SEARCH_BAR, EMailSearchBar)) -#define E_MAIL_SEARCH_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_MAIL_SEARCH_BAR, EMailSearchBarClass)) -#define E_IS_MAIL_SEARCH_BAR(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_MAIL_SEARCH_BAR)) -#define E_IS_MAIL_SEARCH_BAR_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_MAIL_SEARCH_BAR)) -#define E_MAIL_SEARCH_BAR_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_MAIL_SEARCH_BAR, EMailSearchBarClass)) - -G_BEGIN_DECLS - -typedef struct _EMailSearchBar EMailSearchBar; -typedef struct _EMailSearchBarClass EMailSearchBarClass; -typedef struct _EMailSearchBarPrivate EMailSearchBarPrivate; - -struct _EMailSearchBar { - GtkHBox parent; - EMailSearchBarPrivate *priv; -}; - -struct _EMailSearchBarClass { - GtkHBoxClass parent_class; - - /* Signals */ - void (*changed) (EMailSearchBar *search_bar); - void (*clear) (EMailSearchBar *search_bar); -}; - -GType e_mail_search_bar_get_type (void); -GtkWidget * e_mail_search_bar_new (EWebView *web_view); -void e_mail_search_bar_clear (EMailSearchBar *search_bar); -void e_mail_search_bar_changed (EMailSearchBar *search_bar); -EWebView * e_mail_search_bar_get_web_view (EMailSearchBar *search_bar); -ESearchingTokenizer * - e_mail_search_bar_get_tokenizer (EMailSearchBar *search_bar); -gboolean e_mail_search_bar_get_active_search - (EMailSearchBar *search_bar); -gboolean e_mail_search_bar_get_case_sensitive - (EMailSearchBar *search_bar); -void e_mail_search_bar_set_case_sensitive - (EMailSearchBar *search_bar, - gboolean case_sensitive); -gchar * e_mail_search_bar_get_text (EMailSearchBar *search_bar); -void e_mail_search_bar_set_text (EMailSearchBar *search_bar, - const gchar *text); - -G_END_DECLS - -#endif /* E_MAIL_SEARCH_BAR_H */ diff --git a/mail/e-searching-tokenizer.c b/mail/e-searching-tokenizer.c deleted file mode 100644 index 8920a7dd21..0000000000 --- a/mail/e-searching-tokenizer.c +++ /dev/null @@ -1,1195 +0,0 @@ -/* - * 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/> - * - * - * Authors: - * Developed by Jon Trowbridge <trow@ximian.com> - * Rewritten significantly to handle multiple strings and improve performance - * by Michael Zucchi <notzed@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <ctype.h> - -#include "e-searching-tokenizer.h" - -#include "libedataserver/e-memory.h" - -#define d(x) - -#define E_SEARCHING_TOKENIZER_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerPrivate)) - -enum { - MATCH_SIGNAL, - LAST_SIGNAL -}; - -static gpointer parent_class; -static guint signals[LAST_SIGNAL]; - -/* Utility functions */ - -/* This is faster and safer than glib2's utf8 abomination, but isn't exported from camel as yet */ -static inline guint32 -camel_utf8_getc(const guchar **ptr) -{ - register guchar *p = (guchar *)*ptr; - register guchar c, r; - register guint32 v, m; - -again: - r = *p++; -loop: - if (r < 0x80) { - *ptr = p; - v = r; - } else if (r < 0xfe) { /* valid start char? */ - v = r; - m = 0x7f80; /* used to mask out the length bits */ - do { - c = *p++; - if ((c & 0xc0) != 0x80) { - r = c; - goto loop; - } - v = (v<<6) | (c & 0x3f); - r<<=1; - m<<=5; - } while (r & 0x40); - - *ptr = p; - - v &= ~m; - } else { - goto again; - } - - return v; -} - -/* note: our tags of interest are 7 bit ascii, only, no need to do any fancy utf8 stuff */ -/* tags should be upper case - if this list gets longer than 10 entries, consider binary search */ -static const gchar *ignored_tags[] = { - "B", "I", "FONT", "TT", "EM", /* and more? */}; - -static gint -ignore_tag (const gchar *tag) -{ - gchar *t = g_alloca(strlen(tag)+1), c, *out; - const gchar *in; - gint i; - - /* we could use a aho-corasick matcher here too ... but we wont */ - - /* normalise tag into 't'. - Note we use the property that the only tags we're interested in - are 7 bit ascii to shortcut and simplify case insensitivity */ - in = tag+2; /* skip: TAG_ESCAPE '<' */ - if (*in == '/') - in++; - out = t; - while ((c = *in++)) { - if (c >= 'A' && c <= 'Z') - *out++ = c; - else if (c >= 'a' && c <= 'z') - *out++ = c & 0xdf; /* convert ASCII to upper case */ - else - /* maybe should check for > or ' ' etc? */ - break; - } - *out = 0; - - for (i = 0; i < G_N_ELEMENTS (ignored_tags); i++) { - if (strcmp (t, ignored_tags[i]) == 0) - return 1; - } - - return 0; -} - -/* ********************************************************************** */ - -/* Aho-Corasick search tree implmeentation */ - -/* next state if we match a character */ -struct _match { - struct _match *next; - guint32 ch; - struct _state *match; -}; - -/* tree state node */ -struct _state { - struct _match *matches; - guint final; /* max no of chars we just matched */ - struct _state *fail; /* where to try next if we fail */ - struct _state *next; /* next on this level? */ -}; - -/* base tree structure */ -struct _trie { - struct _state root; - gint max_depth; - - EMemChunk *state_chunks; - EMemChunk *match_chunks; -}; - -/* will be enabled only if debug is enabled */ -#if d(1) -1 != -1 -static void -dump_trie (struct _state *s, gint d) -{ - gchar *p = g_alloca(d*2+1); - struct _match *m; - - memset(p, ' ', d*2); - p[d*2]=0; - - printf("%s[state] %p: %d fail->%p\n", p, s, s->final, s->fail); - m = s->matches; - while (m) { - printf(" %s'%c' -> %p\n", p, m->ch, m->match); - if (m->match) - dump_trie (m->match, d+1); - m = m->next; - } -} -#endif - -/* This builds an Aho-Corasick search trie for a set of utf8 words */ -/* See - http://www-sr.informatik.uni-tuebingen.de/~buehler/AC/AC.html - for a neat demo */ - -static inline struct _match * -g (struct _state *q, guint32 c) -{ - struct _match *m = q->matches; - - while (m && m->ch != c) - m = m->next; - - return m; -} - -static struct _trie * -build_trie (gint nocase, gint len, guchar **words) -{ - struct _state *q, *qt, *r; - const guchar *word; - struct _match *m, *n = NULL; - gint i, depth; - guint32 c; - struct _trie *trie; - gint state_depth_max, state_depth_size; - struct _state **state_depth; - - trie = g_malloc(sizeof(*trie)); - trie->root.matches = NULL; - trie->root.final = 0; - trie->root.fail = NULL; - trie->root.next = NULL; - - trie->state_chunks = e_memchunk_new (8, sizeof(struct _state)); - trie->match_chunks = e_memchunk_new (8, sizeof(struct _match)); - - /* This will correspond to the length of the longest pattern */ - state_depth_size = 0; - state_depth_max = 64; - state_depth = g_malloc(sizeof(*state_depth[0])*64); - state_depth[0] = NULL; - - /* Step 1: Build trie */ - - /* This just builds a tree that merges all common prefixes into the same branch */ - - for (i=0;i<len;i++) { - word = words[i]; - q = &trie->root; - depth = 0; - while ((c = camel_utf8_getc (&word))) { - if (nocase) - c = g_unichar_tolower (c); - m = g (q, c); - if (m == NULL) { - m = e_memchunk_alloc(trie->match_chunks); - m->ch = c; - m->next = q->matches; - q->matches = m; - q = m->match = e_memchunk_alloc(trie->state_chunks); - q->matches = NULL; - q->fail = &trie->root; - q->final = 0; - if (state_depth_max < depth) { - state_depth_max += 64; - state_depth = g_realloc(state_depth, sizeof(*state_depth[0])*state_depth_max); - } - if (state_depth_size < depth) { - state_depth[depth] = NULL; - state_depth_size = depth; - } - q->next = state_depth[depth]; - state_depth[depth] = q; - } else { - q = m->match; - } - depth++; - } - q->final = depth; - } - - d(printf("Dumping trie:\n")); - d(dump_trie (&trie->root, 0)); - - /* Step 2: Build failure graph */ - - /* This searches for the longest substring which is a prefix of another string and - builds a graph of failure links so you can find multiple substrings concurrently, - using aho-corasick's algorithm */ - - for (i=0;i<state_depth_size;i++) { - q = state_depth[i]; - while (q) { - m = q->matches; - while (m) { - c = m->ch; - qt = m->match; - r = q->fail; - while (r && (n = g (r, c)) == NULL) - r = r->fail; - if (r != NULL) { - qt->fail = n->match; - if (qt->fail->final > qt->final) - qt->final = qt->fail->final; - } else { - if ((n = g (&trie->root, c))) - qt->fail = n->match; - else - qt->fail = &trie->root; - } - m = m->next; - } - q = q->next; - } - } - - d (printf("After failure analysis\n")); - d (dump_trie (&trie->root, 0)); - - g_free (state_depth); - - trie->max_depth = state_depth_size; - - return trie; -} - -static void -free_trie (struct _trie *t) -{ - e_memchunk_destroy(t->match_chunks); - e_memchunk_destroy(t->state_chunks); - - g_free (t); -} - -/* ********************************************************************** */ - -/* html token searcher */ - -struct _token { - struct _token *next; - struct _token *prev; - guint offset; - /* we need to copy the token for memory management, so why not copy it whole */ - gchar tok[1]; -}; - -/* stack of submatches currently being scanned, used for merging */ -struct _submatch { - guint offstart, offend; /* in bytes */ -}; - -/* flags for new func */ -#define SEARCH_CASE (1) -#define SEARCH_BOLD (2) - -struct _searcher { - struct _trie *t; - - gchar *(*next_token)(); /* callbacks for more tokens */ - gpointer next_data; - - gint words; /* how many words */ - gchar *tags, *tage; /* the tag we used to highlight */ - - gint flags; /* case sensitive or not */ - - struct _state *state; /* state is the current trie state */ - - gint matchcount; - - GQueue input; /* pending 'input' tokens, processed but might match */ - GQueue output; /* output tokens ready for source */ - - struct _token *current; /* for token output memory management */ - - guint32 offset; /* current offset through searchable stream? */ - guint32 offout; /* last output position */ - - guint lastp; /* current position in rotating last buffer */ - guint32 *last; /* buffer that goes back last 'n' positions */ - guint32 last_mask; /* bitmask for efficient rotation calculation */ - - guint submatchp; /* submatch stack */ - struct _submatch *submatches; -}; - -static void -searcher_set_tokenfunc(struct _searcher *s, gchar *(*next)(), gpointer data) -{ - s->next_token = next; - s->next_data = data; -} - -static struct _searcher * -searcher_new (gint flags, gint argc, guchar **argv, const gchar *tags, const gchar *tage) -{ - gint i, m; - struct _searcher *s; - - s = g_malloc(sizeof(*s)); - - s->t = build_trie ((flags&SEARCH_CASE) == 0, argc, argv); - s->words = argc; - s->tags = g_strdup (tags); - s->tage = g_strdup (tage); - s->flags = flags; - s->state = &s->t->root; - s->matchcount = 0; - - g_queue_init (&s->input); - g_queue_init (&s->output); - s->current = NULL; - - s->offset = 0; - s->offout = 0; - - /* rotating queue of previous character positions */ - m = s->t->max_depth+1; - i = 2; - while (i<m) - i<<=2; - s->last = g_malloc(sizeof(s->last[0])*i); - s->last_mask = i-1; - s->lastp = 0; - - /* a stack of possible submatches */ - s->submatchp = 0; - s->submatches = g_malloc(sizeof(s->submatches[0])*argc+1); - - return s; -} - -static void -searcher_free (struct _searcher *s) -{ - struct _token *t; - - while ((t = g_queue_pop_head (&s->input)) != NULL) - g_free (t); - while ((t = g_queue_pop_head (&s->output)) != NULL) - g_free (t); - g_free (s->tags); - g_free (s->tage); - g_free (s->last); - g_free (s->submatches); - free_trie (s->t); - g_free (s); -} - -static struct _token * -append_token (GQueue *queue, const gchar *tok, gint len) -{ - struct _token *token; - - if (len == -1) - len = strlen(tok); - token = g_malloc(sizeof(*token) + len+1); - token->offset = 0; /* set by caller when required */ - memcpy(token->tok, tok, len); - token->tok[len] = 0; - g_queue_push_tail (queue, token); - - return token; -} - -#define free_token(x) (g_free (x)) - -static void -output_token(struct _searcher *s, struct _token *token) -{ - gint offend; - gint left, pre; - - if (token->tok[0] == TAG_ESCAPE) { - if (token->offset >= s->offout) { - d (printf("moving tag token '%s' from input to output\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - g_queue_push_tail (&s->output, token); - } else { - d (printf("discarding tag token '%s' from input\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - free_token(token); - } - } else { - offend = token->offset + strlen(token->tok); - left = offend-s->offout; - if (left > 0) { - pre = s->offout - token->offset; - if (pre>0) - memmove (token->tok, token->tok+pre, left+1); - d (printf("adding partial remaining/failed '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - s->offout = offend; - g_queue_push_tail (&s->output, token); - } else { - d (printf("discarding whole token '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - free_token(token); - } - } -} - -static struct _token * -find_token(struct _searcher *s, gint start) -{ - GList *link; - - /* find token which is start token, from end of list back */ - link = g_queue_peek_tail_link (&s->input); - while (link != NULL) { - struct _token *token = link->data; - - if (token->offset <= start) - return token; - - link = g_list_previous (link); - } - - return NULL; -} - -static void -output_match(struct _searcher *s, guint start, guint end) -{ - register struct _token *token; - struct _token *starttoken, *endtoken; - gchar b[8]; - - d (printf("output match: %d-%d at %d\n", start, end, s->offout)); - - starttoken = find_token(s, start); - endtoken = find_token(s, end); - - if (starttoken == NULL || endtoken == NULL) { - d (printf("Cannot find match history for match %d-%d\n", start, end)); - return; - } - - d (printf("start in token '%s'\n", starttoken->tok[0]==TAG_ESCAPE?starttoken->tok+1:starttoken->tok)); - d (printf("end in token '%s'\n", endtoken->tok[0]==TAG_ESCAPE?endtoken->tok+1:endtoken->tok)); - - /* output pending stuff that didn't match afterall */ - while ((struct _token *) g_queue_peek_head (&s->input) != starttoken) { - token = g_queue_pop_head (&s->input); - d (printf("appending failed match '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - output_token(s, token); - } - - /* output any pre-match text */ - if (s->offout < start) { - token = append_token(&s->output, starttoken->tok + (s->offout-starttoken->offset), start-s->offout); - d (printf("adding pre-match text '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - s->offout = start; - } - - /* output highlight/bold */ - if (s->flags & SEARCH_BOLD) { - sprintf(b, "%c<b>", (gchar)TAG_ESCAPE); - append_token(&s->output, b, -1); - } - if (s->tags) - append_token(&s->output, s->tags, -1); - - /* output match node (s) */ - if (starttoken != endtoken) { - while ((struct _token *) g_queue_peek_head (&s->input) != endtoken) { - token = g_queue_pop_head (&s->input); - d (printf("appending (partial) match node (head) '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - output_token(s, token); - } - } - - /* any remaining partial content */ - if (s->offout < end) { - token = append_token(&s->output, endtoken->tok+(s->offout-endtoken->offset), end-s->offout); - d (printf("appending (partial) match node (tail) '%s'\n", token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - s->offout = end; - } - - /* end highlight */ - if (s->tage) - append_token(&s->output, s->tage, -1); - - /* and close bold if we need to */ - if (s->flags & SEARCH_BOLD) { - sprintf(b, "%c</b>", (gchar)TAG_ESCAPE); - append_token(&s->output, b, -1); - } -} - -/* output any sub-pending blocks */ -static void -output_subpending (struct _searcher *s) -{ - gint i; - - for (i=s->submatchp-1;i>=0;i--) - output_match(s, s->submatches[i].offstart, s->submatches[i].offend); - s->submatchp = 0; -} - -/* returns true if a merge took place */ -static gint -merge_subpending (struct _searcher *s, gint offstart, gint offend) -{ - gint i; - - /* merges overlapping or abutting match strings */ - if (s->submatchp && - s->submatches[s->submatchp-1].offend >= offstart) { - - /* go from end, any that match 'invalidate' follow-on ones too */ - for (i=s->submatchp-1;i>=0;i--) { - if (s->submatches[i].offend >= offstart) { - if (offstart < s->submatches[i].offstart) - s->submatches[i].offstart = offstart; - s->submatches[i].offend = offend; - if (s->submatchp > i) - s->submatchp = i+1; - } - } - return 1; - } - - return 0; -} - -static void -push_subpending (struct _searcher *s, gint offstart, gint offend) -{ - /* This is really an assertion, we just ignore the last pending match instead of crashing though */ - if (s->submatchp >= s->words) { - d (printf("ERROR: submatch pending stack overflow\n")); - s->submatchp = s->words-1; - } - - s->submatches[s->submatchp].offstart = offstart; - s->submatches[s->submatchp].offend = offend; - s->submatchp++; -} - -/* move any (partial) tokens from input to output if they are beyond the current output position */ -static void -output_pending (struct _searcher *s) -{ - struct _token *token; - - while ((token = g_queue_pop_head (&s->input)) != NULL) - output_token(s, token); -} - -/* flushes any nodes we cannot possibly match anymore */ -static void -flush_extra(struct _searcher *s) -{ - guint start; - gint i; - struct _token *starttoken, *token; - - /* find earliest gchar that can be in contention */ - start = s->offset - s->t->max_depth; - for (i=0;i<s->submatchp;i++) - if (s->submatches[i].offstart < start) - start = s->submatches[i].offstart; - - /* now, flush out any tokens which are before this point */ - starttoken = find_token(s, start); - if (starttoken == NULL) - return; - - while ((struct _token *) g_queue_peek_head (&s->input) != starttoken) { - token = g_queue_pop_head (&s->input); - output_token(s, token); - } -} - -static gchar * -searcher_next_token(struct _searcher *s) -{ - struct _token *token; - const guchar *tok, *stok, *pre_tok; - struct _trie *t = s->t; - struct _state *q = s->state; - struct _match *m = NULL; - gint offstart, offend; - guint32 c; - - while (g_queue_is_empty (&s->output)) { - /* get next token */ - tok = (guchar *)s->next_token(s->next_data); - if (tok == NULL) { - output_subpending (s); - output_pending (s); - break; - } - - /* we dont always have to copy each token, e.g. if we dont match anything */ - token = append_token(&s->input, (gchar *)tok, -1); - token->offset = s->offset; - tok = (guchar *)token->tok; - - d (printf("new token %d '%s'\n", token->offset, token->tok[0]==TAG_ESCAPE?token->tok+1:token->tok)); - - /* tag test, reset state on unknown tags */ - if (tok[0] == TAG_ESCAPE) { - if (!ignore_tag ((gchar *)tok)) { - /* force reset */ - output_subpending (s); - output_pending (s); - q = &t->root; - } - - continue; - } - - /* process whole token */ - pre_tok = stok = tok; - while ((c = camel_utf8_getc (&tok))) { - if ((s->flags & SEARCH_CASE) == 0) - c = g_unichar_tolower (c); - while (q && (m = g (q, c)) == NULL) - q = q->fail; - if (q == NULL) { - /* mismatch ... reset state */ - output_subpending (s); - q = &t->root; - } else if (m != NULL) { - /* keep track of previous offsets of utf8 chars, rotating buffer */ - s->last[s->lastp] = s->offset + (pre_tok-stok); - s->lastp = (s->lastp+1)&s->last_mask; - - q = m->match; - /* we have a match of q->final characters for a matching word */ - if (q->final) { - s->matchcount++; - - /* use the last buffer to find the real offset of this gchar */ - offstart = s->last[(s->lastp - q->final)&s->last_mask]; - offend = s->offset + (tok - stok); - - if (q->matches == NULL) { - if (s->submatchp == 0) { - /* nothing pending, always put something in so we can try merge */ - push_subpending (s, offstart, offend); - } else if (!merge_subpending (s, offstart, offend)) { - /* can't merge, output what we have, and start againt */ - output_subpending (s); - push_subpending (s, offstart, offend); - /*output_match(s, offstart, offend);*/ - } else if (g_queue_get_length (&s->input) > 8) { - /* we're continuing to match and merge, but we have a lot of stuff - waiting, so flush it out now since this is a safe point to do it */ - output_subpending (s); - } - } else { - /* merge/add subpending */ - if (!merge_subpending (s, offstart, offend)) - push_subpending (s, offstart, offend); - } - } - } - pre_tok = tok; - } - - s->offset += (pre_tok-stok); - - flush_extra(s); - } - - s->state = q; - - if (s->current) - free_token(s->current); - - s->current = token = g_queue_pop_head (&s->output); - - return token ? g_strdup (token->tok) : NULL; -} - -static gchar * -searcher_peek_token(struct _searcher *s) -{ - gchar *tok; - - /* we just get it and then put it back, it's fast enuf */ - tok = searcher_next_token(s); - if (tok) { - /* need to clear this so we dont free it while its still active */ - g_queue_push_head (&s->output, s->current); - s->current = NULL; - } - - return tok; -} - -static gint -searcher_pending (struct _searcher *s) -{ - return !(g_queue_is_empty (&s->input) && g_queue_is_empty (&s->output)); -} - -/* ********************************************************************** */ - -struct _search_info { - GPtrArray *strv; - gchar *color; - guint size:8; - guint flags:8; -}; - -/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ - -static struct _search_info * -search_info_new (void) -{ - struct _search_info *s; - - s = g_malloc0(sizeof(struct _search_info)); - s->strv = g_ptr_array_new (); - - return s; -} - -static void -search_info_set_flags(struct _search_info *si, guint flags, guint mask) -{ - si->flags = (si->flags & ~mask) | (flags & mask); -} - -static void -search_info_set_color (struct _search_info *si, const gchar *color) -{ - g_free (si->color); - si->color = g_strdup (color); -} - -static void -search_info_add_string (struct _search_info *si, const gchar *s) -{ - const guchar *start; - guint32 c; - - if (s && s[0]) { - const guchar *us = (guchar *) s; - /* strip leading whitespace */ - start = us; - while ((c = camel_utf8_getc (&us))) { - if (!g_unichar_isspace (c)) { - break; - } - start = us; - } - /* should probably also strip trailing, but i'm lazy today */ - if (start[0]) - g_ptr_array_add (si->strv, g_strdup ((gchar *)start)); - } -} - -static void -search_info_clear (struct _search_info *si) -{ - gint i; - - for (i=0;i<si->strv->len;i++) - g_free (si->strv->pdata[i]); - - g_ptr_array_set_size (si->strv, 0); -} - -static void -search_info_free (struct _search_info *si) -{ - gint i; - - for (i=0;i<si->strv->len;i++) - g_free (si->strv->pdata[i]); - - g_ptr_array_free (si->strv, TRUE); - g_free (si->color); - g_free (si); -} - -static struct _search_info * -search_info_clone (struct _search_info *si) -{ - struct _search_info *out; - gint i; - - out = search_info_new (); - for (i=0;i<si->strv->len;i++) - g_ptr_array_add (out->strv, g_strdup (si->strv->pdata[i])); - out->color = g_strdup (si->color); - out->flags = si->flags; - out->size = si->size; - - return out; -} - -static struct _searcher * -search_info_to_searcher (struct _search_info *si) -{ - gchar *tags, *tage; - const gchar *col; - - if (si->strv->len == 0) - return NULL; - - if (si->color == NULL) - col = "red"; - else - col = si->color; - - tags = g_alloca(20+strlen(col)); - sprintf(tags, "%c<font color=\"%s\">", TAG_ESCAPE, col); - tage = g_alloca(20); - sprintf(tage, "%c</font>", TAG_ESCAPE); - - return searcher_new (si->flags, si->strv->len, (guchar **)si->strv->pdata, tags, tage); -} - -/* ********************************************************************** */ - -struct _ESearchingTokenizerPrivate { - struct _search_info *primary, *secondary; - struct _searcher *engine; -}; - -/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ - -/** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** **/ - -/* blah blah the htmltokeniser doesn't like being asked - for a token if it doens't hvae any! */ -static gchar * -get_token (HTMLTokenizer *tokenizer) -{ - HTMLTokenizerClass *class = HTML_TOKENIZER_CLASS (parent_class); - - if (class->has_more (tokenizer)) - return class->next_token (tokenizer); - - return NULL; -} - -/* proxy matched event, not sure what its for otherwise */ -static void -matched (ESearchingTokenizer *tokenizer) -{ - /*++tokenizer->priv->match_count;*/ - g_signal_emit (tokenizer, signals[MATCH_SIGNAL], 0); -} - -/* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ - -static void -searching_tokenizer_finalize (GObject *object) -{ - ESearchingTokenizerPrivate *priv; - - priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (object); - - search_info_free (priv->primary); - search_info_free (priv->secondary); - - if (priv->engine != NULL) - searcher_free (priv->engine); - - /* Chain up to parent's finalize () method. */ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -searching_tokenizer_begin (HTMLTokenizer *tokenizer, - const gchar *content_type) -{ - ESearchingTokenizerPrivate *priv; - - priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer); - - /* reset search */ - if (priv->engine != NULL) { - searcher_free (priv->engine); - priv->engine = NULL; - } - - if ((priv->engine = search_info_to_searcher (priv->primary)) - || (priv->engine = search_info_to_searcher (priv->secondary))) { - searcher_set_tokenfunc(priv->engine, get_token, tokenizer); - } - /* else - no engine, no search active */ - - /* Chain up to parent's begin() method. */ - HTML_TOKENIZER_CLASS (parent_class)->begin (tokenizer, content_type); -} - -static gchar * -searching_tokenizer_peek_token (HTMLTokenizer *tokenizer) -{ - ESearchingTokenizerPrivate *priv; - - priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer); - - if (priv->engine != NULL) - return searcher_peek_token (priv->engine); - - /* Chain up to parent's peek_token() method. */ - return HTML_TOKENIZER_CLASS (parent_class)->peek_token (tokenizer); -} - -static gchar * -searching_tokenizer_next_token (HTMLTokenizer *tokenizer) -{ - ESearchingTokenizerPrivate *priv; - gint oldmatched; - gchar *token; - - priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer); - - /* If no search is active, just use the default method. */ - if (priv->engine == NULL) - return HTML_TOKENIZER_CLASS (parent_class)->next_token (tokenizer); - - oldmatched = priv->engine->matchcount; - - token = searcher_next_token (priv->engine); - - /* not sure if this has to be accurate or just say we had some matches */ - if (oldmatched != priv->engine->matchcount) - g_signal_emit (tokenizer, signals[MATCH_SIGNAL], 0); - - return token; -} - -static gboolean -searching_tokenizer_has_more (HTMLTokenizer *tokenizer) -{ - ESearchingTokenizerPrivate *priv; - - priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer); - - return (priv->engine != NULL && searcher_pending (priv->engine)) || - HTML_TOKENIZER_CLASS (parent_class)->has_more (tokenizer); -} - -static HTMLTokenizer * -searching_tokenizer_clone (HTMLTokenizer *tokenizer) -{ - ESearchingTokenizer *orig_st; - ESearchingTokenizer *new_st; - - orig_st = E_SEARCHING_TOKENIZER (tokenizer); - new_st = e_searching_tokenizer_new (); - - search_info_free (new_st->priv->primary); - search_info_free (new_st->priv->secondary); - - new_st->priv->primary = search_info_clone (orig_st->priv->primary); - new_st->priv->secondary = search_info_clone (orig_st->priv->secondary); - - g_signal_connect_swapped ( - new_st, "match", G_CALLBACK (matched), orig_st); - - return HTML_TOKENIZER (new_st); -} -static void -searching_tokenizer_class_init (ESearchingTokenizerClass *class) -{ - GObjectClass *object_class; - HTMLTokenizerClass *tokenizer_class; - - parent_class = g_type_class_peek_parent (class); - g_type_class_add_private (class, sizeof (ESearchingTokenizerPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = searching_tokenizer_finalize; - - tokenizer_class = HTML_TOKENIZER_CLASS (class); - tokenizer_class->begin = searching_tokenizer_begin; - tokenizer_class->peek_token = searching_tokenizer_peek_token; - tokenizer_class->next_token = searching_tokenizer_next_token; - tokenizer_class->has_more = searching_tokenizer_has_more; - tokenizer_class->clone = searching_tokenizer_clone; - - signals[MATCH_SIGNAL] = g_signal_new ( - "match", - G_TYPE_FROM_CLASS (class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ESearchingTokenizerClass, match), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); -} - -static void -searching_tokenizer_init (ESearchingTokenizer *tokenizer) -{ - tokenizer->priv = E_SEARCHING_TOKENIZER_GET_PRIVATE (tokenizer); - - tokenizer->priv->primary = search_info_new (); - search_info_set_flags ( - tokenizer->priv->primary, - SEARCH_BOLD, SEARCH_CASE | SEARCH_BOLD); - search_info_set_color (tokenizer->priv->primary, "red"); - - tokenizer->priv->secondary = search_info_new (); - search_info_set_flags( - tokenizer->priv->secondary, - SEARCH_BOLD, SEARCH_CASE | SEARCH_BOLD); - search_info_set_color (tokenizer->priv->secondary, "purple"); -} - -GType -e_searching_tokenizer_get_type (void) -{ - static GType type = 0; - - if (G_UNLIKELY (type == 0)) { - static const GTypeInfo type_info = { - sizeof (ESearchingTokenizerClass), - (GBaseInitFunc) NULL, - (GBaseFinalizeFunc) NULL, - (GClassInitFunc) searching_tokenizer_class_init, - (GClassFinalizeFunc) NULL, - NULL, /* class_data */ - sizeof (ESearchingTokenizer), - 0, /* n_preallocs */ - (GInstanceInitFunc) searching_tokenizer_init, - NULL /* value_table */ - }; - - type = g_type_register_static ( - HTML_TYPE_TOKENIZER, "ESearchingTokenizer", - &type_info, 0); - } - - return type; -} - -ESearchingTokenizer * -e_searching_tokenizer_new (void) -{ - return g_object_new (E_TYPE_SEARCHING_TOKENIZER, NULL); -} - -void -e_searching_tokenizer_set_primary_search_string (ESearchingTokenizer *tokenizer, - const gchar *primary_string) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_clear (tokenizer->priv->primary); - search_info_add_string (tokenizer->priv->primary, primary_string); -} - -void -e_searching_tokenizer_add_primary_search_string (ESearchingTokenizer *tokenizer, - const gchar *primary_string) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_add_string (tokenizer->priv->primary, primary_string); -} - -void -e_searching_tokenizer_set_primary_case_sensitivity (ESearchingTokenizer *tokenizer, - gboolean case_sensitive) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_set_flags ( - tokenizer->priv->primary, - case_sensitive ? SEARCH_CASE : 0, SEARCH_CASE); -} - -void -e_searching_tokenizer_set_secondary_search_string (ESearchingTokenizer *tokenizer, - const gchar *secondary_string) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_clear (tokenizer->priv->secondary); - search_info_add_string (tokenizer->priv->secondary, secondary_string); -} - -void -e_searching_tokenizer_add_secondary_search_string (ESearchingTokenizer *tokenizer, - const gchar *secondary_string) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_add_string (tokenizer->priv->secondary, secondary_string); -} - -void -e_searching_tokenizer_set_secondary_case_sensitivity (ESearchingTokenizer *tokenizer, - gboolean case_sensitive) -{ - g_return_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer)); - - search_info_set_flags ( - tokenizer->priv->secondary, - case_sensitive ? SEARCH_CASE : 0, SEARCH_CASE); -} - -/* Note: only returns the primary search string count */ -gint -e_searching_tokenizer_match_count (ESearchingTokenizer *tokenizer) -{ - g_return_val_if_fail (E_IS_SEARCHING_TOKENIZER (tokenizer), -1); - - if (tokenizer->priv->engine && tokenizer->priv->primary->strv->len) - return tokenizer->priv->engine->matchcount; - - return 0; -} diff --git a/mail/e-searching-tokenizer.h b/mail/e-searching-tokenizer.h deleted file mode 100644 index 7eb603a25d..0000000000 --- a/mail/e-searching-tokenizer.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * - * 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/> - * - * - * Authors: - * Jon Trowbridge <trow@ximian.com> - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - */ - -#ifndef E_SEARCHING_TOKENIZER_H -#define E_SEARCHING_TOKENIZER_H - -#include <glib.h> -#include <gtkhtml/htmltokenizer.h> - -/* Standard GObject macros */ -#define E_TYPE_SEARCHING_TOKENIZER \ - (e_searching_tokenizer_get_type ()) -#define E_SEARCHING_TOKENIZER(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizer)) -#define E_SEARCHING_TOKENIZER_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerClass)) -#define E_IS_SEARCHING_TOKENIZER(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_SEARCHING_TOKENIZER)) -#define E_IS_SEARCHING_TOKENIZER_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_SEARCHING_TOKENIZER)) -#define E_SEARCH_TOKENIZER_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_SEARCHING_TOKENIZER, ESearchingTokenizerClass)) - -G_BEGIN_DECLS - -typedef struct _ESearchingTokenizer ESearchingTokenizer; -typedef struct _ESearchingTokenizerClass ESearchingTokenizerClass; -typedef struct _ESearchingTokenizerPrivate ESearchingTokenizerPrivate; - -struct _ESearchingTokenizer { - HTMLTokenizer parent; - ESearchingTokenizerPrivate *priv; -}; - -struct _ESearchingTokenizerClass { - HTMLTokenizerClass parent_class; - - void (*match) (ESearchingTokenizer *tokenizer); -}; - -GType e_searching_tokenizer_get_type (void); -ESearchingTokenizer * - e_searching_tokenizer_new (void); -void e_searching_tokenizer_set_primary_search_string - (ESearchingTokenizer *tokenizer, - const gchar *primary_string); -void e_searching_tokenizer_add_primary_search_string - (ESearchingTokenizer *tokenizer, - const gchar *primary_string); -void e_searching_tokenizer_set_primary_case_sensitivity - (ESearchingTokenizer *tokenizer, - gboolean case_sensitive); -void e_searching_tokenizer_set_secondary_search_string - (ESearchingTokenizer *tokenizer, - const gchar *secondary_string); -void e_searching_tokenizer_add_secondary_search_string - (ESearchingTokenizer *tokenizer, - const gchar *secondary_string); -void e_searching_tokenizer_set_secondary_case_sensitivity - (ESearchingTokenizer *tokenizer, - gboolean case_sensitive); -gint e_searching_tokenizer_match_count - (ESearchingTokenizer *tokenizer); - -G_END_DECLS - -#endif /* E_SEARCHING_TOKENIZER_H */ |