aboutsummaryrefslogtreecommitdiffstats
path: root/lib/widgets/ephy-autocompletion-window.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/widgets/ephy-autocompletion-window.c')
-rw-r--r--lib/widgets/ephy-autocompletion-window.c854
1 files changed, 854 insertions, 0 deletions
diff --git a/lib/widgets/ephy-autocompletion-window.c b/lib/widgets/ephy-autocompletion-window.c
new file mode 100644
index 000000000..9c639a52a
--- /dev/null
+++ b/lib/widgets/ephy-autocompletion-window.c
@@ -0,0 +1,854 @@
+/*
+ * 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.
+ */
+
+#include <libgnome/gnome-i18n.h>
+#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-gobject-misc.h"
+#include "ephy-string.h"
+#include "ephy-marshal.h"
+#include "ephy-gui.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
+
+//#define DEBUG_MSG(x) g_print x
+#define DEBUG_MSG(x)
+
+//#define DEBUG_TIME
+
+#ifdef DEBUG_TIME
+#include <glib/gtimer.h>
+#endif
+
+/**
+ * Private data
+ */
+struct _EphyAutocompletionWindowPrivate {
+ EphyAutocompletion *autocompletion;
+ GtkWidget *parent;
+
+ GtkWidget *window;
+ GtkScrolledWindow *scrolled_window;
+ GtkTreeView *tree_view;
+ GtkTreeViewColumn *col1;
+ GtkTreeView *action_tree_view;
+ GtkTreeViewColumn *action_col1;
+ GtkTreeView *active_tree_view;
+ gboolean only_actions;
+
+ 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,
+ EPHY_AUTOCOMPLETION_WINDOW_HIDDEN,
+ EPHY_AUTOCOMPLETION_WINDOW_LAST_SIGNAL
+};
+static gint EphyAutocompletionWindowSignals[EPHY_AUTOCOMPLETION_WINDOW_LAST_SIGNAL];
+
+/**
+ * AutocompletionWindow object
+ */
+
+MAKE_GET_TYPE (ephy_autocompletion_window, "EphyAutocompletionWindow", EphyAutocompletionWindow,
+ ephy_autocompletion_window_class_init,
+ ephy_autocompletion_window_init, G_TYPE_OBJECT);
+
+static void
+ephy_autocompletion_window_class_init (EphyAutocompletionWindowClass *klass)
+{
+ G_OBJECT_CLASS (klass)->finalize = ephy_autocompletion_window_finalize_impl;
+ g_object_class = g_type_class_peek_parent (klass);
+
+ 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[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);
+}
+
+static void
+ephy_autocompletion_window_init (EphyAutocompletionWindow *aw)
+{
+ EphyAutocompletionWindowPrivate *p = g_new0 (EphyAutocompletionWindowPrivate, 1);
+ 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);
+ g_free (p);
+
+ gdk_pointer_ungrab (GDK_CURRENT_TIME);
+ gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+
+ G_OBJECT_CLASS (g_object_class)->finalize (o);
+}
+
+static void
+ephy_autocompletion_window_init_widgets (EphyAutocompletionWindow *aw)
+{
+ EphyAutocompletionWindowPrivate *p = aw->priv;
+ GtkWidget *sw;
+ GtkCellRenderer *renderer;
+ GtkWidget *frame;
+ GtkWidget *vbox;
+ GdkColor *bg_color;
+ guint32 base, dark;
+ GValue v = { 0 };
+
+ 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));
+
+ 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 ();
+
+ 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);
+ base = ephy_gui_gdk_color_to_rgb (bg_color);
+ dark = ephy_gui_rgb_shift_color (base, 0.15);
+ *bg_color = ephy_gui_gdk_rgb_to_color (dark);
+ g_object_set_property (G_OBJECT (renderer), "cell_background_gdk", &v);
+
+ 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);
+ DEBUG_MSG (("Real list requisition %d, Items %d\n", list_requisition.height, aw->priv->view_nitems));
+ list_requisition.height = MIN (row_height * MAX_VISIBLE_ROWS, list_requisition.height);
+ DEBUG_MSG (("Row Height %d, Fake list requisition %d\n",
+ 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;
+#ifdef DEBUG_TIME
+ GTimer *timer;
+#endif
+ DEBUG_MSG (("ACW: Filling the list from %d\n", last));
+
+#ifdef DEBUG_TIME
+ timer = g_timer_new ();
+ g_timer_start (timer);
+#endif
+
+ 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;
+
+#ifdef DEBUG_TIME
+ DEBUG_MSG (("ACW: %f elapsed filling the gtkliststore\n", g_timer_elapsed (timer, NULL)));
+ g_timer_destroy (timer);
+#endif
+}
+
+void
+ephy_autocompletion_window_show (EphyAutocompletionWindow *aw)
+{
+ EphyAutocompletionWindowPrivate *p = aw->priv;
+ gint x, y, height, width;
+ guint nmatches;
+#ifdef DEBUG_TIME
+ GTimer *timer1;
+ GTimer *timer2;
+#endif
+
+ 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;
+ }
+
+#ifdef DEBUG_TIME
+ DEBUG_MSG (("ACW: showing window.\n"));
+ timer1 = g_timer_new ();
+ g_timer_start (timer1);
+#endif
+
+#ifdef DEBUG_TIME
+ timer2 = g_timer_new ();
+ g_timer_start (timer2);
+#endif
+
+ ephy_autocompletion_window_fill_store_chunk (aw);
+
+ p->only_actions = (!GTK_WIDGET_VISIBLE (p->scrolled_window) ||
+ GTK_WIDGET_HAS_FOCUS (p->action_tree_view));
+ if (p->only_actions)
+ {
+ p->active_tree_view = p->action_tree_view;
+ }
+ else
+ {
+ p->active_tree_view = p->tree_view;
+ }
+
+#ifdef DEBUG_TIME
+ DEBUG_MSG (("ACW: %f elapsed creating liststore\n", g_timer_elapsed (timer2, NULL)));
+#endif
+
+ 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));
+
+#ifdef DEBUG_TIME
+ g_timer_start (timer2);
+#endif
+
+ ephy_autocompletion_window_get_dimensions (aw, &x, &y, &width, &height);
+
+#ifdef DEBUG_TIME
+ DEBUG_MSG (("ACW: %f elapsed calculating dimensions\n", g_timer_elapsed (timer2, NULL)));
+ g_timer_destroy (timer2);
+#endif
+
+ 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));
+#ifdef DEBUG_TIME
+ DEBUG_MSG (("ACW: %f elapsed showing window\n", g_timer_elapsed (timer1, NULL)));
+ g_timer_destroy (timer1);
+#endif
+}
+
+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 GtkTreeView *
+hack_tree_view_move_selection (GtkTreeView *tv, GtkTreeView *alternate, int dir)
+{
+ GtkTreeSelection *ts = gtk_tree_view_get_selection (tv);
+ GtkTreeModel *model;
+ GList *selected = NULL;
+ selected = gtk_tree_selection_get_selected_rows (ts, &model);
+ gboolean prev_result = TRUE;
+
+ gtk_tree_selection_unselect_all (ts);
+
+ if (!selected)
+ {
+ GtkTreePath *p = gtk_tree_path_new_first ();
+ gtk_tree_selection_select_path (ts, p);
+ gtk_tree_view_scroll_to_cell (tv, p, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (p);
+ }
+ else
+ {
+ GtkTreePath *p = selected->data;
+ int i;
+ if (dir > 0)
+ {
+ for (i = 0; i < dir; ++i)
+ {
+ gtk_tree_path_next (p);
+ }
+ }
+ else
+ {
+ for (i = 0; i > dir; --i)
+ {
+ prev_result = gtk_tree_path_prev (p);
+ }
+ }
+
+ if (prev_result)
+ {
+ gtk_tree_selection_select_path (ts, p);
+ gtk_tree_view_scroll_to_cell (tv, p, NULL, FALSE, 0, 0);
+ }
+ }
+
+ g_list_foreach (selected, (GFunc) gtk_tree_path_free, NULL);
+ g_list_free (selected);
+
+ if (!prev_result)
+ {
+ GtkTreeModel *model;
+ int c;
+ GtkTreeIter iter;
+ GtkTreePath *p;
+ GtkTreeSelection *selection;
+
+ model = gtk_tree_view_get_model (alternate);
+ c = gtk_tree_model_iter_n_children (model, NULL);
+ gtk_tree_model_iter_nth_child (model, &iter, NULL, c - 1);
+ p = gtk_tree_model_get_path (model, &iter);
+ selection = gtk_tree_view_get_selection (alternate);
+ gtk_tree_selection_select_path (selection, p);
+ gtk_tree_view_scroll_to_cell (alternate, p, NULL, FALSE, 0, 0);
+ gtk_tree_path_free (p);
+ return alternate;
+ }
+ else if (gtk_tree_selection_count_selected_rows (ts) == 0)
+ {
+ hack_tree_view_move_selection (alternate, tv, dir);
+ return alternate;
+ }
+
+ return tv;
+}
+
+static gboolean
+ephy_autocompletion_window_key_press_hack (EphyAutocompletionWindow *aw,
+ guint keyval)
+{
+ GtkTreeView *tree_view, *alt;
+ EphyAutocompletionWindowPrivate *p = aw->priv;
+ gboolean action;
+
+ action = (p->active_tree_view == p->action_tree_view);
+ tree_view = action ? p->action_tree_view : p->tree_view;
+ alt = action ? p->tree_view : p->action_tree_view;
+ alt = p->only_actions ? p->action_tree_view : alt;
+
+ switch (keyval)
+ {
+ case GDK_Up:
+ p->active_tree_view = hack_tree_view_move_selection
+ (tree_view, alt, -1);
+ break;
+ case GDK_Down:
+ p->active_tree_view = hack_tree_view_move_selection
+ (tree_view, alt, +1);
+ break;
+ case GDK_Page_Down:
+ p->active_tree_view = hack_tree_view_move_selection
+ (tree_view, alt, +5);
+ break;
+ case GDK_Page_Up:
+ p->active_tree_view = hack_tree_view_move_selection
+ (tree_view, alt, -5);
+ break;
+ case GDK_Return:
+ case GDK_space:
+ if (aw->priv->selected)
+ {
+ g_signal_emit (aw, EphyAutocompletionWindowSignals
+ [ACTIVATED], 0, aw->priv->selected, action);
+ }
+ break;
+ default:
+ g_warning ("Unexpected keyval");
+ break;
+ }
+ return TRUE;
+}
+
+static gboolean
+ephy_autocompletion_window_key_press_cb (GtkWidget *widget,
+ GdkEventKey *event,
+ EphyAutocompletionWindow *aw)
+{
+ GdkEventKey tmp_event;
+ EphyAutocompletionWindowPrivate *p = aw->priv;
+ GtkWidget *dest_widget;
+
+ /* allow keyboard navigation in the alternatives clist */
+ 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);
+ }
+ else
+ {
+ dest_widget = p->parent;
+ }
+
+ if (dest_widget != widget)
+ {
+ //DEBUG_MSG (("Resending event\n"));
+
+ tmp_event = *event;
+ gtk_widget_event (dest_widget, (GdkEvent *)&tmp_event);
+
+ return TRUE;
+ }
+ else
+ {
+ if (widget == GTK_WIDGET (p->tree_view))
+ {
+ //DEBUG_MSG (("on the tree view "));
+ }
+ //DEBUG_MSG (("Ignoring event\n"));
+ return FALSE;
+ }
+
+}
+
+void
+ephy_autocompletion_window_hide (EphyAutocompletionWindow *aw)
+{
+ if (aw->priv->window)
+ {
+ 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);
+ }
+ 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);
+ }
+ }
+}