/*
 *  Copyright (C) 2000, 2001, 2002 Marco Pesenti Gritti
 *
 *  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.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#define NOT_IMPLEMENTED g_warning ("not implemented: " G_STRLOC);

#include "galeon-tab.h"
#include "galeon-macros.h"
#include "galeon-shell.h"
#include "galeon-popup.h"
#include "galeon-embed-utils.h"
#include "eel-gconf-extensions.h"
#include "prefs-strings.h"
#include "galeon-embed-prefs.h"
#include "galeon-embed-autoscroller.h"
#include "galeon-embed-manual-scroller.h"
#include "gul-download.h"
#include "gul-notebook.h"
#include "gul-gestures.h"
#include "galeon-embed-favicon.h"
#include "galeon-debug.h"

#include <glib/gi18n.h>
#include <libgnomevfs/gnome-vfs-uri.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkmisc.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkiconfactory.h>
#include <gtk/gtkstyle.h>
#include <gtk/gtkselection.h>
#include <gtk/gtkclipboard.h>
#include <string.h>

struct GaleonTabPrivate
{
	GList *notifiers;
	GaleonWindow *window;
	gboolean is_active;
	GtkWidget *tab_icon;
	TabLoadStatus load_status;
	char *link_message;
	char *js_message;
	char status_message[255];
	char *title;
	char *location;
	char *blocked_popup_uri;
	int load_percent;
	gboolean visibility;
	int cur_requests;
	int total_requests;
	int width;
	int height;
	int zoom;
};

static GHashTable *gestures_actions = NULL;

static void
galeon_tab_class_init (GaleonTabClass *klass);
static void
galeon_tab_init (GaleonTab *tab);
static void
galeon_tab_finalize (GObject *object);

static void
galeon_tab_link_message_cb (GaleonEmbed *embed, 
			    const char *message,
			    GaleonTab *tab);
static void
galeon_tab_js_status_cb (GaleonEmbed *embed, 
			 const char *status,
			 GaleonTab *tab);
static void
galeon_tab_location_cb (GaleonEmbed *embed, GaleonTab *tab);
static void
galeon_tab_title_cb (GaleonEmbed *embed, GaleonTab *tab);
static void
galeon_tab_progress_cb (GaleonEmbed *embed, const char *uri,
			gint curprogress, gint maxprogress,
			GaleonTab *tab);
static void
galeon_tab_net_state_cb (GaleonEmbed *embed, const char *uri,
			 EmbedState state, GaleonTab *tab);
static void
galeon_tab_net_stop_cb (GaleonEmbed *embed, GaleonTab *tab);
static void
galeon_tab_net_start_cb (GaleonEmbed *embed, GaleonTab *tab);
static void
galeon_tab_new_window_cb (GaleonEmbed *embed, GaleonEmbed **new_embed,
			  EmbedChromeMask chromemask, GaleonTab *tab);
static void
galeon_tab_visibility_cb (GaleonEmbed *embed, gboolean visibility,
			  GaleonTab *tab);
static void
galeon_tab_size_to_cb (GaleonEmbed *embed, gint width, gint height,
		       GaleonTab *tab);
static gint
galeon_tab_dom_mouse_click_cb (GaleonEmbed *embed,
			       GaleonEmbedEvent *event,
			       GaleonTab *tab);
static gint
galeon_tab_dom_mouse_down_cb (GaleonEmbed *embed,
			      GaleonEmbedEvent *event,
			      GaleonTab *tab);
static void
galeon_tab_contextmenu_cb (GaleonEmbed *embed,
		           GaleonEmbedEvent *event,
			   GaleonTab *tab);
static void
galeon_tab_security_change_cb (GaleonEmbed *embed, EmbedSecurityLevel level,
			       GaleonTab *tab);
static void
galeon_tab_zoom_changed_cb (GaleonEmbed *embed, gint zoom, 
			    GaleonTab *tab);
static void
galeon_tab_destroy_brsr_cb (GaleonEmbed *embed, GaleonTab *tab);

static void
galeon_tab_popup_blocked_cb(GaleonEmbed *embed, const char *uri,
			    GaleonTab *tab);
static void
galeon_tab_size_allocate(GtkWidget *widget, GtkAllocation *allocation);

static void galeon_tab_destroy (GtkObject *object);

static void
galeon_tab_parent_set (GtkWidget *widget, GtkWidget *previous_parent);

static void
galeon_tab_gesture_performed_cb (GulGestures *g, const gchar *sequence, GaleonTab *tab);

static GObjectClass *parent_class = NULL;

/* Class functions */

GType 
galeon_tab_get_type (void)
{
        static GType galeon_tab_type = 0;

        if (galeon_tab_type == 0)
        {
                static const GTypeInfo our_info =
                {
                        sizeof (GaleonTabClass),
                        NULL, /* base_init */
                        NULL, /* base_finalize */
                        (GClassInitFunc) galeon_tab_class_init,
                        NULL,
                        NULL, /* class_data */
                        sizeof (GaleonTab),
                        0, /* n_preallocs */
                        (GInstanceInitFunc) galeon_tab_init
                };

                galeon_tab_type = g_type_register_static (GTK_TYPE_BIN,
							  "GaleonTab",
							  &our_info, 0);
        }

        return galeon_tab_type;
}

static void
galeon_tab_class_init (GaleonTabClass *klass)
{
	GObjectClass *object_class   = G_OBJECT_CLASS (klass);
	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
	GtkObjectClass *gtk_object_class = GTK_OBJECT_CLASS (klass);

	parent_class = g_type_class_peek_parent (klass);

	widget_class->size_allocate = galeon_tab_size_allocate;
	widget_class->parent_set    = galeon_tab_parent_set;
	gtk_object_class->destroy   = galeon_tab_destroy;
	object_class->finalize = galeon_tab_finalize;
}


static void
galeon_tab_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
{
	GtkWidget *child;

	g_return_if_fail(IS_GALEON_TAB (widget));
	
	widget->allocation = *allocation;
	child = GTK_BIN (widget)->child;

	if (child && GTK_WIDGET_VISIBLE (child))
	{
		gtk_widget_size_allocate (child, allocation);
	}
}

static void
galeon_tab_destroy (GtkObject *object)
{
	GaleonEmbed *embed;
	
	embed = galeon_tab_get_embed (GALEON_TAB (object));
	if (embed)
	{
		/* The GaleonTab (and thus the embed) are about to be
		 * destroyed. We therefore don't care about any
		 * signals that the embed might emit. So we block them
		 * all (bug #138529 - see tests/popup.html testcase)
		 *
		 * The one exception is the "new_window" signal,
		 * which we do want to still listen to, otherwise
		 * if a tab tries to launch a popup window when
		 * it closes, we will crash. So block all, then
		 * unblock just that signal
		 */
		LOG ("Blocking all signals (except new_window)");
		g_signal_handlers_block_matched (embed, G_SIGNAL_MATCH_DATA, 
						 0, 0, NULL, NULL, object);

		/* But don't block the new_window callback */
		g_signal_handlers_unblock_matched (embed, G_SIGNAL_MATCH_FUNC,
						   0, 0, NULL, 
						   galeon_tab_new_window_cb, object);

	}
	
	GTK_OBJECT_CLASS (parent_class)->destroy (object);
}

static void
galeon_tab_parent_set (GtkWidget *widget, GtkWidget *previous_parent)
{
	/* FIXME maybe we dont need to set the tab parent explicitly
	 * anymore with this callback taking care of it */
	
	if (widget->parent)
	{
		GtkWidget *toplevel;
		toplevel = gtk_widget_get_toplevel (widget);

		GALEON_TAB (widget)->priv->window = GALEON_WINDOW (toplevel);
	}
	else
	{
		GALEON_TAB (widget)->priv->is_active = FALSE;
	}

	if (GTK_WIDGET_CLASS (parent_class)->parent_set)
		GTK_WIDGET_CLASS (parent_class)->parent_set (widget, previous_parent);
}

static void
galeon_tab_favicon_cb (GaleonEmbed *embed, const char *favicon_url,
		       GaleonTab *tab)
{
	char *url1 = NULL, *url2 = NULL;
	gboolean download;
	gresult result;

	result = galeon_embed_get_location (embed, TRUE, FALSE, &url1);
	if (result != G_OK) return;

	result = galeon_embed_get_location (embed, TRUE, TRUE, &url2);
	if (result != G_OK) goto out;

	download = (url1 != NULL && url2 != NULL && strcmp (url1, url2) == 0);
	if (download)
	{
		GaleonFaviconCache *cache;
		cache = galeon_shell_get_favicon_cache (galeon_shell);
		galeon_favicon_cache_insert_from_url (cache,
				                      url1,
						      favicon_url);
	}

out:
	g_free (url1);
	g_free (url2);
}


static void
tab_icon_set_visibility (GaleonFavicon *favicon)
{
	int mode = eel_gconf_get_integer (CONF_TABS_FAVICON);

	switch (mode)
	{
	case 0:
		gtk_widget_hide (GTK_WIDGET(favicon));
		break;

	case 1:
		gtk_widget_show (GTK_WIDGET(favicon));
		break;

	default:
		if (galeon_favicon_get_is_default (favicon))
		{
			gtk_widget_hide (GTK_WIDGET(favicon));
		}
		else
		{
			gtk_widget_show (GTK_WIDGET(favicon));
		}
		break;
	}
}


static void
tab_icon_changed_cb (GaleonFavicon *favicon, gpointer dummy)
{
	tab_icon_set_visibility (favicon);
}


static void
tab_favicon_mode_gconf_changed_cb (GConfClient *client, guint cnxn_id,
				   GConfEntry *entry, GaleonTab *tab)
{
	tab_icon_set_visibility (GALEON_FAVICON (tab->priv->tab_icon));
}


static void
galeon_tab_init (GaleonTab *tab)
{
	GObject *embed;
	GaleonEmbedShell *shell;
	
	LOG ("Creating tab %p", tab);

        tab->priv = g_new0 (GaleonTabPrivate, 1);

	shell = galeon_shell_get_embed_shell (galeon_shell);
	
	embed = G_OBJECT (galeon_embed_new (G_OBJECT(shell)));

	tab->priv->tab_icon = galeon_embed_favicon_new (GALEON_EMBED (embed));
	g_object_ref (G_OBJECT (tab->priv->tab_icon));
	gtk_object_sink (GTK_OBJECT (tab->priv->tab_icon));

	tab_icon_set_visibility (GALEON_FAVICON (tab->priv->tab_icon));

	galeon_notification_add(CONF_TABS_FAVICON,
				(GConfClientNotifyFunc)tab_favicon_mode_gconf_changed_cb,
				tab, &tab->priv->notifiers);

	g_signal_connect_object (tab->priv->tab_icon, "changed",
				 G_CALLBACK (tab_icon_changed_cb), 
				 tab, 0);

	tab->priv->window = NULL;
	tab->priv->is_active = FALSE;
	tab->priv->link_message = g_strdup ("");
	tab->priv->js_message = g_strdup ("");
	*tab->priv->status_message = '\0';
	tab->priv->load_status = TAB_LOAD_NONE;
	tab->priv->load_percent = 0;
	tab->priv->title = NULL;
	tab->priv->location = NULL;
	tab->priv->total_requests = 0;
	tab->priv->cur_requests = 0;
	tab->priv->width = -1;
	tab->priv->height = -1;
	tab->priv->zoom   = 100;

	gtk_container_add (GTK_CONTAINER (tab), GTK_WIDGET (embed));
	gtk_widget_show (GTK_WIDGET(embed));

	g_signal_connect_object (embed, "ge_link_message",
				 GTK_SIGNAL_FUNC (galeon_tab_link_message_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_js_status",
				 GTK_SIGNAL_FUNC (galeon_tab_js_status_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_location",
				 GTK_SIGNAL_FUNC (galeon_tab_location_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_title",
				 GTK_SIGNAL_FUNC (galeon_tab_title_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_zoom_change",
				 GTK_SIGNAL_FUNC (galeon_tab_zoom_changed_cb),
				 tab, 0);
	g_signal_connect_object (embed, "ge_progress",
				 GTK_SIGNAL_FUNC (galeon_tab_progress_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_net_state",
				 GTK_SIGNAL_FUNC (galeon_tab_net_state_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_net_stop",
				 GTK_SIGNAL_FUNC (galeon_tab_net_stop_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_net_start",
				 GTK_SIGNAL_FUNC (galeon_tab_net_start_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_new_window",
				 GTK_SIGNAL_FUNC (galeon_tab_new_window_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_visibility",
				 GTK_SIGNAL_FUNC (galeon_tab_visibility_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_destroy_brsr",
				 GTK_SIGNAL_FUNC (galeon_tab_destroy_brsr_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_size_to",
				 GTK_SIGNAL_FUNC (galeon_tab_size_to_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_dom_mouse_click",
				 GTK_SIGNAL_FUNC (galeon_tab_dom_mouse_click_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_dom_mouse_down",
				 GTK_SIGNAL_FUNC (galeon_tab_dom_mouse_down_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_contextmenu",
				 GTK_SIGNAL_FUNC (galeon_tab_contextmenu_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_security_change",
				 GTK_SIGNAL_FUNC (galeon_tab_security_change_cb), 
				 tab, 0);
	g_signal_connect_object (embed, "ge_favicon",
				 G_CALLBACK (galeon_tab_favicon_cb),
				 tab, 0);
	g_signal_connect_object (embed, "ge_popupblocked",
				 G_CALLBACK(galeon_tab_popup_blocked_cb),
				 tab, 0);
}

/* Destructor */

static void
galeon_tab_finalize (GObject *object)
{
        GaleonTab *tab = GALEON_TAB (object);

        g_return_if_fail (tab->priv != NULL);

	galeon_notification_remove (&tab->priv->notifiers);

	g_idle_remove_by_data (tab);

	g_object_unref (G_OBJECT(tab->priv->tab_icon));

	g_free (tab->priv->link_message);
	g_free (tab->priv->js_message);
	g_free (tab->priv->blocked_popup_uri);

        g_free (tab->priv->title);
        g_free (tab->priv->location);
        g_free (tab->priv);

	G_OBJECT_CLASS (parent_class)->finalize (object);

	LOG ("GaleonTab finalized %p", tab);
}


static gboolean
address_has_web_scheme (const char *address)
{
	gboolean has_web_scheme;

	if (address == NULL) return FALSE;

	has_web_scheme = (g_str_has_prefix (address, "http:") ||
			  g_str_has_prefix (address, "https:") ||
			  g_str_has_prefix (address, "ftp:") ||
			  g_str_has_prefix (address, "file:") ||
			  g_str_has_prefix (address, "gopher:"));

	return has_web_scheme;
}


/* Public functions */

GaleonTab *
galeon_tab_new ()
{
	GaleonTab *tab;

	tab = GALEON_TAB (g_object_new (GALEON_TAB_TYPE, NULL));

	return tab;
}

static void	      
galeon_tab_set_load_status (GaleonTab *tab,
			    TabLoadStatus status)
{
	if (status == TAB_LOAD_COMPLETED)
	{
		Session *s;
	    	s = galeon_shell_get_session (galeon_shell);
        	session_save (s, SESSION_CRASHED);
	}

	if (galeon_tab_get_is_active (tab) && 
	    status == TAB_LOAD_COMPLETED)
	{
		tab->priv->load_status = TAB_LOAD_NONE;
	}
	else
	{
		tab->priv->load_status = status;
	}
}

GaleonEmbed *
galeon_tab_get_embed (GaleonTab *tab)
{
	GtkWidget* child;

	g_return_val_if_fail (IS_GALEON_TAB (tab), NULL);

	child = gtk_bin_get_child (GTK_BIN (tab));

	return child ? GALEON_EMBED (child) : NULL;
}

void
galeon_tab_set_window (GaleonTab *tab, GaleonWindow *window)
{
	g_return_if_fail (IS_GALEON_TAB (G_OBJECT (tab)));
	if (window) g_return_if_fail (IS_GALEON_WINDOW (G_OBJECT (window)));

	tab->priv->window = window;
}

GaleonWindow *
galeon_tab_get_window (GaleonTab *tab)
{
	g_return_val_if_fail (IS_GALEON_TAB (G_OBJECT (tab)), NULL);

	return tab->priv->window;
}

static void
galeon_tab_update_color (GaleonTab *tab)
{
	galeon_window_set_tab_status (tab->priv->window, tab, 
			              tab->priv->load_status);
}

void
galeon_tab_set_is_active (GaleonTab *tab, gboolean is_active)
{
	TabLoadStatus status;
	
	g_return_if_fail (IS_GALEON_TAB (G_OBJECT (tab)));
	
	tab->priv->is_active = is_active;

       	status = galeon_tab_get_load_status (tab);
	if (status == TAB_LOAD_COMPLETED)
	{
		galeon_tab_set_load_status (tab, TAB_LOAD_NONE);
	}
	galeon_tab_update_color (tab);
}

gboolean
galeon_tab_get_is_active (GaleonTab *tab)
{
	g_return_val_if_fail (IS_GALEON_TAB (G_OBJECT (tab)), FALSE);

	return tab->priv->is_active;
}

gboolean
galeon_tab_get_visibility (GaleonTab *tab)
{
	return tab->priv->visibility;
}

void
galeon_tab_get_size (GaleonTab *tab, int *width, int *height)
{
	*width = tab->priv->width;
	*height = tab->priv->height;
}

GtkWidget *
galeon_tab_get_icon (GaleonTab *tab)
{
	return tab->priv->tab_icon;
}

static void 
galeon_tab_set_visibility (GaleonTab *tab,
                           gboolean visible)
{	
	GaleonWindow *window;

	g_return_if_fail (IS_GALEON_TAB (tab));

	tab->priv->visibility = visible;

	window = galeon_tab_get_window (tab);
	g_return_if_fail (window != NULL);
	
	galeon_window_update_control (window, WindowVisibilityControl);

}

/* Private callbacks for embed signals */

static void
galeon_tab_link_message_cb (GaleonEmbed *embed, 
			    const char *message,
			    GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);

	g_free (tab->priv->link_message);
	tab->priv->link_message = g_strdup (message);
	
	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window,
					      StatusbarMessageControl);
	}
}

static void
galeon_tab_js_status_cb (GaleonEmbed *embed,
			 const char *status,
			 GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);
	
	g_free (tab->priv->js_message);
	tab->priv->js_message = g_strdup (status);
			
	if (tab->priv->is_active 
	    && eel_gconf_get_boolean (CONF_FILTERING_DEFAULT_STATUSBAR))
	{
		galeon_window_update_control (tab->priv->window,
					      StatusbarMessageControl);
	}
}

static void
galeon_tab_location_cb (GaleonEmbed *embed, GaleonTab *tab)
{		
	CHECK_EMBED_TAB_WINDOW(embed, tab, tab->priv->window);

	if (tab->priv->location) g_free(tab->priv->location);
	galeon_embed_get_location(embed, TRUE, FALSE, 
				  &tab->priv->location);

	/* restore zoom level, but only for pages that are from the
	 * Internet, i.e. not javascript, about or data pages */
	if (address_has_web_scheme (tab->priv->location))
	{
		GlobalHistory *history;
		GaleonEmbedShell *ges;
		gint saved_zoom, current_zoom;
		gresult ret;

		ges = galeon_shell_get_embed_shell (galeon_shell);
		history = galeon_embed_shell_get_global_history (ges);

		saved_zoom = global_history_get_host_zoom (history, tab->priv->location);
		ret = galeon_embed_zoom_get (embed, &current_zoom);
		if (ret == G_OK && saved_zoom > 0 && current_zoom != saved_zoom)
		{
			g_signal_handlers_block_by_func (embed, galeon_tab_zoom_changed_cb, tab);
			tab->priv->zoom = saved_zoom;
			galeon_embed_zoom_set (embed, saved_zoom, FALSE);
			g_signal_handlers_unblock_by_func (embed, galeon_tab_zoom_changed_cb, tab);
		}
	}

	if (tab->priv->is_active)
	{
		galeon_window_update_control(tab->priv->window, LocationControl);
		galeon_window_update_control(tab->priv->window, PermissionControl);
	}

	galeon_window_site_visited(tab->priv->window, tab->priv->location, tab->priv->title);
}

static void
galeon_tab_zoom_changed_cb (GaleonEmbed *embed, gint zoom, GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);
	tab->priv->zoom = zoom;

	if (address_has_web_scheme (tab->priv->location))
	{
		GlobalHistory *history;
		GaleonEmbedShell *ges;	

		ges = galeon_shell_get_embed_shell (galeon_shell);
		history = galeon_embed_shell_get_global_history (ges);
		global_history_set_host_zoom (history, tab->priv->location, zoom);
	} 
}

static void
galeon_tab_title_cb (GaleonEmbed *embed, GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);

	if (tab->priv->title) g_free (tab->priv->title);
	galeon_embed_get_title (embed, &tab->priv->title);
	
	if (*(tab->priv->title) == '\0')
	{
		g_free (tab->priv->title);
		if (!strcmp (tab->priv->location, "about:blank"))
		{
			tab->priv->title = g_strdup(_("Untitled"));
		}
		else
		{
			tab->priv->title = g_strdup (tab->priv->location);
		}
	}
	
	galeon_window_site_visited (tab->priv->window, tab->priv->location, tab->priv->title);

	galeon_window_set_tab_title (tab->priv->window, 
			             tab, tab->priv->title);
}

static int
build_load_percent (int bytes_loaded, int max_bytes_loaded)
{
	if (max_bytes_loaded > 0)
	{
		return (bytes_loaded * 100) / max_bytes_loaded;
	}
	else
	{
		return -1;
	}
}

static char *
get_host_name_from_uri (const char *uri)
{
	GnomeVFSURI *vfs_uri = NULL;
	const char *host = NULL;
	char *result;
	
	if (uri)
	{
		vfs_uri = gnome_vfs_uri_new (uri);
	}
	
	if (vfs_uri)
	{
		host = gnome_vfs_uri_get_host_name (vfs_uri);
	}
	
	if (!host)
	{
		host = _("site");
	}

	result = g_strdup (host);
	
	if (vfs_uri) gnome_vfs_uri_unref (vfs_uri);
	
	return result;
}

static void
build_progress_message (char *message,
			const char *uri,
			int bytes_loaded, 
			int max_bytes_loaded, 
			int load_percent)
{
	char *host;

	host = get_host_name_from_uri (uri);
	
	if (max_bytes_loaded == 0 || 
	    bytes_loaded > max_bytes_loaded ) 
	{
		g_snprintf (message, 255, 
			    _("Transferring data from %s (%d kB loaded)"), 
                            host, bytes_loaded / 1024);	
	}
	else
	{
		g_snprintf (message, 255, 
                    	    _("Transferring data from %s (%d%% complete, %d kB of %d kB loaded)"),
                    	    host, load_percent, bytes_loaded / 1024, max_bytes_loaded / 1024);
	}

	g_free (host);
}

static void
galeon_tab_progress_cb (GaleonEmbed *embed, const char *uri,
			gint curprogress, gint maxprogress,
			GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);

	build_progress_message (tab->priv->status_message,
				uri,
				curprogress,
				maxprogress,
				tab->priv->load_percent);
	
	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window,
					      StatusbarMessageControl);
	}
}

static void
build_net_state_message (char *message,
			 const char *uri,
			 EmbedState flags)
{
	const char *msg = NULL;	
	char *host;

	host = get_host_name_from_uri (uri);
	
	/* IS_REQUEST and IS_NETWORK can be both set */
	
	if (flags & EMBED_STATE_IS_REQUEST)
        {
                if (flags & EMBED_STATE_REDIRECTING)
                {
                	msg = _("Redirecting to %s...");
                }
                else if (flags & EMBED_STATE_TRANSFERRING)
                {
                    	msg = _("Transferring data from %s...");
                }
                else if (flags & EMBED_STATE_NEGOTIATING)
                {
                   	msg = _("Waiting for authorization from %s...");
                }
        }
	
	if (flags & EMBED_STATE_IS_NETWORK)
        {
                if (flags & EMBED_STATE_START)
                {
                        msg = _("Loading %s...");
                }
                else if (flags & EMBED_STATE_STOP)
                {
                      	msg = " ";
                }
        }

	if (msg)
	{
		g_snprintf (message, 255, msg, host);
	}

	g_free (host);
}

static void
build_progress_from_requests (GaleonTab *tab, EmbedState state)
{
	int load_percent;
	
	if (state & EMBED_STATE_IS_REQUEST)
        {
                if (state & EMBED_STATE_START)
                {
			tab->priv->total_requests ++;
		}
		else if (state & EMBED_STATE_STOP)
		{
			tab->priv->cur_requests ++;
		}
		
		load_percent = build_load_percent (tab->priv->cur_requests,
						   tab->priv->total_requests);
		if (load_percent > tab->priv->load_percent)
		{
			tab->priv->load_percent = load_percent;

			galeon_window_update_control (tab->priv->window,
						      StatusbarProgressControl);
		}
	}
}

static void
galeon_tab_net_start_cb (GaleonEmbed *embed, GaleonTab *tab)
{
	tab->priv->total_requests = 0;
	tab->priv->cur_requests   = 0;
	tab->priv->load_percent   = 0;

	galeon_tab_set_load_status (tab, TAB_LOAD_STARTED);
	galeon_tab_update_color (tab);
	
	if (tab->priv->blocked_popup_uri)
	{
		g_free(tab->priv->blocked_popup_uri);
		tab->priv->blocked_popup_uri = NULL;
	}

	galeon_window_update_control (tab->priv->window, SpinnerControl);

	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window, StatusbarPopupBlockControl);
	}
}


static void
galeon_tab_net_state_cb (GaleonEmbed *embed, const char *uri,
			 EmbedState state, GaleonTab *tab)
{	
	build_net_state_message (tab->priv->status_message, uri, state);
	build_progress_from_requests (tab, state);
	
	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window,
					      StatusbarMessageControl);

	}
}

static void
galeon_tab_net_stop_cb (GaleonEmbed *embed, GaleonTab *tab)
{
	tab->priv->load_percent = 0;
			
	galeon_tab_set_load_status (tab, TAB_LOAD_COMPLETED);
			
	galeon_window_update_control (tab->priv->window, SpinnerControl);
	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window, StatusbarProgressControl);
	}
	galeon_tab_update_color (tab);
}

static void
galeon_tab_new_window_cb (GaleonEmbed *embed, GaleonEmbed **new_embed,
			  EmbedChromeMask chromemask, GaleonTab *tab)
{
	GaleonTab *new_tab;
	GaleonWindow *window;
	gboolean open_in_tab;

	open_in_tab = (chromemask & EMBED_CHROME_OPENASCHROME) ?
		      FALSE :
		      eel_gconf_get_boolean (CONF_TABS_TABBED_POPUPS);

	if (open_in_tab)
	{
		window = galeon_tab_get_window (tab);
	}
	else
	{
		window = galeon_window_new ();
		galeon_window_set_chrome (window, chromemask);
	}
	
	new_tab = galeon_tab_new ();
	gtk_widget_show (GTK_WIDGET (new_tab));
        galeon_window_add_tab (window, new_tab,
			       GALEON_WINDOW_ADD_TAB_GROUPED, FALSE);
	
	*new_embed = galeon_tab_get_embed (new_tab);
}

static void
galeon_tab_visibility_cb (GaleonEmbed *embed, gboolean visibility,
			  GaleonTab *tab)
{
	if (visibility)
	{
		gtk_widget_show (GTK_WIDGET (tab));
	}
	else
	{
		gtk_widget_hide (GTK_WIDGET (tab));
	}
        
	galeon_tab_set_visibility (tab, visibility);
}


static gboolean
galeon_tab_destroy_brsr_idle_cb (GaleonTab *tab)
{
	GulNotebook *nb;
	g_return_val_if_fail (IS_GALEON_TAB (tab), FALSE);
	
	LOG ("galeon_tab_destroy_brsr_idle_cb");

	/* Don't use galeon_window_remove_tab here, we don't
	 * want to run the "unsubmitted" changes code */
	nb = galeon_window_get_notebook (tab->priv->window);
	gul_notebook_remove_page (GUL_NOTEBOOK (nb), GTK_WIDGET (tab));
	return FALSE;
}


static void
galeon_tab_destroy_brsr_cb (GaleonEmbed *embed, GaleonTab *tab)
{
	/* This is a dirty to hack to work around mozillas problems
	 * with closing windows via javascript that dont have focus.
	 * 
	 * The problem is that if you have a tab loaded in the
	 * background in the notebook, then under some circumstances
	 * mozilla won't emit the ge_destroy_brsr signal until it gets
	 * focus by someone clicking on the tab label.  At that point,
	 * the signal is emitted, we close the tab, remove the label,
	 * and then the notebook crashes because widget for the label
	 * has been destroyed.
	 *
	 * See bug #116256
	 *
	 * So lets work around the problem by adding an g_idle function
	 * that destroys the tab when we know nothing is referencing it
	 * in the stack.
	 */
	LOG ("galeon_tab_destroy_brsr_cb");

	g_idle_add_full (G_PRIORITY_HIGH_IDLE, 
			 (GSourceFunc)galeon_tab_destroy_brsr_idle_cb, tab, 0);
}

static gboolean
let_me_resize_hack (gpointer data)
{
	g_return_val_if_fail (IS_GALEON_TAB (data), FALSE);

	gtk_widget_set_size_request (GTK_WIDGET(data), -1, -1);
	return FALSE;
}

static void
galeon_tab_size_to_cb (GaleonEmbed *embed, gint width, gint height,
		       GaleonTab *tab)
{
	GList *tabs;
	GaleonWindow *window;
	EmbedChromeMask chromemask;

	tab->priv->width = width;
	tab->priv->height = height;
	
	window = galeon_tab_get_window (tab);
	tabs = (GList *) galeon_window_get_tabs (window);
	chromemask = galeon_window_get_chrome (window);
	
	/* Do not resize window with multiple tabs.
	 * Do not resize window already showed because
	 * it's not possible to calculate a sensible window
	 * size based on the embed size */
	if (g_list_length (tabs) == 1 && !tab->priv->visibility)
	{
		gtk_widget_set_size_request 
			(GTK_WIDGET (tab), width, height);

		/* HACK reset widget requisition after the container
		 * has been resized. It appears to be the only way
		 * to have the window sized according to embed 
		 * size correctly.
		 * We dont do it for XUL dialogs because in that case
		 * a "forced" requisition appear correct.
		 */
		if (!(chromemask & EMBED_CHROME_OPENASCHROME))
		{
			g_idle_add (let_me_resize_hack, tab);
		}
	}
}

static void
galeon_tab_show_embed_popup (GaleonTab *tab, GaleonEmbedEvent *event)
{
	GaleonPopup *popup;
	GaleonWindow *window;
	GaleonEmbed *embed;

	window = galeon_tab_get_window (tab);
	embed = galeon_tab_get_embed (tab);

	popup = GALEON_POPUP (galeon_window_get_popup_factory (window));
	galeon_popup_set_event (popup, event);
	galeon_popup_show (popup, embed);
}

static gint
galeon_tab_dom_mouse_down_cb  (GaleonEmbed *embed,
			       GaleonEmbedEvent *event,
			       GaleonTab *tab)
{
	GaleonWindow *window;
	int button;
	EmbedEventContext context;
        
	g_assert (IS_GALEON_EMBED_EVENT(event));

	window = galeon_tab_get_window (tab);
	g_return_val_if_fail (window != NULL, FALSE);

	galeon_embed_event_get_mouse_button (event, &button);
	galeon_embed_event_get_context (event, &context); 

	if (button == 2)
	{
		gint action = eel_gconf_get_integer (CONF_MOUSE_RIGHT_BUTTON_ACTION);
		switch (action)
		{
		case 0: /* display context menu immediately */
			galeon_tab_show_embed_popup (tab, event);
			break;
		case 1: /* perform gestures, show context menu on mouse up */
		{ 
			GulGestures *g = gul_gestures_new ();
			guint x, y;
			g_object_set_data_full(G_OBJECT(g), "embed_event", 
					       g_object_ref (event), 
					       g_object_unref);
			
			galeon_embed_event_get_coords (event, &x, &y);
			gul_gestures_start (g, GTK_WIDGET (window), button, x, y);
			
			g_signal_connect (g, "gesture-performed", 
					  G_CALLBACK (galeon_tab_gesture_performed_cb), tab);

			g_object_unref (g);
		}
		break;
		default:
			g_warning ("Unexpected pref value for " CONF_MOUSE_RIGHT_BUTTON_ACTION "\n");
			break;
		}
	}
	else if (button == 1
		&& !(context & EMBED_CONTEXT_LINK
		     || context & EMBED_CONTEXT_EMAIL_LINK
		     || context & EMBED_CONTEXT_INPUT))
	{
		gint action = eel_gconf_get_integer (CONF_MOUSE_MIDDLE_BUTTON_ACTION);
		switch (action) 
		{
		case 0: /* bookmarks context menu */
			g_warning ("Bookmarks context menu is not implemented yet\n");
			break;
		case 1: /* paste url: handled by click_cb */
		case 6: /* paste url in current tab: handled by click_cb */
			break;
		case 2: /* go back */
			galeon_embed_go_back (embed);
			break;
		case 3: /* gestures */
		{
			GulGestures *g = gul_gestures_new ();
			guint x, y;
			galeon_embed_event_get_coords (event, &x, &y);
			gul_gestures_start (g, GTK_WIDGET (window), button, x, y);
			
			g_signal_connect (g, "gesture-performed", 
					  G_CALLBACK (galeon_tab_gesture_performed_cb), tab);
			
			g_object_unref (g);
		}
		break;
		case 4: /* auto scroll */
		{
			GaleonEmbedAutoscroller *as = galeon_embed_autoscroller_new ();
			guint x, y;
			galeon_embed_event_get_coords (event, &x, &y);
			galeon_embed_autoscroller_set_embed (as, embed);
			galeon_embed_autoscroller_start_scroll (as, GTK_WIDGET (window), x, y);
			g_object_unref (as);
		}
		break;
		case 5: /* manual scroll */
		{
			GaleonEmbedManualScroller *ms = galeon_embed_manual_scroller_new ();
			guint x, y;
			galeon_embed_event_get_coords (event, &x, &y);
			galeon_embed_manual_scroller_set_embed (ms, embed);
			galeon_embed_manual_scroller_start_scroll (ms, GTK_WIDGET (window), x, y);
			g_object_unref (ms);
		}
		break;
		default:
			g_warning ("Unexpected pref value for " CONF_MOUSE_MIDDLE_BUTTON_ACTION "\n");
			break;
		}
	}
	
	return FALSE;
}

/**
 *  Callback function from the paste of text.
 */
static void
galeon_tab_clipboard_text_received_cb (GtkClipboard *clipboard, const gchar *text,
				       gpointer data)
{
	gpointer *weak_ptr = (gpointer*)data;
	GaleonTab *tab = NULL;
	gint action;

	if (*weak_ptr != NULL)
	{
		tab = GALEON_TAB (*weak_ptr);
		g_object_remove_weak_pointer (G_OBJECT (tab), weak_ptr);
	}
	g_free (data);

	if (tab == NULL || text == NULL || text[0] == '\0')
	{
		return;
	}

	action = eel_gconf_get_integer(CONF_MOUSE_MIDDLE_BUTTON_ACTION);
	
	if (action == 6) /* Paste into current tab */
	{
		galeon_embed_load_url (galeon_tab_get_embed (tab), text);
	}
	else /* Paste into new tab (action == 1 or default) */
	{
		GaleonWindow * window = galeon_tab_get_window (tab);

		galeon_shell_new_tab(galeon_shell, window, tab,
 				     text, 0);
	}
}

static gint
galeon_tab_dom_mouse_click_cb(GaleonEmbed *embed,
			      GaleonEmbedEvent *event,
			      GaleonTab *tab)
{
	GaleonWindow *window;
	guint button, modifier;
	EmbedEventContext context;
        
	g_assert(IS_GALEON_EMBED_EVENT(event));

	window = galeon_tab_get_window(tab);
	g_return_val_if_fail(window != NULL, FALSE);

	galeon_embed_event_get_mouse_button(event, &button);
	galeon_embed_event_get_context(event, &context);
	galeon_embed_event_get_modifier(event, &modifier);

	if (button == 0 && (context & EMBED_CONTEXT_LINK) &&
	    !(context & EMBED_CONTEXT_INPUT) &&
	    (modifier & GDK_SHIFT_MASK))
	{
		galeon_embed_utils_download_event_property (embed, event, 
							    FALSE, NULL, "link");
	}
	else if (button == 0 && (context & EMBED_CONTEXT_IMAGE) &&
		 !(context & EMBED_CONTEXT_INPUT) &&
		 (modifier & GDK_SHIFT_MASK))
	{
		galeon_embed_utils_save_event_property(embed, event, 
						       TRUE,  /* always_ask_dir */
						       FALSE, /* show_progress */
						       _("Save Image As..."),
						       "image");
	}
	else if ((button == 0 && (context & EMBED_CONTEXT_LINK) &&
		  !(context & EMBED_CONTEXT_INPUT) &&
		  (modifier & GDK_CONTROL_MASK)) ||
		 (button == 1 && (context & EMBED_CONTEXT_LINK)))
	{
		GaleonNewTabFlags flags = 0;
		const GValue *value;
		const gchar *link;

		if (button == 1)
		{
			flags = galeon_shell_modifier_flags (modifier);
		}
		galeon_embed_event_get_property(event, "link", &value);
		link = g_value_get_string (value);

		if (galeon_embed_event_has_property(event, "link-can-open-in-new-tab"))
		{
			galeon_shell_new_tab(galeon_shell, window, tab, 
					     link, flags);
		}
		else
		{
			galeon_embed_load_url (galeon_tab_get_embed (tab), link);
		}
	}
	else if (button == 1 &&
		 (eel_gconf_get_integer(CONF_MOUSE_MIDDLE_BUTTON_ACTION) == 1 ||
		  eel_gconf_get_integer(CONF_MOUSE_MIDDLE_BUTTON_ACTION) == 6) &&
		 !(context & EMBED_CONTEXT_LINK ||
		   context & EMBED_CONTEXT_EMAIL_LINK ||
		   context & EMBED_CONTEXT_INPUT))
	{
		/* Handle UTF8 characters as well as normal variants, see bug 133633 */

		/* Ensure that if the tab is destroyed while we are requesting text,
		 * we don't dereference  a bogus tab pointer */
		gpointer * weak_ptr = g_new (gpointer, 1);
		*weak_ptr = tab;
		g_object_add_weak_pointer (G_OBJECT (tab), weak_ptr);

		gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (embed),
								      GDK_SELECTION_PRIMARY),
					    galeon_tab_clipboard_text_received_cb, weak_ptr);
	}
	return FALSE;
}

static void
galeon_tab_contextmenu_cb (GaleonEmbed *embed,
		           GaleonEmbedEvent *event,
			   GaleonTab *tab)
{
	galeon_tab_show_embed_popup (tab, event);
}

static void
galeon_tab_security_change_cb (GaleonEmbed *embed, EmbedSecurityLevel level,
			       GaleonTab *tab)
{
	CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);
	
	if (tab->priv->is_active)
	{
		galeon_window_update_control (tab->priv->window,
					      StatusbarSecurityControl);
	}
}

static void
galeon_tab_popup_blocked_cb(GaleonEmbed *embed, const char *uri,  
                            GaleonTab *tab)
{
        CHECK_EMBED_TAB_WINDOW (embed, tab, tab->priv->window);

	if (tab->priv->blocked_popup_uri)
		g_free(tab->priv->blocked_popup_uri);

	tab->priv->blocked_popup_uri = uri ? g_strdup(uri) : NULL;

        if (tab->priv->is_active)
        {
                galeon_window_update_control(tab->priv->window,
                                             StatusbarPopupBlockControl);
        }
}

TabLoadStatus 
galeon_tab_get_load_status (GaleonTab *tab)
{
	return tab->priv->load_status;	
}

int
galeon_tab_get_load_percent (GaleonTab *tab)
{
	return tab->priv->load_percent;
}

const char *
galeon_tab_get_status_message (GaleonTab *tab)
{
	/* The statusbar message is the first one of the following:
	 * 1) the URL if hovering the mouse over a link
	 * 2) the javascript message if overwriting the statusbar is allowed
	 * 3) normal status message (Loading..., Done, etc.)
	 *
	 * Note: CONF_FILTERING_DEFAULT_STATUSBAR is basically obsolete since
	 * we don't let people 'fake' URLs by rewriting the statusbar.
	 */

	if (*tab->priv->link_message)
	{
		return tab->priv->link_message;
	}
	else if (*tab->priv->js_message
		 && eel_gconf_get_boolean (CONF_FILTERING_DEFAULT_STATUSBAR))
	{
		return tab->priv->js_message;
	}
	return tab->priv->status_message;
}

const char *
galeon_tab_get_title (GaleonTab *tab)
{
	if (tab->priv->title && 
	    g_utf8_strlen(tab->priv->title, -1))
	{
		return tab->priv->title;
	}
	else
	{
		return _("Untitled");
	}
}

const char *
galeon_tab_get_location (GaleonTab *tab)
{
	return tab->priv->location;
}

const char *
galeon_tab_get_blocked_popup_uri(GaleonTab *tab)
{
	return tab->priv->blocked_popup_uri;
}

int
galeon_tab_get_zoom(GaleonTab *tab)
{
	return tab->priv->zoom;
}

void
galeon_tab_clear_blocked_popup_uri(GaleonTab *tab)
{
	if (tab->priv->blocked_popup_uri)
	{
		g_free(tab->priv->blocked_popup_uri);
		tab->priv->blocked_popup_uri = NULL;
	}
}

void 
galeon_tab_set_location (GaleonTab *tab,
			 char *location)
{
	if (tab->priv->location) g_free (tab->priv->location);
	tab->priv->location = g_strdup (location);
}

static void
galeon_tab_update_gestures (void)
{
	/* this should read a pref, parse it, setup a listener, etc */
	/* for now, just hardcode some */

	int i;
	static const struct 
	{
		gchar *sequence;
		gchar *action;
	} default_gestures[] = {
                /* menu */
                { "5"           , "menu"          },

		/* down */
                { "258"         , "new_tab"       },
		
                /* up */
                { "852"         , "new_window"    },
		
                /* up-down */
                { "85258"       , "reload"        },
                { "8525"        , "reload"        },
                { "5258"        , "reload"        },

                /* up-down-up */
                { "8525852"     , "reload_bypass" },
                { "852585"      , "reload_bypass" },
                { "85252"       , "reload_bypass" },
                { "85852"       , "reload_bypass" },
                { "525852"      , "reload_bypass" },
                { "52585"       , "reload_bypass" },

                /* up-left-down */
                { "9632147"     , "homepage"      },
                { "963214"      , "homepage"      },
                { "632147"      , "homepage"      },
                { "962147"      , "homepage"      },
                { "963147"      , "homepage"      },

                /* down-up */
                { "25852"       , "clone_window"  },
                { "2585"        , "clone_window"  },
                { "5852"        , "clone_window"  },

                /* down-up-down */
                { "2585258"     , "clone_tab"     },
                { "258525"      , "clone_tab"     },
                { "25858"       , "clone_tab"     },
                { "25258"       , "clone_tab"     },
                { "585258"      , "clone_tab"     },
                { "58525"       , "clone_tab"     },

                /* up-left-up */
                { "96541"       , "up"            },
                { "9651"        , "up"            },
                { "9541"        , "up"            },

                /* right-left-right */
                { "4565456"     , "close"         },
                { "456545"      , "close"         },
                { "45656"       , "close"         },
                { "45456"       , "close"         },
                { "565456"      , "close"         },
                { "56545"       , "close"         },

                /* down-right */
                { "14789"       , "close"         },
                { "1489"        , "close"         },

                /* left */
                { "654"         , "back"          },

                /* right */
                { "456"         , "forward"       },

                /* down-left */
                { "36987"       , "fullscreen"    },
                { "3687"        , "fullscreen"    },

                /* up-right */
                { "74123"       , "next_tab"      },
                { "7423"        , "next_tab"      },

                /* up-left */
                { "96321"       , "prev_tab"      },
                { "9621"        , "prev_tab"      },

                /* s */
                { "321456987"   , "view_source"   },
                { "21456987"    , "view_source"   },
                { "32145698"    , "view_source"   },
                { "3256987"     , "view_source"   },
                { "3214587"     , "view_source"   },
                { "32145987"    , "view_source"   },
                { "32156987"    , "view_source"   },

                /* left-up */
                { "98741"       , "stop"          },
                { "9841"        , "stop"          },

                /* left-down */
                { "32147"       , "prev_link"     },
                { "3247"        , "prev_link"     },
		
                /* right-down */
                { "12369"       , "next_link"     },
                { "1269"        , "next_link"     },

                /* up-right-up */
                { "74563"       , "contents_link" },
                { "7453"        , "contents_link" },
                { "7563"        , "contents_link" },

		{ NULL		, NULL 		  }

	};
	
	if (gestures_actions)
	{
		g_hash_table_destroy (gestures_actions);
	}

	gestures_actions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);

	for (i = 0; default_gestures[i].sequence; ++i)
	{
		g_hash_table_insert (gestures_actions, 
				     g_strdup (default_gestures[i].sequence), 
				     g_strdup (default_gestures[i].action));
	}
}

static void
galeon_tab_gesture_performed_cb (GulGestures *g, const gchar *sequence, GaleonTab *tab)
{
	const gchar  *action;
	GaleonWindow *window;
	GaleonEmbed  *embed;
	
	LOG ("gesture: %s", sequence);
	
	if (!gestures_actions)
	{
		galeon_tab_update_gestures ();
	}
	
	action = g_hash_table_lookup (gestures_actions, sequence);
	
	if (!action) 
	{
		return;
	}
	
	LOG ("action: %s", action);
	window = galeon_tab_get_window (tab);
	embed = galeon_tab_get_embed (tab);
	
	if (!strcmp (action, "menu"))
	{
		GaleonEmbedEvent *event = g_object_get_data(G_OBJECT(g), "embed_event");
		if (event != NULL)
		{
			galeon_tab_show_embed_popup (tab, event);
		}
	}
	else if (!strcmp (action, "new_tab"))
	{
		galeon_shell_new_tab (galeon_shell, window, tab, NULL, 
				      GALEON_NEW_TAB_HOMEPAGE |
				      GALEON_NEW_TAB_IN_EXISTING_WINDOW |
				      GALEON_NEW_TAB_JUMP);
	}
	else if (!strcmp (action, "new_window"))
	{
		galeon_shell_new_tab (galeon_shell, window, tab, NULL,
				      GALEON_NEW_TAB_HOMEPAGE |
				      GALEON_NEW_TAB_IN_NEW_WINDOW |
				      GALEON_NEW_TAB_JUMP);
	}
	else if (!strcmp (action, "reload"))
	{
		galeon_embed_reload (embed, EMBED_RELOAD_NORMAL);
	}
	else if (!strcmp (action, "reload_bypass"))
	{
		galeon_embed_reload (embed, EMBED_RELOAD_FORCE);
	}
	else if (!strcmp (action, "homepage"))
	{
		char *homepage;

		homepage = eel_gconf_get_string (CONF_GENERAL_HOMEPAGE);
		g_return_if_fail (homepage != NULL);

		galeon_embed_load_url (embed, homepage);

		g_free (homepage);
	}
	else if (!strcmp (action, "clone_window"))
	{
		NOT_IMPLEMENTED;
	}
	else if (!strcmp (action, "clone_tab"))
	{
		galeon_shell_new_tab (galeon_shell, window, tab, NULL,
				      GALEON_NEW_TAB_IN_EXISTING_WINDOW |
				      GALEON_NEW_TAB_JUMP |
				      GALEON_NEW_TAB_IS_A_COPY);
	}
	else if (!strcmp (action, "up"))
	{
		galeon_embed_go_up (embed);
	}
	else if (!strcmp (action, "close"))
	{
		galeon_window_remove_tab (window, tab);
	}
	else if (!strcmp (action, "back"))
	{
		galeon_embed_go_back (embed);
	}
	else if (!strcmp (action, "forward"))
	{
		galeon_embed_go_forward (embed);
	}
	else if (!strcmp (action, "fullscreen"))
	{
		galeon_window_toggle_fullscreen (window);
	}
	else if (!strcmp (action, "next_tab"))
	{
		galeon_window_next_tab (window);
	}
	else if (!strcmp (action, "prev_tab"))
	{
		galeon_window_prev_tab (window);
	}
	else if (!strcmp (action, "view_source"))
	{
		galeon_shell_new_tab (galeon_shell, window, tab, NULL,
				      GALEON_NEW_TAB_IN_EXISTING_WINDOW |
				      GALEON_NEW_TAB_JUMP |
				      GALEON_NEW_TAB_VIEW_SOURCE);
	}
	else if (!strcmp (action, "stop"))
	{
		galeon_embed_stop_load (embed);
	}
	else if (!strcmp (action, "prev_link"))
	{
		NOT_IMPLEMENTED;
	}
	else if (!strcmp (action, "next_link"))
	{
		NOT_IMPLEMENTED;
	}
	else if (!strcmp (action, "contents_link"))
	{
		NOT_IMPLEMENTED;
	}
	else
	{
		/* unrecognized */
	}
}
