/*
 *  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);
		}
	}
}
