/*
 *  Copyright (C) 2000 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.
 */

#include "galeon.h"
#include "mozcallbacks.h"
#include "mozilla_prefs.h"
#include "embed.h"
#include "misc.h"
#include "window.h"
#include "history.h"
#include "spinner.h"
#include "session.h"
#include "mozilla.h"
#include "context.h"
#include "prefs.h"
#include "downloader.h"
#include "menubar.h"

#include <time.h>
#include <string.h>
#include <libgnomeui/gnome-app.h>
#include <libgnomeui/gnome-app-helper.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-triggers.h>
#include <libgnome/gnome-i18n.h>
#include <libgnome/gnome-util.h>
#include <gdk/gdkkeysyms.h>

/* Flag to enable/disable mouse motion listener */
static gboolean mouse_listener_active = FALSE;

typedef struct
{
	gint original_x, original_y;
	GaleonEmbed *embed;
	WrapperMouseEventInfo *info;
} MouseInfo;

/* local function prototyprs */
static gint mouse_listener (MouseInfo *minfo);

/**
 * mozembed_new_window_cb: GTKMOZEMBED SIGNAL, emitted any time a new 
 * window is requested by the document 
 */
void 
mozembed_new_window_cb (GtkMozEmbed *dummy, GtkMozEmbed **retval, 
			guint chrome_mask, GaleonEmbed *embed) 
{
	GaleonEmbed *new_embed;
	GaleonWindow *window;
	GaleonWindow *new_window;
	gboolean open_in_tab;

	/* check callback args */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);
	g_assert (retval != NULL);

	/* check config */
	open_in_tab = eel_gconf_get_boolean (CONF_TABS_TABBED_POPUPS);

	/* override if it's some sort of XUL (must be some sort of dialog) */
	if ((chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME) != 0)
	{
		open_in_tab = FALSE;
	}

	/* create a new browser */
	new_embed = embed_create (embed, !open_in_tab, FALSE, 
				  TRUE, chrome_mask);

	if ((chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME) != 0)
	{
		new_window = new_embed->parent_window;
		return_if_not_window (new_window);
		gtk_window_set_transient_for (GTK_WINDOW(new_window->WMain),
					      GTK_WINDOW(window->WMain));
	}

	/* set the popup flag */
	if (new_embed->parent_window != embed->parent_window)
	{
		new_embed->parent_window->is_popup = TRUE;
	}

	/* return the new browser to gtkmozembed */
	*retval = GTK_MOZ_EMBED (new_embed->mozEmbed);
}

void new_window_orphan_cb (GtkMozEmbedSingle *embed,
			   GtkMozEmbed **retval, guint chrome_mask,
			   gpointer data)
{
	GaleonEmbed *new_embed;

	/* create a new browser */
	new_embed = embed_create (NULL, FALSE, FALSE, TRUE,
				  chrome_mask);

	/* set the popup flag */
	new_embed->parent_window->is_popup = TRUE;

	/* return the new browser to gtkmozembed */
	*retval = GTK_MOZ_EMBED (new_embed->mozEmbed);
}

/**
 * mozembed_visibility_cb: GTKMOZEMBED SIGNAL, emitted when the toplevel
 * window need to be showed or hidden
 */
void 
mozembed_visibility_cb (GtkMozEmbed *dummy, gboolean visibility, 
			GaleonEmbed *embed) 
{
        GaleonWindow *window;

	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* set visiblity of this embed */
	embed_set_visibility (embed, visibility);

	if (visibility && 
	    window->is_popup &&
	    !window->layered)
	{
	        window_set_layer (window);
	}

	/* do embed init if necessary */
	if (visibility && !embed->wrapper)
	{
		embed_wrapper_init (embed);
	}
}

/**
 * mozembed_destroy_brsr_cb: GTKMOZEMBED SIGNAL, emitted when the document
 * has requested that the toplevel window be closed
 */
void 
mozembed_destroy_brsr_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	return_if_not_embed (embed);

	/* close the GaleonEmbed */
	embed_close (embed);
}

/**
 * mozembed_location_changed_cb: GTKMOZEMBED SIGNAL, emitted any time that
 * the location of the document has changed
 */
void
mozembed_location_changed_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	GaleonWindow *window;

 	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* autosave the updated session */
	session_autosave ();
	
	/* get the new location */
	if (embed->site_location != NULL)
	{
		g_free (embed->site_location);
	}
	embed->site_location = gtk_moz_embed_get_location (
			GTK_MOZ_EMBED (embed->mozEmbed));

	/* update in gui */
	embed_update_page_location (embed);

	/* update the buttons view */
	if (embed->is_active)
	{
		window_update_nav_controls (window);
	}
}

/**
 * mozembed_title_changed_cb: GTKMOZEMBED SIGNAL, emitted any time that the 
 * title of the document has changed
 */
void
mozembed_title_changed_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	return_if_not_embed (embed);

	/* free existing titles */
	g_free (embed->site_title);
	g_free (embed->site_title_utf8);
	embed->site_title = NULL;
	embed->site_title_utf8 = NULL;

	/* get both versions of new title */
	embed->site_title = 
		mozilla_get_document_title (embed, &(embed->site_title_utf8));
	
	/* reset to untitled if this title is nonsense */
	if (embed->site_title == NULL || strlen (embed->site_title) == 0)
	{
		g_free (embed->site_title);
		g_free (embed->site_title_utf8);
		embed->site_title = g_strdup (_("Untitled"));
		embed->site_title_utf8 = mozilla_locale_to_utf8 (embed->site_title);
	}

	/* update in gui */
	embed_update_page_title (embed);
}

/**
 * mozembed_load_started_cb: GTKMOZEMBED SIGNAL, emitted any time that the 
 * load of a document has been started
 */
void
mozembed_load_started_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	GaleonWindow *window;

	/* check callback args */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	embed->load_started++;

	/* if we haven't already, start the window spinner, set the
	 * load_started tag, and clear the info about the progress */
	if (embed->load_started == 1)
	{
		embed_update_tab_status (embed);
		embed_progress_clear (embed);
		embed->when_started = time (NULL);

		/* start spinner if this is the active embed */
		if (embed->is_active)
			spinner_start (window);
	}

	/* update the buttons view */
	if (embed->is_active)
	{
		window_update_nav_controls (window);
		window_update_statusbar (window);
		if (window->csslist != NULL)
			gtk_widget_set_sensitive (window->csslist, FALSE);
	}

	/* no user sheet has been applied yet */
	embed->usercss_applied = FALSE;
}

/**
 * mozembed_load_finished_cb: GTKMOZEMBED SIGNAL, emitted any time that 
 * the load of a document has finished
 */
void
mozembed_load_finished_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	GaleonWindow *window;
	gint zoom;
	gchar *realurl;
	
	/* check callback args */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* set in the embed */
	embed->load_started--;
	embed_progress_clear (embed);

	if (embed->load_started == 0)
	{
		gint index;
		AlternateStyleSheet *a = NULL;
	
		/* check zoom setting for the new site, unless it has 
		   a javascript URI */
		if (eel_gconf_get_boolean (CONF_HISTORY_AUTOZOOM) &&
		    embed->site_location 
		    && strncmp ("javascript:", embed->site_location, 11))
		{
			zoom = history_get_zoom (embed->site_location);
			if (zoom == 0 && embed->zoom_auto_set)
			{
				embed_set_zoom (embed, 100);
			}
			else if (zoom != 0 && zoom != embed->zoom)
			{
				embed_set_zoom (embed, zoom);
				embed->zoom_auto_set = TRUE;
			}
		}

		/* stop spinner if this is the active embed */
		if (embed->is_active)
			spinner_stop (window);

		/* new content, so only viewed if this is already active */
		embed->has_been_viewed = embed->is_active;

		/* update tab title and status */
		embed_update_tab_title (embed);
		embed_update_tab_status (embed);
		
		/* load saved alternate stylesheet */
		index = history_get_selected_authorcss (embed->site_location);
		if (index != -1)
		{
			GList *csslist =
				mozilla_get_alternate_stylesheets (embed);
			GList *l;
			gint i;
			gboolean found = FALSE;

			for (i = 1, l = csslist; l; l = g_list_next (l), i++)
			{
				a = (AlternateStyleSheet *) l->data;
				if (index == i)
				{
					found = TRUE;
					break;
				}
				g_free (a->name);
				g_free (a);
			}

			if (index == 0 && g_list_length (csslist) > 0)
			{
				a = g_new0 (AlternateStyleSheet, 1);
				a->name = g_strdup ("");
				a->sheet = NULL;
				found = TRUE;
			}
			
			g_list_free (csslist);

			if (found)
			{
				mozilla_set_alternate_stylesheet (embed, a);
				g_free (a->name);
				g_free (a);
			}
		}

		/* load user stylesheet */
		if (embed->usercss_applied == FALSE)
		{
			gchar *usersheet;

			usersheet = eel_gconf_get_string
					(CONF_RENDERING_USER_STYLESHEET);

			if (usersheet && strcmp (usersheet, "") != 0)
			{
				if (g_file_exists (usersheet))
				{
					embed->usersheet =
						mozilla_set_user_sheet
							(embed, usersheet);
				}
				g_free (usersheet);
			}

			embed->usercss_applied = TRUE;
		}
	}

	/* update the buttons view */
	if (embed->is_active)
	{
		window_update_nav_controls (window);
		window_update_statusbar (window);
		embed_init_css_menu (embed);
	}

	/* hack to workaround a bunch of problems when no document is loaded */
	realurl = gtk_object_get_data
		(GTK_OBJECT (embed->mozEmbed), "real_url");
	if (realurl != NULL)
	{
		embed_load_url (embed, realurl);
		gtk_object_remove_data
			(GTK_OBJECT (embed->mozEmbed), "real_url");
		g_free (realurl);
	}
}

/**
 * mozembed_net_status_change_cb: GTKMOZEMBED SIGNAL, emitted any time that
 * there is a change in the status of network loading
 */
void mozembed_net_status_change_cb (GtkMozEmbed *dummy, gint flags, 
				    guint status, GaleonEmbed *embed) 
{
	GaleonWindow *window;
/*	float f;*/

	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* sync the zoom levels, this shouldn't be necessary but mozilla
	 * ignore the zoom level setting until it's loaded its first doc */
	/* FIXME is this really really necessary ? I dont like the idea to
	   force a zoom level much, also I this is called too many times
	   here ! */
	/*
	if (embed->wrapper != NULL)
	{
		mozilla_get_zoom(embed, &f);
		if ((gint)(f * 100.0) != embed->zoom)
		{
			mozilla_set_zoom (embed, (float)embed->zoom / 100.0);
		}
	}*/

	/* clear temporary message, if any */
	if (embed->is_active)
	{
		window_update_temp_message (window, NULL);
	}

	if (flags & GTK_MOZ_EMBED_FLAG_IS_REQUEST)
	{
		if (flags & GTK_MOZ_EMBED_FLAG_START)
		{
			/* ignore */
		}
		else if (flags & GTK_MOZ_EMBED_FLAG_REDIRECTING)
		{
			embed->statusMessage = _("Redirecting to site...");
		}
		else if (flags & GTK_MOZ_EMBED_FLAG_TRANSFERRING)
		{
			embed->statusMessage = 
				_("Transferring data from site...");
		}
		else if (flags & GTK_MOZ_EMBED_FLAG_NEGOTIATING)
		{
			embed->statusMessage = 
				_("Waiting for authorization...");
		}
		else if (flags & GTK_MOZ_EMBED_FLAG_STOP)
		{
			/* ignore */
		}
	}

	if (flags & GTK_MOZ_EMBED_FLAG_IS_DOCUMENT)
	{
		if (flags & GTK_MOZ_EMBED_FLAG_START)
		{
			embed->statusMessage = _("Loading site...");
		}
		else if (flags & GTK_MOZ_EMBED_FLAG_STOP)
		{
			embed->statusMessage = _("Done.");
		}
	}

	switch (status)
	{
	case GTK_MOZ_EMBED_STATUS_FAILED_DNS:
		embed->statusMessage = _("Site not found.");
		break;
		
	case GTK_MOZ_EMBED_STATUS_FAILED_CONNECT:
		embed->statusMessage = _("Failed to connect to site.");
		break;

	case GTK_MOZ_EMBED_STATUS_FAILED_TIMEOUT:
		embed->statusMessage = _("Failed due to connection timeout.");
		break;

	case 0:
	case GTK_MOZ_EMBED_STATUS_FAILED_USERCANCELED:
		/* don't do a message for this */
		break;

	default:
/* FIXME: find out what these are, but for now it's best not to
 * alarm users with lots of warning messages -- MattA
		g_warning ("unhandled status message 0x%08x\n", status);
 */
		break;
	}

	/* display the update */
	window_update_statusbar (window);
}

/**
 * mozembed_progress_change_cb: GTKMOZEMBED SIGNAL, emitted any time that 
 * there is a change in the progress of loading a document.
 */
void mozembed_progress_change_cb (GtkMozEmbed *dummy, gint cur, gint max,
				  GaleonEmbed *embed)
{
	GaleonWindow *window;

	/* check callback args */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* compute percentages */
	if (max == -1)
	{
		embed->loadPercent = 0;
		embed->bytesLoaded = cur;
		embed->maxBytesLoaded = 0;
	}
	else if (cur > max)
	{
		/* sometimes length of the downloaded document is 
		 * greater than the length of the document */
		embed->loadPercent = 100;
		embed->bytesLoaded = cur;
		embed->maxBytesLoaded = max;
	} 
	else
	{
		/* normal conditions */
		embed->bytesLoaded = cur;
		embed->maxBytesLoaded = max;
		embed->loadPercent = (!max) ? 0 : (cur * 100) / max;
	}

	/* update view */
	if (embed->is_active)
	{
		window_update_statusbar (window);
	}
}

/**
 * mozembed_link_message_cb: GTKMOZEMBED SIGNAL, emitted when the 
 * link message changes
 */
void
mozembed_link_message_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	char *message;
	GaleonWindow *window;

	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);
	
	/* get the link message */
	message = mozilla_get_link_message (embed, NULL);

	/* update browser message */
	window_update_temp_message (window, message);
	if (message)
	{
		g_free (message);
	}
}

/**
 * mozembed_js_status_cb: GTKMOZEMBED SIGNAL, emitted when the Javascript 
 * message status changes
 */
void
mozembed_js_status_cb (GtkMozEmbed *dummy, GaleonEmbed *embed)
{
	char *message;
	GaleonWindow *window;

	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

 	if (eel_gconf_get_boolean (CONF_FILTERING_STATUSBAR_REWRITE))
  	{
		/* get the javascript message */
		if (embed->is_active)
		{
			message = mozilla_get_js_status (embed, NULL);
			
			/* update the status bar message */
			if (message != NULL)
			{
				window_update_temp_message (window, message);
				g_free (message);
			}
		}
	}
}

/**
 * mozembed_dom_mouse_down_cb: emitted on a button press event
 */
gint
mozembed_dom_mouse_down_cb (GtkMozEmbed *dummy, gpointer dom_event, 
			    GaleonEmbed *embed)
{
	WrapperMouseEventInfo *info;
	gint button_action = eel_gconf_get_integer
				(CONF_MOUSE_RMB_ACTION);

	return_val_if_not_embed (embed, FALSE);

	info = g_new0 (WrapperMouseEventInfo, 1);
	if (!mozilla_get_mouse_event_info (embed, dom_event, info)) 
	{
		mozilla_free_context_info_sub(&info->ctx);
		g_free(info);
		return FALSE;
	}

	if (info->button == 2)
	{
		if (button_action == 1)
		{
			MouseInfo *minfo;
			GdkWindow *win = embed->parent_window->WMain->window;

			if (mouse_listener_active)
				return FALSE;

			minfo = g_new0 (MouseInfo, 1);
			gdk_window_get_pointer (win, &minfo->original_x,
						&minfo->original_y, NULL);
			minfo->embed = embed;
			minfo->info = info;

			/* start the mouse listener timeout */
			gtk_timeout_add (10, (GtkFunction) mouse_listener,
					 minfo);
			mouse_listener_active = TRUE;

			return FALSE;
		}
		else
			context_show_menu (embed, &info->ctx, 3,
					   info->timestamp);
	}

	mozilla_free_context_info_sub(&info->ctx);
	g_free (info);

	return FALSE;
}

static guint
mozembed_modifier_to_gdk_state (guint modifier)
{
	guint state = 0;
	if (modifier & CTRL_KEY)
		state |= GDK_CONTROL_MASK;
	if (modifier & SHIFT_KEY)
		state |= GDK_SHIFT_MASK;
	if (modifier & ALT_KEY)
		state |= GDK_MOD1_MASK;
	return state;
}

/**
 * mozembed_dom_mouse_click_cb: GTKMOZEMBED SIGNAL, emitted when user 
 * clicks on the document
 */
gint 
mozembed_dom_mouse_click_cb (GtkMozEmbed *dummy, gpointer dom_event, 
			     GaleonEmbed *embed)
{
	GaleonWindow *window;
	WrapperMouseEventInfo *info;
	gboolean handled = FALSE;

	return_val_if_not_embed (embed, FALSE);
	window = embed->parent_window;
	return_val_if_not_window (window, FALSE);

	info = g_new0 (WrapperMouseEventInfo,1);
	if (!mozilla_get_mouse_event_info (embed, dom_event, info)) 
	{
		mozilla_free_context_info_sub(&info->ctx);
		g_free(info);
		return FALSE;
	}

	/* make a sound if a link was clicked */
	if ((info->ctx.context & CONTEXT_LINK) && 
	    (info->ctx.link && info->button != 3))
	{
		gnome_triggers_do ("", "program", "galeon", 
				   "url_clicked", NULL);
	}

	switch (info->button)
	{
	case 2:
		if (eel_gconf_get_integer(CONF_MOUSE_RMB_ACTION)
		    == 1)
		{
			/* kill the motion listener, if it exists */
			if (mouse_listener_active)
			{
				mouse_listener_active = FALSE;
			}
			gtk_moz_embed_go_back (GTK_MOZ_EMBED(embed->mozEmbed));
		}
		else
			context_show_menu (embed, &info->ctx, -1, 0);

		handled = TRUE;
		break;

	case 1:
		if (!(info->ctx.context & CONTEXT_LINK))
		{
			gint mmb_action;

			mmb_action = eel_gconf_get_integer (
				CONF_MOUSE_MMB_ACTION);

			switch (mmb_action)
			{
				/* show the bookmarks menu */
				case 0:
					context_show_bookmark_menu (embed);
					break;
				/* load the url from the clipboard */
				case 1:
					gtk_selection_convert(window->WMain,
						GDK_SELECTION_PRIMARY,
						GDK_SELECTION_TYPE_STRING,
						GDK_CURRENT_TIME);
					break;
				/* do nothing */
				default:
					break;
			}

			handled = TRUE;
		}
		break;

	case 0: 
		if ((info->ctx.context & CONTEXT_IMAGE) &&
		    (info->modifier & CTRL_KEY))
		{
			embed_save_image (embed, info->ctx.img, FALSE);
			handled = TRUE;
		}
		break;

	/* perform back/forward for extra buttons */
	case 5:
		gtk_moz_embed_go_back (GTK_MOZ_EMBED (embed->mozEmbed));
		handled = TRUE;
		break;

	case 6:
		gtk_moz_embed_go_forward (GTK_MOZ_EMBED (embed->mozEmbed));
		handled = TRUE;
		break;

	default:
		break;
	}

	/* if not handled already handle with generic click handler (only if it
	 * is not a non modified left click, since we need to let moz handle
	 * that for frames to work properly ) */
	if (!handled && (info->ctx.context & CONTEXT_LINK) && 
		!(info->button==0
		 && mozembed_modifier_to_gdk_state (info->modifier)==0))
	{
		LinkState link_state;

		link_state = mouse_state_to_link_state 
			(info->button + 1, 
			 mozembed_modifier_to_gdk_state (info->modifier));

		handled = embed_activate_link (embed, NULL, info->ctx.link,
					       link_state);
	}
	mozilla_free_context_info_sub(&info->ctx);
	g_free (info);

	return handled;
}

/**
 * mozembed_dom_key_press_cb: GTKMOZEMBED SIGNAL, emitted when a key is 
 * pressed
 */
gint mozembed_dom_key_press_cb (GtkMozEmbed *dummy, gpointer dom_event, 
				GaleonEmbed *embed)
{
	GaleonWindow *window;
	int state = 0;
	gboolean handled = FALSE;
	WrapperKeyEventInfo *info;

	return_val_if_not_embed (embed, FALSE);
	window = embed->parent_window;
	return_val_if_not_window (window, FALSE);

	info = g_new0 (WrapperKeyEventInfo,1);
	if (!mozilla_get_key_event_info(embed, dom_event, info))
	{
		mozilla_free_context_info_sub(&info->ctx);
		g_free(info);
		return FALSE;
	}

	state = mozembed_modifier_to_gdk_state (info->modifier);

	/* g_print("key = %ld  modifier = %d\n",info->key, info->modifier); */

	/* Keypresses that are not handled here are now passed on to the main
	   window's acceltable, so you should *not* handle any functions here
	   that have an accelerator key in the menubar. --Josh */

	if ((info->ctx.context & CONTEXT_LINK) && info->key == DOM_VK_RETURN)
	{
		handled = embed_activate_link_keyboard (embed, NULL, 
							info->ctx.link, state);
	}
	else if (info->modifier == KEY_CODE)
	{
		switch(info->key)
		{
		case DOM_VK_BACK_SPACE:
			/* FIXME disabled because at some point of the load
			   the embed get the focus, so if users try to correct
			   the url they wrote it goes back ... very confusing,
			   we got a lot of bug reports */
/*			if ((info->ctx.context & CONTEXT_INPUT) == 0)
			{
				gtk_moz_embed_go_back
					(GTK_MOZ_EMBED (embed->mozEmbed));
				handled = TRUE;
				}*/
			break;
		default:
			break;
		}
	}
	else if ((info->modifier & ALT_KEY) && (info->modifier & CTRL_KEY))
	{
		switch(info->key)
		{
		default:
			break;
		}
	}
	else if ((info->modifier & KEY_CODE) && (info->modifier & SHIFT_KEY))
	{
		switch(info->key)
		{
		case DOM_VK_F10:
			context_show_menu (embed, &info->ctx, -1, 0);
			handled = TRUE;
			break;
		default:
			break;
		}
	}
	else if ((info->modifier & KEY_CODE) && ((info->modifier & CTRL_KEY) ||
						 (info->modifier & ALT_KEY)))
	{
		switch(info->key)
		{
		case DOM_VK_KP_LEFT:
		case DOM_VK_LEFT:
			gtk_moz_embed_go_back(GTK_MOZ_EMBED(embed->mozEmbed));
			handled = TRUE;
			break;
		case DOM_VK_KP_RIGHT:
		case DOM_VK_RIGHT:
			gtk_moz_embed_go_forward(GTK_MOZ_EMBED(embed->mozEmbed));
			handled = TRUE;
			break;
		default:
			break;
		}
	}
	else if ((info->modifier & ALT_KEY) && 
		 !(info->modifier & (CTRL_KEY | SHIFT_KEY)))
	{
		switch(info->key)
		{
		case DOM_VK_1:
		case DOM_VK_2:
		case DOM_VK_3:
		case DOM_VK_4:
		case DOM_VK_5:
		case DOM_VK_6:
		case DOM_VK_7:
		case DOM_VK_8:
		case DOM_VK_9:
			gtk_notebook_set_page (GTK_NOTEBOOK (window->notebook),
					       info->key - DOM_VK_1);
			handled = TRUE;
			break;
		case DOM_VK_0:
			gtk_notebook_set_page (GTK_NOTEBOOK (window->notebook),
					       9);
			handled = TRUE;
			break;
		default:
			break;

		}
	}
	else if (info->modifier & CTRL_KEY)
	{		
		switch(info->key)
		{
		case DOM_VK_g:
		case DOM_VK_G:
		case DOM_VK_l:
		case DOM_VK_L:
			if (window->location_entry != NULL)
			{
				gtk_editable_select_region
					(GTK_EDITABLE(window->location_entry),
					 0, -1);
				gtk_window_set_focus 
					(GTK_WINDOW(window->WMain),
					 window->location_entry);
			}
			handled = TRUE;
			break;
		default:
			break;

		}
	}

	if (info->modifier & KEY_CODE)
	{
		/* The extended keys have different keycodes in Mozilla/GDK,
		   so we need to translate them here */
		/* FIXME cleanup */
		switch(info->key)
		{
		case DOM_VK_ESCAPE:
			info->key = GDK_Escape;
			break;
		case DOM_VK_PAGE_UP:
			info->key = GDK_Page_Up;
			break;
		case DOM_VK_PAGE_DOWN:
			info->key = GDK_Page_Down;
			break;
		case DOM_VK_HOME:
			info->key = GDK_Home;
			break;
		case DOM_VK_END:
			info->key = GDK_End;
			break;
		case DOM_VK_INSERT:
			info->key = GDK_Insert;
			break;
		case DOM_VK_F12:
			info->key = GDK_F12;
			break;
		case DOM_VK_F11:
			info->key = GDK_F11;
			break;
		case DOM_VK_F10:
			info->key = GDK_F10;
			break;
		case DOM_VK_F9:
			info->key = GDK_F9;
			break;
		case DOM_VK_F8:
			info->key = GDK_F8;
			break;
		case DOM_VK_F7:
			info->key = GDK_F7;
			break;
		case DOM_VK_F6:
			info->key = GDK_F6;
			break;
		case DOM_VK_F5:
			info->key = GDK_F5;
			break;
		case DOM_VK_F4:
			info->key = GDK_F4;
			break;
		case DOM_VK_F3:
			info->key = GDK_F3;
			break;
		case DOM_VK_F2:
			info->key = GDK_F2;
			break;
		case DOM_VK_F1:
			info->key = GDK_F1;
			break;
		default:
			break;
		}
	}

	/* We haven't specifically handled the keypress, so send it to the
	   main window in case it is a menu accelerator key */
	if (!handled)
	{
		handled = gtk_accel_groups_activate 
			(GTK_OBJECT (window->WMain), info->key, state);
	}
	
	mozilla_free_context_info_sub(&info->ctx);
	g_free (info);
				
	/* If it wasn't an accelerator, let Mozilla handle it */
	return handled;
}

/**
 * mozembed_size_to_cb: GTKMOZEMBED SIGNAL, emitted when a  size change 
 * is requested
 */
void 
mozembed_size_to_cb (GtkMozEmbed *dummy, gint width, gint height,
		     GaleonEmbed *embed)
{
	GaleonWindow *window;

	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* when window hasn't been shown... */
	if (window->visible_embeds == 0)
	{
		/* resize the embed */
		gtk_widget_set_usize (GTK_WIDGET (embed->mozEmbed), 
				      width, height);
		
		/* don't override this by changing the main window size! */
		window->set_size = TRUE;
	}
	else
	{
		/* forcibly resize the window */
		gtk_widget_set_usize (GTK_WIDGET (window->WMain), 
				      width, height);
	}
}

void mozembed_security_change_cb (GtkMozEmbed *dummy, gpointer request,
				  guint state, GaleonEmbed *embed)
{	
	GaleonWindow *window;
	gboolean security_state = (state!=0);

	/* get parent window */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	if (embed->secure != security_state)
	{
		embed->secure = security_state;
		window_statusbar_set_security_icon 
			(window, security_state);
	}
}


/**
 * mozembed_destroy_cb: gtkmozembed component destroying
 */
void
mozembed_destroy_cb (GtkObject *object, GaleonEmbed *embed)
{
	GaleonWindow *window;
	gint n_embeds;

	/* get parent window */
	return_if_not_embed (embed);
	window = embed->parent_window;
	return_if_not_window (window);

	/* no longer the active embed */
	if (embed->is_active)
	{
		embed->is_active = FALSE;
		window->active_embed = NULL;
	}

	/* free memory */
	if (embed->site_location != NULL)
	{
		g_free (embed->site_location);
		embed->site_location = NULL;
	}
	if (embed->site_title != NULL)
	{
		g_free (embed->site_title);
		g_free (embed->site_title_utf8);
		embed->site_title = NULL;
		embed->site_title_utf8 = NULL;
	}

	/* no longer visible */
	if (embed->is_visible)
	{
		window->visible_embeds--;
	}

	/* destroy C++ wrapper */
	if (embed->wrapper)
	{
		mozilla_wrapper_destroy (embed);
	}

	/* remove from list of embeds */
	all_embeds = g_list_remove (all_embeds, embed);

	/* from from list of embeds in parent */
	window->embed_list = g_list_remove (window->embed_list, embed);

	/* show tabs if more than one embed or if the user has requested it */
	n_embeds = g_list_length (window->embed_list);

	/* if that's the last embed in parent window, destroy it */
	if (n_embeds == 0)
	{
		window_close (window);
	}

	/* autosave the updated session */
	session_autosave ();

	/* scrub and free the GaleonEmbed structure */
	memset (embed, 0, sizeof(GaleonEmbed));
	g_free (embed);
	
	/* start the quit timeout */
	if (all_embeds == NULL)
		session_server_start_timeout ();
		
}

/** 
 * mozembed_drag_drop_cb:
 */
gboolean
mozembed_drag_drop_cb (GtkWidget * widget, GdkDragContext *context, 
		       gint x, gint y, GtkSelectionData *selection_data, 
		       guint info, guint time)
{
	g_warning ("unexpected mozembed_drag_drop_cb\n");
	return FALSE;
}

/**
 * mouse_listener: Internal function. Used to poll the mouse position to
 *                 determine if motion has occurred.
 **/
static gint
mouse_listener (MouseInfo *minfo)
{
	GdkWindow *window = minfo->embed->parent_window->WMain->window;
	gint x, y;

	if (!mouse_listener_active)
	{
		mozilla_free_context_info_sub(&minfo->info->ctx);
		g_free (minfo->info);
		g_free (minfo);

		return FALSE;
	}

	gdk_window_get_pointer (window, &x, &y, NULL);

	if (ABS(x - minfo->original_x) > 1 || ABS(y - minfo->original_y) > 1)
	{
		context_show_menu (minfo->embed, &minfo->info->ctx, 3,
				   minfo->info->timestamp);
		mouse_listener_active = FALSE;
	}

	return TRUE;
}
