diff options
author | Marco Pesenti Gritti <marco@src.gnome.org> | 2003-10-12 01:41:26 +0800 |
---|---|---|
committer | Marco Pesenti Gritti <marco@src.gnome.org> | 2003-10-12 01:41:26 +0800 |
commit | 890b0f0278c284b3f72b17b2a320d7f443683d77 (patch) | |
tree | 838de9eb530807959804418a6d14f44eb8036e82 /lib/widgets | |
parent | 0b5f13ef5a9f6a28812cfa959ad9f5b786312ad2 (diff) | |
download | gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar.gz gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar.bz2 gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar.lz gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar.xz gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.tar.zst gsoc2013-epiphany-890b0f0278c284b3f72b17b2a320d7f443683d77.zip |
Merge completion branch
Diffstat (limited to 'lib/widgets')
-rw-r--r-- | lib/widgets/Makefile.am | 2 | ||||
-rw-r--r-- | lib/widgets/ephy-autocompletion-window.c | 839 | ||||
-rw-r--r-- | lib/widgets/ephy-autocompletion-window.h | 89 | ||||
-rw-r--r-- | lib/widgets/ephy-location-entry.c | 876 |
4 files changed, 335 insertions, 1471 deletions
diff --git a/lib/widgets/Makefile.am b/lib/widgets/Makefile.am index 7331079a5..6a8835728 100644 --- a/lib/widgets/Makefile.am +++ b/lib/widgets/Makefile.am @@ -14,8 +14,6 @@ noinst_LTLIBRARIES = libephywidgets.la libephywidgets_la_SOURCES = \ ephy-arrow-toolbutton.c \ ephy-arrow-toolbutton.h \ - ephy-autocompletion-window.c \ - ephy-autocompletion-window.h \ ephy-ellipsizing-label.c \ ephy-ellipsizing-label.h \ ephy-location-entry.c \ diff --git a/lib/widgets/ephy-autocompletion-window.c b/lib/widgets/ephy-autocompletion-window.c deleted file mode 100644 index 39bd9d77e..000000000 --- a/lib/widgets/ephy-autocompletion-window.c +++ /dev/null @@ -1,839 +0,0 @@ -/* - * Copyright (C) 2002 Ricardo Fernández Pascual - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - * $Id$ - */ - -#include <gtk/gtkcellrenderertext.h> -#include <gtk/gtktreeview.h> -#include <gtk/gtkscrolledwindow.h> -#include <gtk/gtktreeselection.h> -#include <gtk/gtkliststore.h> -#include <gtk/gtkwindow.h> -#include <gtk/gtkmain.h> -#include <gtk/gtkvbox.h> -#include <gdk/gdkkeysyms.h> -#include <gtk/gtkframe.h> - -#include "ephy-autocompletion-window.h" -#include "ephy-string.h" -#include "ephy-marshal.h" -#include "ephy-gui.h" -#include "ephy-debug.h" - -/* This is copied from gtkscrollbarwindow.c */ -#define DEFAULT_SCROLLBAR_SPACING 3 - -#define SCROLLBAR_SPACING(w) \ - (GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing >= 0 ? \ - GTK_SCROLLED_WINDOW_GET_CLASS (w)->scrollbar_spacing : DEFAULT_SCROLLBAR_SPACING) - -#define MAX_VISIBLE_ROWS 9 -#define MAX_COMPLETION_ALTERNATIVES 7 - -/** - * Private data - */ - -#define EPHY_AUTOCOMPLETION_WINDOW_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_AUTOCOMPLETION_WINDOW, EphyAutocompletionWindowPrivate)) - -struct _EphyAutocompletionWindowPrivate { - EphyAutocompletion *autocompletion; - GtkWidget *parent; - - GtkWidget *window; - GtkScrolledWindow *scrolled_window; - GtkTreeView *tree_view; - GtkTreeViewColumn *col1; - GtkTreeView *action_tree_view; - GtkTreeViewColumn *action_col1; - int sel_index; - gboolean action; - - char *selected; - - GtkListStore *list_store; - GtkListStore *action_list_store; - guint last_added_match; - int view_nitems; - - gboolean shown; -}; - -/** - * Private functions, only availble from this file - */ -static void ephy_autocompletion_window_class_init (EphyAutocompletionWindowClass *klass); -static void ephy_autocompletion_window_init (EphyAutocompletionWindow *aw); -static void ephy_autocompletion_window_finalize_impl (GObject *o); -static void ephy_autocompletion_window_init_widgets (EphyAutocompletionWindow *aw); -static void ephy_autocompletion_window_selection_changed_cb (GtkTreeSelection *treeselection, - EphyAutocompletionWindow *aw); -static gboolean ephy_autocompletion_window_button_press_event_cb (GtkWidget *widget, - GdkEventButton *event, - EphyAutocompletionWindow *aw); -static gboolean ephy_autocompletion_window_key_press_cb (GtkWidget *widget, - GdkEventKey *event, - EphyAutocompletionWindow *aw); -static void ephy_autocompletion_window_event_after_cb (GtkWidget *wid, GdkEvent *event, - EphyAutocompletionWindow *aw); -static void ephy_autocompletion_window_fill_store_chunk (EphyAutocompletionWindow *aw); - - -static gpointer g_object_class; - -enum EphyAutocompletionWindowSignalsEnum { - ACTIVATED, - SELECTED, - EPHY_AUTOCOMPLETION_WINDOW_HIDDEN, - EPHY_AUTOCOMPLETION_WINDOW_LAST_SIGNAL -}; -static gint EphyAutocompletionWindowSignals[EPHY_AUTOCOMPLETION_WINDOW_LAST_SIGNAL]; - -GType -ephy_autocompletion_window_get_type (void) -{ - static GType ephy_autocompletion_window_type = 0; - - if (ephy_autocompletion_window_type == 0) - { - static const GTypeInfo our_info = - { - sizeof (EphyAutocompletionWindowClass), - NULL, - NULL, - (GClassInitFunc) ephy_autocompletion_window_class_init, - NULL, - NULL, - sizeof (EphyAutocompletionWindow), - 0, - (GInstanceInitFunc) ephy_autocompletion_window_init - }; - - ephy_autocompletion_window_type = g_type_register_static (G_TYPE_OBJECT, - "EphyAutocompletionWindow", - &our_info, 0); - } - - return ephy_autocompletion_window_type; -} - -static void -ephy_autocompletion_window_class_init (EphyAutocompletionWindowClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_object_class = g_type_class_peek_parent (klass); - - object_class->finalize = ephy_autocompletion_window_finalize_impl; - - EphyAutocompletionWindowSignals[ACTIVATED] = g_signal_new ( - "activated", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP, - G_STRUCT_OFFSET (EphyAutocompletionWindowClass, activated), - NULL, NULL, - ephy_marshal_VOID__STRING_INT, - G_TYPE_NONE, - 2, - G_TYPE_STRING, - G_TYPE_INT); - - EphyAutocompletionWindowSignals[SELECTED] = g_signal_new ( - "selected", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP, - G_STRUCT_OFFSET (EphyAutocompletionWindowClass, selected), - NULL, NULL, - ephy_marshal_VOID__STRING_INT, - G_TYPE_NONE, - 2, - G_TYPE_STRING, - G_TYPE_INT); - - EphyAutocompletionWindowSignals[EPHY_AUTOCOMPLETION_WINDOW_HIDDEN] = g_signal_new ( - "hidden", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP, - G_STRUCT_OFFSET (EphyAutocompletionWindowClass, hidden), - NULL, NULL, - ephy_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - g_type_class_add_private (object_class, sizeof (EphyAutocompletionWindowPrivate)); -} - -static void -ephy_autocompletion_window_init (EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = EPHY_AUTOCOMPLETION_WINDOW_GET_PRIVATE (aw); - GtkTreeSelection *s; - - aw->priv = p; - p->selected = NULL; - - ephy_autocompletion_window_init_widgets (aw); - - s = gtk_tree_view_get_selection (p->tree_view); - /* I would like to use GTK_SELECTION_SINGLE, but it seems to require that one - item is selected always */ - gtk_tree_selection_set_mode (s, GTK_SELECTION_MULTIPLE); - - g_signal_connect (s, "changed", G_CALLBACK (ephy_autocompletion_window_selection_changed_cb), aw); - - s = gtk_tree_view_get_selection (p->action_tree_view); - gtk_tree_selection_set_mode (s, GTK_SELECTION_MULTIPLE); - - g_signal_connect (s, "changed", G_CALLBACK (ephy_autocompletion_window_selection_changed_cb), aw); -} - -static void -ephy_autocompletion_window_finalize_impl (GObject *o) -{ - EphyAutocompletionWindow *aw = EPHY_AUTOCOMPLETION_WINDOW (o); - EphyAutocompletionWindowPrivate *p = aw->priv; - - if (p->list_store) g_object_unref (p->list_store); - if (p->action_list_store) g_object_unref (p->action_list_store); - if (p->parent) g_object_unref (p->parent); - if (p->window) gtk_widget_destroy (p->window); - - if (p->autocompletion) - { - g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, aw); - g_object_unref (p->autocompletion); - } - - g_free (p->selected); - - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_keyboard_ungrab (GDK_CURRENT_TIME); - - G_OBJECT_CLASS (g_object_class)->finalize (o); -} - -static gboolean -set_renderer_bg_color (GtkWidget *widget, - GtkStyle *previous_style, - GtkCellRenderer *renderer) -{ - GValue v = { 0 }; - GdkColor *bg_color; - GtkStyle *style; - - g_value_init (&v, GDK_TYPE_COLOR); - g_object_get_property (G_OBJECT (renderer), "cell_background_gdk", &v); - bg_color = g_value_peek_pointer (&v); - style = gtk_widget_get_style (widget); - *bg_color = style->bg[GTK_STATE_NORMAL]; - g_object_set_property (G_OBJECT (renderer), "cell_background_gdk", &v); - - return FALSE; -} - -static void -ephy_autocompletion_window_init_widgets (EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - GtkWidget *sw; - GtkCellRenderer *renderer; - GtkWidget *frame; - GtkWidget *vbox; - - p->window = gtk_window_new (GTK_WINDOW_POPUP); - gtk_window_set_resizable (GTK_WINDOW (p->window), FALSE); - - frame = gtk_frame_new (NULL); - gtk_frame_set_shadow_type (GTK_FRAME (frame), - GTK_SHADOW_OUT); - gtk_container_add (GTK_CONTAINER (p->window), frame); - gtk_widget_show (frame); - - vbox = gtk_vbox_new (FALSE, 0); - gtk_widget_show (vbox); - gtk_container_add (GTK_CONTAINER (frame), vbox); - - sw = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (vbox), - sw, TRUE, TRUE, 0); - gtk_scrolled_window_set_shadow_type - (GTK_SCROLLED_WINDOW(sw), GTK_SHADOW_IN); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), - GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); - p->scrolled_window = GTK_SCROLLED_WINDOW (sw); - gtk_widget_show (sw); - - p->tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); - gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (p->tree_view)); - gtk_widget_realize (GTK_WIDGET (p->tree_view)); - - renderer = gtk_cell_renderer_text_new (); - p->col1 = gtk_tree_view_column_new (); - gtk_tree_view_column_pack_start (p->col1, renderer, TRUE); - gtk_tree_view_column_set_attributes (p->col1, renderer, - "text", 0, - NULL); - gtk_tree_view_append_column (p->tree_view, p->col1); - - gtk_tree_view_set_headers_visible (p->tree_view, FALSE); - gtk_widget_show (GTK_WIDGET(p->tree_view)); - - p->action_tree_view = GTK_TREE_VIEW (gtk_tree_view_new ()); - gtk_box_pack_start (GTK_BOX (vbox), - GTK_WIDGET (p->action_tree_view), - FALSE, TRUE, 0); - - renderer = gtk_cell_renderer_text_new (); - - set_renderer_bg_color (p->window, NULL, renderer); - g_signal_connect (G_OBJECT (p->window), "style-set", - G_CALLBACK (set_renderer_bg_color), G_OBJECT (renderer)); - - p->action_col1 = gtk_tree_view_column_new (); - gtk_tree_view_column_pack_start (p->action_col1, renderer, TRUE); - gtk_tree_view_column_set_attributes (p->action_col1, renderer, - "text", 0, - NULL); - gtk_tree_view_append_column (p->action_tree_view, p->action_col1); - - gtk_tree_view_set_headers_visible (p->action_tree_view, FALSE); - gtk_widget_show (GTK_WIDGET(p->action_tree_view)); -} - -EphyAutocompletionWindow * -ephy_autocompletion_window_new (EphyAutocompletion *ac, GtkWidget *w) -{ - EphyAutocompletionWindow *ret = g_object_new (EPHY_TYPE_AUTOCOMPLETION_WINDOW, NULL); - ephy_autocompletion_window_set_parent_widget (ret, w); - ephy_autocompletion_window_set_autocompletion (ret, ac); - return ret; -} - -void -ephy_autocompletion_window_set_parent_widget (EphyAutocompletionWindow *aw, GtkWidget *w) -{ - if (aw->priv->parent) g_object_unref (aw->priv->parent); - aw->priv->parent = g_object_ref (w); -} - -void -ephy_autocompletion_window_set_autocompletion (EphyAutocompletionWindow *aw, - EphyAutocompletion *ac) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - - if (p->autocompletion) - { - g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, aw); - - g_object_unref (p->autocompletion); - - } - p->autocompletion = g_object_ref (ac); -} - -static void -ephy_autocompletion_window_selection_changed_cb (GtkTreeSelection *treeselection, - EphyAutocompletionWindow *aw) -{ - GList *l; - GtkTreeModel *model; - - if (aw->priv->selected) g_free (aw->priv->selected); - - l = gtk_tree_selection_get_selected_rows (treeselection, &model); - if (l) - { - GtkTreePath *path; - GtkTreeIter iter; - path = (GtkTreePath *)l->data; - - gtk_tree_model_get_iter (model, &iter, path); - gtk_tree_model_get (model, &iter, 1, - &aw->priv->selected, -1); - - g_list_foreach (l, (GFunc)gtk_tree_path_free, NULL); - g_list_free (l); - } - else - { - aw->priv->selected = NULL; - } -} - -static void -ephy_autocompletion_window_get_dimensions (EphyAutocompletionWindow *aw, - int *x, int *y, int *width, int *height) -{ - GtkBin *popwin; - GtkWidget *widget; - GtkScrolledWindow *popup; - gint real_height; - GtkRequisition list_requisition; - gboolean show_vscroll = FALSE; - gint avail_height; - gint min_height; - gint alloc_width; - gint work_height; - gint old_height; - gint old_width; - int row_height; - - widget = GTK_WIDGET (aw->priv->parent); - popup = GTK_SCROLLED_WINDOW (aw->priv->scrolled_window); - popwin = GTK_BIN (aw->priv->window); - - gdk_window_get_origin (widget->window, x, y); - real_height = MIN (widget->requisition.height, - widget->allocation.height); - *y += real_height; - avail_height = gdk_screen_height () - *y; - - gtk_widget_size_request (GTK_WIDGET(aw->priv->tree_view), - &list_requisition); - - alloc_width = (widget->allocation.width - - 2 * popwin->child->style->xthickness - - 2 * GTK_CONTAINER (popwin->child)->border_width - - 2 * GTK_CONTAINER (popup)->border_width - - 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width - - 2 * GTK_BIN (popup)->child->style->xthickness); - - work_height = (2 * popwin->child->style->ythickness + - 2 * GTK_CONTAINER (popwin->child)->border_width + - 2 * GTK_CONTAINER (popup)->border_width + - 2 * GTK_CONTAINER (GTK_BIN (popup)->child)->border_width + - 2 * GTK_BIN (popup)->child->style->ythickness); - - min_height = MIN (list_requisition.height, - popup->vscrollbar->requisition.height); - - row_height = list_requisition.height / MAX (aw->priv->view_nitems, 1); - LOG ("Real list requisition %d, Items %d", list_requisition.height, aw->priv->view_nitems) - list_requisition.height = MIN (row_height * MAX_VISIBLE_ROWS, list_requisition.height); - LOG ("Row Height %d, Fake list requisition %d", row_height, list_requisition.height) - - do - { - old_width = alloc_width; - old_height = work_height; - - if (!show_vscroll && - work_height + list_requisition.height > avail_height) - { - if (work_height + min_height > avail_height && - *y - real_height > avail_height) - { - *y -= (work_height + list_requisition.height + - real_height); - break; - } - alloc_width -= (popup->vscrollbar->requisition.width + - SCROLLBAR_SPACING (popup)); - show_vscroll = TRUE; - } - } while (old_width != alloc_width || old_height != work_height); - - *width = widget->allocation.width; - - if (*x < 0) *x = 0; - - *height = MIN (work_height + list_requisition.height, - avail_height); - - /* Action view */ - work_height = (2 * GTK_CONTAINER (popup)->border_width + - 2 * GTK_WIDGET (popup)->style->ythickness); - - if (!GTK_WIDGET_VISIBLE (aw->priv->scrolled_window)) - { - *height = work_height; - } - - gtk_widget_size_request (GTK_WIDGET(aw->priv->action_tree_view), - &list_requisition); - - if (GTK_WIDGET_VISIBLE (aw->priv->action_tree_view)) - { - *height += list_requisition.height; - } -} - -static void -ephy_autocompletion_window_fill_store_chunk (EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - const EphyAutocompletionMatch *matches; - guint i; - gboolean changed; - guint nmatches; - guint last; - guint completion_nitems = 0, action_nitems = 0, substring_nitems = 0; - - START_PROFILER ("Fill store") - - nmatches = ephy_autocompletion_get_num_matches (p->autocompletion); - matches = ephy_autocompletion_get_matches_sorted_by_score (p->autocompletion, - &changed); - if (!changed) return; - - if (p->list_store) g_object_unref (p->list_store); - p->list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); - - if (p->action_list_store) g_object_unref (p->action_list_store); - p->action_list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING); - - last = p->last_added_match = 0; - - for (i = 0; last < nmatches; i++, last++) - { - const EphyAutocompletionMatch *m = &matches[last]; - GtkTreeIter iter; - GtkListStore *store; - - if (m->is_action || m->substring || - completion_nitems <= MAX_COMPLETION_ALTERNATIVES) - { - if (m->is_action) - { - store = p->action_list_store; - action_nitems ++; - } - else if (m->substring) - { - store = p->list_store; - substring_nitems ++; - } - else - { - store = p->list_store; - completion_nitems ++; - } - - gtk_list_store_append (store, &iter); - gtk_list_store_set (store, &iter, - 0, m->title, - 1, m->target, - -1); - } - } - - p->view_nitems = substring_nitems + completion_nitems; - - gtk_widget_show (GTK_WIDGET (p->scrolled_window)); - gtk_widget_show (GTK_WIDGET (p->action_tree_view)); - if (p->view_nitems == 0) - { - gtk_widget_hide (GTK_WIDGET (p->scrolled_window)); - } - if (action_nitems == 0) - { - gtk_widget_hide (GTK_WIDGET (p->action_tree_view)); - } - - p->last_added_match = last; - - STOP_PROFILER ("Fill store") -} - -void -ephy_autocompletion_window_show (EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - gint x, y, height, width; - guint nmatches; - - g_return_if_fail (p->window); - g_return_if_fail (p->autocompletion); - - nmatches = ephy_autocompletion_get_num_matches (p->autocompletion); - if (nmatches <= 0) - { - ephy_autocompletion_window_hide (aw); - return; - } - - START_PROFILER ("Showing window") - - ephy_autocompletion_window_fill_store_chunk (aw); - - gtk_tree_view_set_model (p->tree_view, GTK_TREE_MODEL (p->list_store)); - gtk_tree_view_set_model (p->action_tree_view, GTK_TREE_MODEL (p->action_list_store)); - - ephy_autocompletion_window_get_dimensions (aw, &x, &y, &width, &height); - - gtk_widget_set_size_request (GTK_WIDGET (p->window), width, - height); - gtk_window_move (GTK_WINDOW (p->window), x, y); - - if (!p->shown) - { - gtk_widget_show (p->window); - - gdk_pointer_grab (p->parent->window, TRUE, - GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK, - NULL, NULL, GDK_CURRENT_TIME); - gdk_keyboard_grab (p->parent->window, TRUE, GDK_CURRENT_TIME); - gtk_grab_add (p->window); - - g_signal_connect (p->window, "button-press-event", - G_CALLBACK (ephy_autocompletion_window_button_press_event_cb), - aw); - g_signal_connect (p->window, "key-press-event", - G_CALLBACK (ephy_autocompletion_window_key_press_cb), - aw); - g_signal_connect (p->tree_view, "event-after", - G_CALLBACK (ephy_autocompletion_window_event_after_cb), - aw); - g_signal_connect (p->action_tree_view, "event-after", - G_CALLBACK (ephy_autocompletion_window_event_after_cb), - aw); - - p->shown = TRUE; - } - - gtk_tree_view_scroll_to_point (GTK_TREE_VIEW (p->tree_view), 0, 0); - - gtk_widget_grab_focus (GTK_WIDGET (p->tree_view)); - - STOP_PROFILER ("Showing window") -} - -static gboolean -ephy_autocompletion_window_button_press_event_cb (GtkWidget *widget, - GdkEventButton *event, - EphyAutocompletionWindow *aw) -{ - GtkWidget *event_widget; - - event_widget = gtk_get_event_widget ((GdkEvent *) event); - - /* Check to see if button press happened inside the alternatives - window. If not, destroy the window. */ - if (event_widget != aw->priv->window) - { - while (event_widget) - { - if (event_widget == aw->priv->window) - return FALSE; - event_widget = event_widget->parent; - } - } - ephy_autocompletion_window_hide (aw); - - return TRUE; -} - -static void -move_selection (EphyAutocompletionWindow *aw, int dir) -{ - int new_index; - int n_compl, n_actions, n_items; - GtkTreeModel *compl_model, *actions_model; - GtkTreeSelection *compl_sel, *actions_sel; - - compl_model = gtk_tree_view_get_model (aw->priv->tree_view); - actions_model = gtk_tree_view_get_model (aw->priv->action_tree_view); - compl_sel = gtk_tree_view_get_selection (aw->priv->tree_view); - actions_sel = gtk_tree_view_get_selection (aw->priv->action_tree_view); - - n_compl = gtk_tree_model_iter_n_children (compl_model, NULL); - n_actions = gtk_tree_model_iter_n_children (actions_model, NULL); - n_items = n_compl + n_actions; - - /* Index 0 no selection. - * Index 1 in the completion. - * Index -1 in the actions. */ - new_index = aw->priv->sel_index + dir; - - /* On overflow stay on 0/max if you are no already there. Otherwise - go on the opposite limit */ - if (new_index < 0) - { - new_index = (aw->priv->sel_index != 0) ? 0 : n_items; - } - else if (new_index > n_items) - { - new_index = (aw->priv->sel_index != n_items) ? n_items : 0; - } - - gtk_tree_selection_unselect_all (compl_sel); - gtk_tree_selection_unselect_all (actions_sel); - - if (new_index == 0) - { - } - else if (new_index > n_compl) - { - GtkTreeIter iter; - GtkTreePath *path; - - gtk_tree_selection_unselect_all (compl_sel); - gtk_tree_model_iter_nth_child (actions_model, &iter, NULL, - new_index - n_compl - 1); - gtk_tree_selection_select_iter (actions_sel, &iter); - - path = gtk_tree_model_get_path (actions_model, &iter); - gtk_tree_view_scroll_to_cell (aw->priv->action_tree_view, - path, NULL, FALSE, 0, 0); - gtk_tree_path_free (path); - - aw->priv->action = TRUE; - } - else if (new_index <= n_compl) - { - GtkTreeIter iter; - GtkTreePath *path; - - gtk_tree_selection_unselect_all (actions_sel); - gtk_tree_model_iter_nth_child (compl_model, &iter, NULL, - new_index - 1); - gtk_tree_selection_select_iter (compl_sel, &iter); - - path = gtk_tree_model_get_path (compl_model, &iter); - gtk_tree_view_scroll_to_cell (aw->priv->tree_view, - path, NULL, FALSE, 0, 0); - gtk_tree_path_free (path); - - aw->priv->action = FALSE; - } - else - { - g_assert_not_reached (); - } - - aw->priv->sel_index = new_index; -} - -static gboolean -ephy_autocompletion_window_key_press_hack (EphyAutocompletionWindow *aw, - guint keyval) -{ - switch (keyval) - { - case GDK_Up: - move_selection (aw, -1); - break; - case GDK_Down: - move_selection (aw, +1); - break; - case GDK_Page_Down: - move_selection (aw, +5); - break; - case GDK_Page_Up: - move_selection (aw, -5); - break; - case GDK_Return: - case GDK_space: - if (aw->priv->selected) - { - g_signal_emit (aw, EphyAutocompletionWindowSignals - [ACTIVATED], 0, aw->priv->selected, - aw->priv->action); - } - break; - } - - switch (keyval) - { - case GDK_Up: - case GDK_Down: - case GDK_Page_Down: - case GDK_Page_Up: - if (aw->priv->selected) - { - g_signal_emit (aw, EphyAutocompletionWindowSignals - [SELECTED], 0, aw->priv->selected, - aw->priv->action); - } - break; - default: - break; - } - - return TRUE; -} - -static gboolean -ephy_autocompletion_window_key_press_cb (GtkWidget *widget, - GdkEventKey *event, - EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - GdkEventKey tmp_event; - - if (event->keyval == GDK_Up || event->keyval == GDK_Down - || event->keyval == GDK_Page_Up || event->keyval == GDK_Page_Down - || ((event->keyval == GDK_space || event->keyval == GDK_Return) - && p->selected)) - { - return ephy_autocompletion_window_key_press_hack - (aw, event->keyval); - } - - tmp_event = *event; - gtk_widget_event (p->parent, (GdkEvent *)&tmp_event); - - return TRUE; -} - -void -ephy_autocompletion_window_hide (EphyAutocompletionWindow *aw) -{ - if (aw->priv->window && aw->priv->shown) - { - gtk_widget_hide (aw->priv->window); - gtk_grab_remove (aw->priv->window); - gdk_pointer_ungrab (GDK_CURRENT_TIME); - gdk_keyboard_ungrab (GDK_CURRENT_TIME); - ephy_autocompletion_window_unselect (aw); - g_signal_emit (aw, EphyAutocompletionWindowSignals[EPHY_AUTOCOMPLETION_WINDOW_HIDDEN], 0); - aw->priv->sel_index = 0; - aw->priv->action = FALSE; - } - g_free (aw->priv->selected); - aw->priv->selected = NULL; - aw->priv->shown = FALSE; -} - -void -ephy_autocompletion_window_unselect (EphyAutocompletionWindow *aw) -{ - EphyAutocompletionWindowPrivate *p = aw->priv; - GtkTreeSelection *ts = gtk_tree_view_get_selection (p->tree_view); - gtk_tree_selection_unselect_all (ts); -} - -static void -ephy_autocompletion_window_event_after_cb (GtkWidget *wid, GdkEvent *event, - EphyAutocompletionWindow *aw) -{ - gboolean action; - EphyAutocompletionWindowPrivate *p = aw->priv; - - action = (wid == GTK_WIDGET (p->action_tree_view)); - - if (event->type == GDK_BUTTON_PRESS - && ((GdkEventButton *) event)->button == 1) - { - if (p->selected) - { - g_signal_emit (aw, EphyAutocompletionWindowSignals - [ACTIVATED], 0, p->selected, action); - } - } -} diff --git a/lib/widgets/ephy-autocompletion-window.h b/lib/widgets/ephy-autocompletion-window.h deleted file mode 100644 index 63b4a4ed3..000000000 --- a/lib/widgets/ephy-autocompletion-window.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2002 Ricardo Fernández Pascual - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -#ifndef EPHY_AUTOCOMPLETION_WINDOW_H -#define EPHY_AUTOCOMPLETION_WINDOW_H - -#include <glib-object.h> -#include <gtk/gtkwidget.h> - -#include "ephy-autocompletion.h" - -G_BEGIN_DECLS - -/* object forward declarations */ - -typedef struct _EphyAutocompletionWindow EphyAutocompletionWindow; -typedef struct _EphyAutocompletionWindowClass EphyAutocompletionWindowClass; -typedef struct _EphyAutocompletionWindowPrivate EphyAutocompletionWindowPrivate; - -/** - * Editor object - */ - -#define EPHY_TYPE_AUTOCOMPLETION_WINDOW (ephy_autocompletion_window_get_type()) -#define EPHY_AUTOCOMPLETION_WINDOW(object) (G_TYPE_CHECK_INSTANCE_CAST((object), \ - EPHY_TYPE_AUTOCOMPLETION_WINDOW,\ - EphyAutocompletionWindow)) -#define EPHY_AUTOCOMPLETION_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \ - EPHY_TYPE_AUTOCOMPLETION_WINDOW,\ - EphyAutocompletionWindowClass)) -#define EPHY_IS_AUTOCOMPLETION_WINDOW(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), \ - EPHY_TYPE_AUTOCOMPLETION_WINDOW)) -#define EPHY_IS_AUTOCOMPLETION_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), \ - EPHY_TYPE_AUTOCOMPLETION_WINDOW)) -#define EPHY_AUTOCOMPLETION_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), \ - EPHY_TYPE_AUTOCOMPLETION_WINDOW,\ - EphyAutocompletionWindowClass)) - -struct _EphyAutocompletionWindowClass -{ - GObjectClass parent_class; - - /* signals */ - void (*hidden) (EphyAutocompletionWindow *aw); - void (*activated) (EphyAutocompletionWindow *aw, - const char *target, - int action); - void (*selected) (EphyAutocompletionWindow *aw, - const char *target, - int action); -}; - -/* Remember: fields are public read-only */ -struct _EphyAutocompletionWindow -{ - GObject parent_object; - - EphyAutocompletionWindowPrivate *priv; -}; - -GType ephy_autocompletion_window_get_type (void); -EphyAutocompletionWindow *ephy_autocompletion_window_new (EphyAutocompletion *ac, - GtkWidget *parent); -void ephy_autocompletion_window_set_parent_widget (EphyAutocompletionWindow *aw, - GtkWidget *w); -void ephy_autocompletion_window_set_autocompletion (EphyAutocompletionWindow *aw, - EphyAutocompletion *ac); -void ephy_autocompletion_window_show (EphyAutocompletionWindow *aw); -void ephy_autocompletion_window_hide (EphyAutocompletionWindow *aw); -void ephy_autocompletion_window_unselect (EphyAutocompletionWindow *aw); - -G_END_DECLS - -#endif diff --git a/lib/widgets/ephy-location-entry.c b/lib/widgets/ephy-location-entry.c index bca1d9790..c160a898e 100644 --- a/lib/widgets/ephy-location-entry.c +++ b/lib/widgets/ephy-location-entry.c @@ -20,106 +20,78 @@ * $Id$ */ +#include "ephy-tree-model-node.h" #include "ephy-location-entry.h" -#include "ephy-autocompletion-window.h" #include "ephy-marshal.h" -#include "eel-gconf-extensions.h" -#include "ephy-prefs.h" #include "ephy-debug.h" +#include "ephy-file-helpers.h" -#include <glib-object.h> -#include <gtk/gtkhbox.h> +#include <gtk/gtktoolbar.h> +#include <gtk/gtkliststore.h> +#include <gtk/gtkcomboboxentry.h> #include <gtk/gtkentry.h> #include <gtk/gtkwindow.h> -#include <gdk/gdkkeysyms.h> -#include <gtk/gtkmain.h> -#include <gtk/gtktoolbar.h> -#include <gtk/gtktooltips.h> -#include <libgnomeui/gnome-entry.h> +#include <gtk/gtkcellrenderertext.h> +#include <gtk/gtkcelllayout.h> +#include <gtk/gtktreemodelsort.h> #include <string.h> -/** - * Private data - */ - #define EPHY_LOCATION_ENTRY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_LOCATION_ENTRY, EphyLocationEntryPrivate)) -struct _EphyLocationEntryPrivate { +struct _EphyLocationEntryPrivate +{ + EphyTreeModelNode *combo_model; GtkWidget *combo; GtkWidget *entry; char *before_completion; - EphyAutocompletion *autocompletion; - EphyAutocompletionWindow *autocompletion_window; - gboolean autocompletion_window_visible; - gint show_alternatives_timeout; - gboolean block_set_autocompletion_key; - gboolean going_to_site; gboolean user_changed; gboolean activation_mode; + EphyNodeDb *combo_db; + EphyNode *combo_root; + char *xml_file; + + guint text_col; + guint action_col; + guint keywords_col; + guint relevance_col; +}; - char *autocompletion_key; - char *last_action_target; +static char *web_prefixes [] = +{ + "http://www.", + "http://", + "https://www.", + "https://", + "www." }; +static int n_web_prefixes = G_N_ELEMENTS (web_prefixes); -#define AUTOCOMPLETION_DELAY 10 -#define SHOW_ALTERNATIVES_DELAY 100 - -/** - * Private functions, only availble from this file - */ -static void ephy_location_entry_class_init (EphyLocationEntryClass *klass); -static void ephy_location_entry_init (EphyLocationEntry *w); -static void ephy_location_entry_finalize_impl (GObject *o); -static void ephy_location_entry_construct_contents (EphyLocationEntry *w); -static gboolean ephy_location_entry_key_press_event_cb (GtkWidget *entry, GdkEventKey *event, - EphyLocationEntry *w); -static void ephy_location_entry_activate_cb (GtkEntry *entry, - EphyLocationEntry *w); -static void ephy_location_entry_autocompletion_sources_changed_cb - (EphyAutocompletion *aw, - EphyLocationEntry *w); -static gint ephy_location_entry_autocompletion_show_alternatives_to (EphyLocationEntry *w); -static void ephy_location_entry_autocompletion_window_url_activated_cb - (EphyAutocompletionWindow *aw, - const gchar *target, - int action, - EphyLocationEntry *w); -static void ephy_location_entry_list_event_after_cb (GtkWidget *list, - GdkEvent *event, - EphyLocationEntry *e); -static void ephy_location_entry_editable_changed_cb (GtkEditable *editable, - EphyLocationEntry *e); -static void ephy_location_entry_set_autocompletion_key (EphyLocationEntry *e); -static void ephy_location_entry_autocompletion_show_alternatives (EphyLocationEntry *w); -static void ephy_location_entry_autocompletion_hide_alternatives (EphyLocationEntry *w); -static void ephy_location_entry_autocompletion_window_hidden_cb - (EphyAutocompletionWindow *aw, - EphyLocationEntry *w); -static void -insert_text_cb (GtkWidget *editable, - char *new_text, - int new_text_length, - int *position, - EphyLocationEntry *w); -static void -delete_text_cb (GtkWidget *editable, - int start_pos, - int end_pos, - EphyLocationEntry *w); +static void ephy_location_entry_class_init (EphyLocationEntryClass *klass); +static void ephy_location_entry_init (EphyLocationEntry *le); +static void ephy_location_entry_finalize (GObject *o); static GObjectClass *parent_class = NULL; -/** - * Signals enums and ids - */ -enum EphyLocationEntrySignalsEnum { - ACTIVATED, +enum EphyLocationEntrySignalsEnum +{ USER_CHANGED, LAST_SIGNAL }; static gint EphyLocationEntrySignals[LAST_SIGNAL]; -#define MENU_ID "ephy-location-entry-menu-id" +enum +{ + LOCATION_HISTORY_NODE_ID = 1 +}; + +enum +{ + EPHY_NODE_LOC_HISTORY_PROP_TEXT = 1 +}; + +#define MAX_LOC_HISTORY_ITEMS 10 +#define EPHY_LOC_HISTORY_XML_ROOT "ephy_location_history" +#define EPHY_LOC_HISTORY_XML_VERSION "0.1" GType ephy_location_entry_get_type (void) @@ -172,20 +144,10 @@ ephy_location_entry_class_init (EphyLocationEntryClass *klass) parent_class = g_type_class_peek_parent (klass); - object_class->finalize = ephy_location_entry_finalize_impl; + object_class->finalize = ephy_location_entry_finalize; tool_item_class->set_tooltip = ephy_location_entry_set_tooltip; - EphyLocationEntrySignals[ACTIVATED] = g_signal_new ( - "activated", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP, - G_STRUCT_OFFSET (EphyLocationEntryClass, activated), - NULL, NULL, - ephy_marshal_VOID__STRING_STRING, - G_TYPE_NONE, - 2, - G_TYPE_STRING, - G_TYPE_STRING); EphyLocationEntrySignals[USER_CHANGED] = g_signal_new ( "user_changed", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP, @@ -215,78 +177,30 @@ ephy_location_entry_activation_finished (EphyLocationEntry *entry) } static gboolean -location_focus_out_cb (GtkWidget *widget, GdkEventFocus *event, EphyLocationEntry *w) +location_focus_out_cb (GtkWidget *leidget, GdkEventFocus *event, EphyLocationEntry *le) { - ephy_location_entry_activation_finished (w); + ephy_location_entry_activation_finished (le); return FALSE; } static void -ephy_location_entry_init (EphyLocationEntry *w) +editable_changed_cb (GtkEditable *editable, EphyLocationEntry *e) { - EphyLocationEntryPrivate *p; - - LOG ("EphyLocationEntry initialising %p", w) - - p = EPHY_LOCATION_ENTRY_GET_PRIVATE (w); - w->priv = p; - - p->last_action_target = NULL; - p->before_completion = NULL; - p->user_changed = TRUE; - p->activation_mode = FALSE; - p->autocompletion_key = NULL; - p->autocompletion_window_visible = FALSE; - - ephy_location_entry_construct_contents (w); - - gtk_tool_item_set_expand (GTK_TOOL_ITEM (w), TRUE); - - g_signal_connect (w->priv->entry, - "focus_out_event", - G_CALLBACK (location_focus_out_cb), - w); -} - -static void -ephy_location_entry_finalize_impl (GObject *o) -{ - EphyLocationEntry *w = EPHY_LOCATION_ENTRY (o); - EphyLocationEntryPrivate *p = w->priv; + EphyLocationEntryPrivate *p = e->priv; - if (p->autocompletion) + if (p->user_changed) { - g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, w); - - g_signal_handlers_disconnect_matched (p->autocompletion_window, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, w); - - g_object_unref (G_OBJECT (p->autocompletion)); - g_object_unref (G_OBJECT (p->autocompletion_window)); + g_signal_emit (e, EphyLocationEntrySignals[USER_CHANGED], 0); } - - LOG ("EphyLocationEntry finalized") - - g_free (p->before_completion); - g_free (p->autocompletion_key); - - G_OBJECT_CLASS (parent_class)->finalize (o); -} - -GtkWidget * -ephy_location_entry_new (void) -{ - return GTK_WIDGET (g_object_new (EPHY_TYPE_LOCATION_ENTRY, NULL)); } static gboolean -ephy_location_entry_button_press_event_cb (GtkWidget *entry, GdkEventButton *event, EphyLocationEntry *w) +entry_button_press_cb (GtkWidget *entry, GdkEventButton *event, EphyLocationEntry *le) { if (event->button == 1 && event->type == GDK_2BUTTON_PRESS) { - ephy_location_entry_activate (w); + ephy_location_entry_activate (le); return TRUE; } @@ -294,513 +208,393 @@ ephy_location_entry_button_press_event_cb (GtkWidget *entry, GdkEventButton *eve } static void -ephy_location_entry_construct_contents (EphyLocationEntry *w) +add_to_history (EphyLocationEntry *le, const char *text) { - EphyLocationEntryPrivate *p = w->priv; - GtkWidget *hbox, *list; - - LOG ("EphyLocationEntry constructing contents %p", w) + GPtrArray *children; + int i, l, n_items, index = -1; + int *order; - hbox = gtk_hbox_new (FALSE, 0); - gtk_container_add (GTK_CONTAINER (w), hbox); - gtk_widget_show (hbox); - - p->combo = gnome_entry_new ("ephy-url-history"); - p->entry = GTK_COMBO (p->combo)->entry; - gtk_widget_show (p->combo); - gtk_box_pack_start (GTK_BOX (hbox), p->combo, TRUE, TRUE, 0); - - g_signal_connect (p->entry, "key-press-event", - G_CALLBACK (ephy_location_entry_key_press_event_cb), w); - g_signal_connect (p->entry, "insert-text", - G_CALLBACK (insert_text_cb), w); - g_signal_connect (p->entry, "delete-text", - G_CALLBACK (delete_text_cb), w); - g_signal_connect (p->entry, "button-press-event", - G_CALLBACK (ephy_location_entry_button_press_event_cb), w); - - g_signal_connect (p->entry, "activate", - G_CALLBACK (ephy_location_entry_activate_cb), w); - - g_signal_connect (p->entry, "changed", - G_CALLBACK (ephy_location_entry_editable_changed_cb), w); - - list = GTK_COMBO (p->combo)->list; + /* check if it already exists */ + children = ephy_node_get_children (le->priv->combo_root); + n_items = children->len; + for (i = 0; i < n_items; i++) + { + EphyNode *kid; + const char *node_text; + + kid = g_ptr_array_index (children, i); + node_text = ephy_node_get_property_string + (kid, EPHY_NODE_LOC_HISTORY_PROP_TEXT); + + if (strcmp (text, node_text) == 0) + { + index = i; + break; + } + } + ephy_node_thaw (le->priv->combo_root); - g_signal_connect_after (list, "event-after", - G_CALLBACK (ephy_location_entry_list_event_after_cb), w); + /* it doesnt exist, add it */ + if (index < 0) + { + GValue value = { 0, }; + EphyNode *node; -} + node = ephy_node_new (le->priv->combo_db); + g_value_init (&value, G_TYPE_STRING); + g_value_set_string (&value, text); + ephy_node_set_property (node, EPHY_NODE_LOC_HISTORY_PROP_TEXT, + &value); -static gboolean -ephy_location_ignore_prefix (EphyLocationEntry *w) -{ - const char *text; - int text_len; - int i, k; - gboolean result = FALSE; - static const gchar *prefixes[] = { - EPHY_AUTOCOMPLETION_USUAL_WEB_PREFIXES, - NULL - }; + if (n_items >= MAX_LOC_HISTORY_ITEMS) + { + EphyNode *last; - text = ephy_location_entry_get_location (w); - g_return_val_if_fail (text != NULL, FALSE); + last = ephy_node_get_nth_child + (le->priv->combo_root, n_items - 1); + ephy_node_remove_child (le->priv->combo_root, last); + } - text_len = g_utf8_strlen (text, -1); + ephy_node_add_child (le->priv->combo_root, node); + } - for (i = 0; prefixes[i] != NULL; i++) + /* move it at the top */ + n_items = ephy_node_get_n_children (le->priv->combo_root); + order = g_new0 (int, n_items); + l = 1; + if (index == -1) index = n_items -1; + for (i = 0; i < n_items; i++) { - const char *prefix = prefixes[i]; - - for (k = 0; k < g_utf8_strlen (prefix, -1); k++) + if (index != i) { - if (text_len == (k + 1) && - (strncmp (text, prefix, k + 1) == 0)) - { - result = TRUE; - } + order[i] = l; + l++; + } + else + { + order[i] = 0; } } - - return result; + ephy_node_reorder_children (le->priv->combo_root, order); + g_free (order); } -static gint -ephy_location_entry_autocompletion_show_alternatives_to (EphyLocationEntry *w) +static void +entry_activate_cb (GtkEntry *entry, EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = w->priv; - - g_free (p->before_completion); - p->before_completion = gtk_editable_get_chars (GTK_EDITABLE (p->entry), 0, -1); - - if (ephy_location_ignore_prefix (w)) return FALSE; + char *content; - if (p->autocompletion) + content = gtk_editable_get_chars (GTK_EDITABLE(entry), 0, -1); + if (content) { - LOG ("Show alternatives") - ephy_location_entry_set_autocompletion_key (w); - ephy_location_entry_autocompletion_show_alternatives (w); + add_to_history (le, content); + g_free (content); } - p->show_alternatives_timeout = 0; - return FALSE; } -static void -ephy_location_entry_autocompletion_hide_alternatives (EphyLocationEntry *w) -{ - EphyLocationEntryPrivate *p = w->priv; - if (p->autocompletion_window) +static gboolean +completion_func (GtkEntryCompletion *completion, + const char *key, + GtkTreeIter *iter, + gpointer data) +{ + int i; + char *item = NULL; + char *keywords = NULL; + char *normalized_string, *normalized_keywords; + char *case_normalized_string, *case_normalized_keywords; + gboolean ret = FALSE; + EphyLocationEntry *le = EPHY_LOCATION_ENTRY (data); + GtkTreeModel *model; + + model = gtk_entry_completion_get_model (completion); + + gtk_tree_model_get (model, iter, + le->priv->text_col, &item, -1); + gtk_tree_model_get (model, iter, + le->priv->keywords_col, &keywords, -1); + + normalized_string = g_utf8_normalize (item, -1, G_NORMALIZE_ALL); + case_normalized_string = g_utf8_casefold (normalized_string, -1); + normalized_keywords = g_utf8_normalize (keywords, -1, G_NORMALIZE_ALL); + case_normalized_keywords = g_utf8_casefold (normalized_keywords, -1); + + if (!strncmp (key, case_normalized_string, strlen (key))) { - ephy_autocompletion_window_hide (p->autocompletion_window); - p->autocompletion_window_visible = FALSE; + ret = TRUE; } - - p->autocompletion_window_visible = FALSE; - - if (p->show_alternatives_timeout) + else if (strstr (case_normalized_keywords, key)) { - g_source_remove (p->show_alternatives_timeout); - p->show_alternatives_timeout = 0; + ret = TRUE; } -} - -static void -ephy_location_entry_autocompletion_show_alternatives (EphyLocationEntry *w) -{ - EphyLocationEntryPrivate *p = w->priv; + else + { + for (i = 0; i < n_web_prefixes; i++) + { + char *key_prefixed; - /* Reset it because if we do a grab on click (like when pasting - text in the location entry, the entry will lose the release - event and will not reset it. It's what gtk does for the entry - popup */ - GTK_ENTRY (p->entry)->button = 0; + key_prefixed = g_strconcat (web_prefixes[i], key, NULL); - if (p->autocompletion_window) - { - ephy_autocompletion_window_show (p->autocompletion_window); - p->autocompletion_window_visible = TRUE; - } -} + if (!strncmp (key_prefixed, case_normalized_string, + strlen (key_prefixed))) + { + ret = TRUE; + break; + } -static void -ephy_location_entry_autocompletion_unselect_alternatives (EphyLocationEntry *w) -{ - EphyLocationEntryPrivate *p = w->priv; - if (p->autocompletion_window) - { - ephy_autocompletion_window_unselect (p->autocompletion_window); + g_free (key_prefixed); + } } -} -static int -get_editable_number_of_chars (GtkEditable *editable) -{ - char *text; - int length; + g_free (item); + g_free (normalized_string); + g_free (case_normalized_string); - text = gtk_editable_get_chars (editable, 0, -1); - length = g_utf8_strlen (text, -1); - g_free (text); - return length; + return ret; } static gboolean -position_is_at_end (GtkEditable *editable) -{ - int end; - - end = get_editable_number_of_chars (editable); - return gtk_editable_get_position (editable) == end; -} - -static void -delete_text_cb (GtkWidget *editable, - int start_pos, - int end_pos, - EphyLocationEntry *w) -{ - ephy_location_entry_autocompletion_hide_alternatives (w); -} - -static void -insert_text_cb (GtkWidget *editable, - char *new_text, - int new_text_length, - int *position, - EphyLocationEntry *w) +match_selected_cb (GtkEntryCompletion *completion, + GtkTreeModel *model, + GtkTreeIter *iter, + EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = w->priv; - GtkWidget *window; + char *item = NULL; - window = gtk_widget_get_toplevel (editable); - g_return_if_fail (window != NULL); - if (!GTK_WINDOW (window)->has_focus) return; + gtk_tree_model_get (model, iter, + le->priv->action_col, &item, -1); - if (p->going_to_site) return; + ephy_location_entry_set_location (le, item); + g_signal_emit_by_name (le->priv->entry, "activate"); - if (p->show_alternatives_timeout != 0) - { - g_source_remove (p->show_alternatives_timeout); - p->show_alternatives_timeout = 0; - } + g_free (item); - ephy_location_entry_autocompletion_unselect_alternatives (w); - if (position_is_at_end (GTK_EDITABLE (editable))) - { - p->show_alternatives_timeout = g_timeout_add - (SHOW_ALTERNATIVES_DELAY, - (GSourceFunc) ephy_location_entry_autocompletion_show_alternatives_to, w); - } + return TRUE; } -static gboolean -ephy_location_entry_key_press_event_cb (GtkWidget *entry, GdkEventKey *event, EphyLocationEntry *w) +static void +ephy_location_entry_construct_contents (EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = w->priv; - GtkWidget *window; + EphyLocationEntryPrivate *p = le->priv; + int combo_text_col; - switch (event->keyval) - { - case GDK_Left: - case GDK_Right: - ephy_location_entry_autocompletion_hide_alternatives (w); - break; - case GDK_Escape: - ephy_location_entry_set_location (w, p->before_completion); - gtk_editable_set_position (GTK_EDITABLE (p->entry), -1); - ephy_location_entry_autocompletion_hide_alternatives (w); - break; - case GDK_Tab: - case GDK_KP_Tab: - if (p->autocompletion_window_visible) - { - ephy_location_entry_autocompletion_hide_alternatives (w); - window = gtk_widget_get_toplevel (entry); - g_signal_emit_by_name (window, "move_focus", 1); - } - break; - default: - break; - } + LOG ("EphyLocationEntry constructing contents %p", le) - return FALSE; -} + p->combo_model = ephy_tree_model_node_new (p->combo_root, NULL); + combo_text_col = ephy_tree_model_node_add_prop_column + (p->combo_model, G_TYPE_STRING, EPHY_NODE_LOC_HISTORY_PROP_TEXT); -static gboolean -ephy_location_entry_content_is_text (const char *content) -{ - return ((g_strrstr (content, ".") == NULL) && - (g_strrstr (content, "/") == NULL) && - (g_strrstr (content, ":") == NULL)); + p->combo = gtk_combo_box_entry_new (GTK_TREE_MODEL (p->combo_model), + combo_text_col); + gtk_container_add (GTK_CONTAINER (le), p->combo); + gtk_widget_show (p->combo); + p->entry = GTK_BIN (p->combo)->child; + + g_signal_connect (p->entry, "button_press_event", + G_CALLBACK (entry_button_press_cb), le); + g_signal_connect (p->entry, "changed", + G_CALLBACK (editable_changed_cb), le); + g_signal_connect (p->entry, "activate", + G_CALLBACK (entry_activate_cb), le); } static void -ephy_location_entry_activate_cb (GtkEntry *entry, EphyLocationEntry *w) +ephy_location_entry_init (EphyLocationEntry *le) { - char *content; - char *target = NULL; + EphyLocationEntryPrivate *p; - content = gtk_editable_get_chars (GTK_EDITABLE(entry), 0, -1); - if (w->priv->last_action_target && - ephy_location_entry_content_is_text (content)) - { - target = g_strdup (w->priv->last_action_target); - } - else - { - target = content; - content = NULL; - g_free ( w->priv->last_action_target); - w->priv->last_action_target = NULL; - } + LOG ("EphyLocationEntry initialising %p", le) - ephy_location_entry_autocompletion_hide_alternatives (w); + p = EPHY_LOCATION_ENTRY_GET_PRIVATE (le); + le->priv = p; - LOG ("In ephy_location_entry_activate_cb, activating %s", content) + p->user_changed = TRUE; + p->activation_mode = FALSE; + p->combo_db = ephy_node_db_new ("NodeDB"); + p->combo_root = ephy_node_new_with_id + (p->combo_db, LOCATION_HISTORY_NODE_ID); + p->xml_file = g_build_filename (ephy_dot_dir (), + "ephy-location-history.xml", + NULL); + ephy_node_db_load_from_file (p->combo_db, p->xml_file, + EPHY_LOC_HISTORY_XML_ROOT, + EPHY_LOC_HISTORY_XML_VERSION); + + ephy_location_entry_construct_contents (le); - g_signal_emit (w, EphyLocationEntrySignals[ACTIVATED], 0, content, target); - ephy_location_entry_activation_finished (w); + gtk_tool_item_set_expand (GTK_TOOL_ITEM (le), TRUE); - g_free (content); - g_free (target); + g_signal_connect (le->priv->entry, + "focus_out_event", + G_CALLBACK (location_focus_out_cb), le); } static void -ephy_location_entry_autocompletion_sources_changed_cb (EphyAutocompletion *aw, - EphyLocationEntry *w) -{ - EphyLocationEntryPrivate *p = w->priv; - - LOG ("in ephy_location_entry_autocompletion_sources_changed_cb") +save_location_history (EphyLocationEntry *le) +{ + xmlDocPtr doc; + xmlNodePtr root; + GPtrArray *children; + int i; + + xmlIndentTreeOutput = TRUE; + + doc = xmlNewDoc ("1.0"); + root = xmlNewDocNode (doc, NULL, EPHY_LOC_HISTORY_XML_ROOT, NULL); + xmlSetProp (root, "version", EPHY_LOC_HISTORY_XML_VERSION); + xmlDocSetRootElement (doc, root); + + children = ephy_node_get_children (le->priv->combo_root); + for (i = 0; i < children->len; i++) + { + EphyNode *kid; + + kid = g_ptr_array_index (children, i); + + ephy_node_save_to_xml (kid, root); + } + ephy_node_thaw (le->priv->combo_root); - if (p->show_alternatives_timeout == 0 - && p->autocompletion_window_visible) - { - p->show_alternatives_timeout = g_timeout_add - (SHOW_ALTERNATIVES_DELAY, - (GSourceFunc) ephy_location_entry_autocompletion_show_alternatives_to, w); - } + ephy_file_save_xml (le->priv->xml_file, doc); + xmlFreeDoc(doc); } -void -ephy_location_entry_set_location (EphyLocationEntry *w, - const gchar *new_location) +static void +ephy_location_entry_finalize (GObject *o) { - EphyLocationEntryPrivate *p = w->priv; - int pos; - - g_return_if_fail (new_location != NULL); - - p->user_changed = FALSE; + EphyLocationEntry *le; + + le = EPHY_LOCATION_ENTRY (o); - g_signal_handlers_block_by_func (G_OBJECT (p->entry), - delete_text_cb, w); - gtk_editable_delete_text (GTK_EDITABLE (p->entry), 0, -1); - g_signal_handlers_unblock_by_func (G_OBJECT (p->entry), - delete_text_cb, w); + save_location_history (le); + g_free (le->priv->xml_file); - g_signal_handlers_block_by_func (G_OBJECT (p->entry), - insert_text_cb, w); - gtk_editable_insert_text (GTK_EDITABLE (p->entry), new_location, strlen(new_location), - &pos); - g_signal_handlers_unblock_by_func (G_OBJECT (p->entry), - insert_text_cb, w); + LOG ("EphyLocationEntry finalized") - p->user_changed = TRUE; + G_OBJECT_CLASS (parent_class)->finalize (o); } -const char * -ephy_location_entry_get_location (EphyLocationEntry *w) +GtkWidget * +ephy_location_entry_new (void) { - return gtk_entry_get_text (GTK_ENTRY (w->priv->entry)); + return GTK_WIDGET (g_object_new (EPHY_TYPE_LOCATION_ENTRY, NULL)); } -static void -ephy_location_entry_autocompletion_window_url_selected_cb (EphyAutocompletionWindow *aw, - const char *target, - int action, - EphyLocationEntry *w) +static gint +sort_func (GtkTreeModel *model, + GtkTreeIter *a, + GtkTreeIter *b, + gpointer data) { - if (target) - { - ephy_location_entry_set_location (w, action ? w->priv->before_completion : target); - } - else - { - ephy_location_entry_set_location (w, w->priv->before_completion); - } + gint valuea, valueb; + EphyLocationEntry *le = EPHY_LOCATION_ENTRY (data); + + gtk_tree_model_get (model, a, + le->priv->relevance_col, &valuea, -1); + gtk_tree_model_get (model, b, + le->priv->relevance_col, &valueb, -1); - gtk_editable_set_position (GTK_EDITABLE (w->priv->entry), -1); + return valueb - valuea; } void -ephy_location_entry_set_autocompletion (EphyLocationEntry *w, - EphyAutocompletion *ac) -{ - EphyLocationEntryPrivate *p = w->priv; - if (p->autocompletion) - { - g_signal_handlers_disconnect_matched (p->autocompletion, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, w); - - g_signal_handlers_disconnect_matched (p->autocompletion_window, G_SIGNAL_MATCH_DATA, 0, 0, - NULL, NULL, w); - - g_object_unref (G_OBJECT (p->autocompletion)); - g_object_unref (p->autocompletion_window); - } - p->autocompletion = ac; - if (p->autocompletion) - { - g_object_ref (G_OBJECT (p->autocompletion)); - p->autocompletion_window = ephy_autocompletion_window_new (p->autocompletion, - p->entry); - g_signal_connect (p->autocompletion_window, "activated", - G_CALLBACK (ephy_location_entry_autocompletion_window_url_activated_cb), - w); - - g_signal_connect (p->autocompletion_window, "selected", - G_CALLBACK (ephy_location_entry_autocompletion_window_url_selected_cb), - w); - - g_signal_connect (p->autocompletion_window, "hidden", - G_CALLBACK (ephy_location_entry_autocompletion_window_hidden_cb), - w); - - g_signal_connect (p->autocompletion, "sources-changed", - G_CALLBACK (ephy_location_entry_autocompletion_sources_changed_cb), - w); - - ephy_location_entry_set_autocompletion_key (w); - } - +ephy_location_entry_set_completion (EphyLocationEntry *le, + GtkTreeModel *model, + guint text_col, + guint action_col, + guint keywords_col, + guint relevance_col) +{ + GtkTreeModel *sort_model; + GtkEntryCompletion *completion; + GtkCellRenderer *cell; + + le->priv->text_col = text_col; + le->priv->action_col = action_col; + le->priv->keywords_col = keywords_col; + le->priv->relevance_col = relevance_col; + + sort_model = gtk_tree_model_sort_new_with_model (model); + g_object_unref (model); + gtk_tree_sortable_set_default_sort_func + (GTK_TREE_SORTABLE (sort_model), + sort_func, le, NULL); + + completion = gtk_entry_completion_new (); + gtk_entry_completion_set_model (completion, sort_model); + g_object_unref (sort_model); + gtk_entry_completion_set_match_func (completion, completion_func, le, NULL); + g_signal_connect (completion, "match_selected", + G_CALLBACK (match_selected_cb), le); + + cell = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion), + cell, TRUE); + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion), + cell, "text", text_col); + + gtk_entry_set_completion (GTK_ENTRY (le->priv->entry), completion); } -static void -ephy_location_entry_autocompletion_window_url_activated_cb (EphyAutocompletionWindow *aw, - const char *target, - int action, - EphyLocationEntry *w) +void +ephy_location_entry_set_location (EphyLocationEntry *le, + const gchar *new_location) { - char *content; + EphyLocationEntryPrivate *p = le->priv; - if (action) - { - if (w->priv->last_action_target) - g_free (w->priv->last_action_target); - w->priv->last_action_target = g_strdup (target); - } - else - { - ephy_location_entry_set_location (w, target); - } - - content = gtk_editable_get_chars (GTK_EDITABLE(w->priv->entry), 0, -1); - - LOG ("In location_entry_autocompletion_window_url_activated_cb, going to %s", content); + g_return_if_fail (new_location != NULL); - ephy_location_entry_autocompletion_hide_alternatives (w); + p->user_changed = FALSE; - g_signal_emit (w, EphyLocationEntrySignals[ACTIVATED], 0, - action ? content : NULL, target); + gtk_entry_set_text (GTK_ENTRY (p->entry), new_location); - g_free (content); + p->user_changed = TRUE; } -static void -ephy_location_entry_autocompletion_window_hidden_cb (EphyAutocompletionWindow *aw, - EphyLocationEntry *w) +const char * +ephy_location_entry_get_location (EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = w->priv; - - LOG ("In location_entry_autocompletion_window_hidden_cb"); - - p->autocompletion_window_visible = FALSE; - - if (p->show_alternatives_timeout) - { - g_source_remove (p->show_alternatives_timeout); - p->show_alternatives_timeout = 0; - } + return gtk_entry_get_text (GTK_ENTRY (le->priv->entry)); } void -ephy_location_entry_activate (EphyLocationEntry *w) +ephy_location_entry_activate (EphyLocationEntry *le) { GtkWidget *toplevel, *toolbar; - toolbar = gtk_widget_get_ancestor (GTK_WIDGET (w), GTK_TYPE_TOOLBAR); + toolbar = gtk_widget_get_ancestor (GTK_WIDGET (le), GTK_TYPE_TOOLBAR); if (!GTK_WIDGET_VISIBLE (toolbar)) { - w->priv->activation_mode = TRUE; + le->priv->activation_mode = TRUE; gtk_widget_show (toolbar); } - toplevel = gtk_widget_get_toplevel (w->priv->entry); + toplevel = gtk_widget_get_toplevel (le->priv->entry); - gtk_editable_select_region (GTK_EDITABLE(w->priv->entry), + gtk_editable_select_region (GTK_EDITABLE(le->priv->entry), 0, -1); gtk_window_set_focus (GTK_WINDOW(toplevel), - w->priv->entry); -} - -static void -ephy_location_entry_list_event_after_cb (GtkWidget *list, - GdkEvent *event, - EphyLocationEntry *e) -{ - if (event->type == GDK_BUTTON_PRESS - && ((GdkEventButton *) event)->button == 1) - { - e->priv->going_to_site = TRUE; - } + le->priv->entry); } -static void -ephy_location_entry_editable_changed_cb (GtkEditable *editable, EphyLocationEntry *e) +void +ephy_location_entry_clear_history (EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = e->priv; - - ephy_location_entry_set_autocompletion_key (e); - - if (p->going_to_site) + EphyNode *node; + + while ((node = ephy_node_get_nth_child (le->priv->combo_root, 0)) != NULL) { - const char *url = ephy_location_entry_get_location (e); - if (url && url[0] != '\0') - { - p->going_to_site = FALSE; - g_signal_emit (e, EphyLocationEntrySignals[ACTIVATED], 0, NULL, url); - } + ephy_node_unref (node); } - if (p->user_changed) - { - g_signal_emit (e, EphyLocationEntrySignals[USER_CHANGED], 0); - } + save_location_history (le); } -static void -ephy_location_entry_set_autocompletion_key (EphyLocationEntry *e) +GtkWidget * +ephy_location_entry_get_entry (EphyLocationEntry *le) { - EphyLocationEntryPrivate *p = e->priv; - if (p->autocompletion && !p->block_set_autocompletion_key) - { - GtkEditable *editable = GTK_EDITABLE (p->entry); - gint sstart, send; - gchar *text; - gtk_editable_get_selection_bounds (editable, &sstart, &send); - text = gtk_editable_get_chars (editable, 0, sstart); - ephy_autocompletion_set_key (p->autocompletion, text); - g_free (p->autocompletion_key); - p->autocompletion_key = text; - } + return le->priv->entry; } -void -ephy_location_entry_clear_history (EphyLocationEntry *w) -{ - gnome_entry_clear_history (GNOME_ENTRY (w->priv->combo)); -} |