/*
 *  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.
 */

/* Galeon includes */
#include "galeon.h"
#include "embed.h"
#include "window.h"
#include "misc.h"
#include "history.h"
#include "prefs.h"
#include "mozilla_prefs.h"
#include "mozilla.h"
#include "mozcallbacks.h"
#include "bookmarks.h"
#include "downloader.h"
#include "MozRegisterComponents.h"
#include "filepicker.h"
#include "dialog.h"
#include "mozilla_notifiers.h"
#include "themes.h"
#include "tabbutton.h"
#include "session.h"

#include <stdlib.h>
#include <string.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-util.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gnome-dialog-util.h>
#include <libgnomeui/gnome-preferences.h>
#include <libgnomevfs/gnome-vfs.h>
#include <libgnomevfs/gnome-vfs-mime.h>

/* local function prototypes */
static GaleonEmbed *embed_create_no_window (void);
static void embed_connect_signals (GaleonEmbed *embed);
static void embed_single_init (void);
static void embed_view_source_external (GaleonEmbed *embed, const gchar *url);
static void add_bf_menu (GtkWidget *menu, char* label, int index, 
			 GaleonEmbed *embed, int level);
static GtkWidget * new_num_accel_menu_item (gint num, gchar *origtext, 
					    gboolean lettersok, 
					    GtkWidget *menu);
static void save_url_message (GaleonEmbed *embed, char *path,
			      gboolean result);
static void embed_select_new_page (GaleonEmbed *embed);
static gboolean save_with_content (GaleonEmbed *embed, char *path, gboolean main);
static void embed_free_stylesheet_menu_item (GtkMenuItem *mi, gpointer data);

/** The global list of all GaleonEmbed structures */
GList *all_embeds = NULL;

/* global character set hash and sorted title list*/
GHashTable *charsets = NULL;
GList *sorted_charset_titles = NULL;

/* tab close button global pixmap data */
PixmapData *close_pix = NULL;

/* global var to indicate whether xpcom has been initialised */
gboolean pushed_startup = FALSE;

/* DnD drop types for embeds and tabs -- NOTE NOTE NOTE dont mess around 
 * with the order of these unless you know what you're doing! -- MattA */
const GtkTargetEntry embed_drop_types[] =
{
	{ "GALEON_URL",      0, DND_TARGET_GALEON_URL      },
	{ "text/uri-list",   0, DND_TARGET_TEXT_URI_LIST   },
	{ "_NETSCAPE_URL",   0, DND_TARGET_NETSCAPE_URL    },
	{ "STRING",          0, DND_TARGET_STRING          }
};
const gint embed_drop_types_num_items = (sizeof (embed_drop_types) / 
				         sizeof (GtkTargetEntry));

/**
 * embed_create: create a GaleonEmbed structure based on a given Embed
 * @previous can be NULL (orphan popups)
 */
GaleonEmbed *
embed_create (GaleonEmbed *previous, gboolean new_window, 
	      gboolean force_jump, gboolean raise_window,
	      guint32 chrome_mask)
{
	GaleonWindow *window = NULL;
	GaleonEmbed *embed;

	/* get the parent window if we're using it */
	if (previous != NULL && !new_window)
	{
		window = previous->parent_window;
	}
	else
	{
		/* create our own */
		window = window_create (chrome_mask);
	}

	/* create embed */
	embed = embed_create_no_window ();

	/* setup chromedness */
	if ((chrome_mask & GTK_MOZ_EMBED_FLAG_OPENASCHROME) != 0)
	{
		embed->is_chrome = TRUE;
	}

	/* add to window */
	window_add_embed (window, embed, force_jump, raise_window);

	/* return completed embed */
	return embed;
}

/**
 * embed_create_in_window: create embed in a given window
 */
GaleonEmbed *
embed_create_in_window (GaleonWindow *window)
{
	GaleonEmbed *embed;

	/* create embed */
	embed = embed_create_no_window ();

	/* add to window */
	window_add_embed (window, embed, FALSE, TRUE);

	/* return completed embed */
	return embed;
}

/**
 * embed_create_from_url_noraise: create a browser from a given url string
 * without raising the window
 */
GaleonEmbed *
embed_create_from_url_noraise (GaleonEmbed *previous, const gchar *url, 
			       gboolean force_jump, gboolean new_window)
{
	GaleonEmbed *embed;

	/* check argument */
	g_assert(url != NULL);
	
	/* this is a workaround for bug #65399. This is reimplemented in HEAD */
	if(previous && strncasecmp (url, "javascript", 10) == 0) {
		embed_load_url (previous, url);
		return previous;
	}

	/* create a window */
	embed = embed_create (previous, new_window, force_jump, 
			      FALSE, GTK_MOZ_EMBED_FLAG_ALLCHROME);

	/* show the window */
	embed_set_visibility (embed, TRUE);

	/* load the url */
	embed_load_url (embed, url);

	/* return completed embed */
	return embed;
}

/**
 * embed_create_from_url: create a browser from a given url string
 */
GaleonEmbed *
embed_create_from_url (GaleonEmbed *previous, const gchar *url, 
		       gboolean force_jump, gboolean new_window)
{
	GaleonEmbed *embed;

	/* check argument */
	g_assert(url != NULL);

	/* this is a workaround for bug #65399. This is reimplemented in HEAD */
	if(previous && strncasecmp (url, "javascript", 10) == 0) {
		embed_load_url (previous, url);
		return previous;
	}

	/* create a window */
	embed = embed_create (previous, new_window, force_jump, 
			      TRUE, GTK_MOZ_EMBED_FLAG_ALLCHROME);

	/* show the window */
	embed_set_visibility (embed, TRUE);

	/* load the url */
	embed_load_url (embed, url);

	/* return completed embed */
	return embed;
}

/**
 * embed_create_default: create a new browser pointing at the page
 * specified by configuration
 */
GaleonEmbed *
embed_create_default (GaleonEmbed *previous_embed, gboolean new_window)
{
	StartPageType start_page;
	GaleonEmbed *embed;
	gchar *url;

	/* get default URL */
	url = embed_get_default_url (previous_embed);

	/* load into a new window */
	embed = embed_create_from_url (previous_embed, url, TRUE, new_window);

	/* copy across session history (the wrapper must be initialized) */
	start_page = eel_gconf_get_integer (CONF_GENERAL_NEWPAGE_TYPE);
	if (previous_embed != NULL && start_page == STARTPAGE_LAST)
	{
		mozilla_copy_session_history (previous_embed, embed);
	}

	/* free and return */
	g_free (url);
	return embed;
}

/**
 * embed_create_from_url_view_source: create a browser from a given 
 * url string  in view source mode
 */
GaleonEmbed *
embed_create_from_url_view_source (GaleonEmbed *previous, const gchar *url,
				   gboolean new_window)
{
	GaleonEmbed *embed;
	gchar *vs_url;

	/* prepend view-source: protocol */
	vs_url = g_strconcat ("view-source:", url, NULL);

	/* get completed browser */
	embed = embed_create_from_url (previous, vs_url, TRUE, new_window);

	/* free allocated string */
	g_free (vs_url);
	
	/* return completed browser */
	return embed;
}

/**
 * embed_create_no_window: create a GaleonEmbed without reference to
 * a parent window
 */
static GaleonEmbed *
embed_create_no_window (void)
{
	GaleonEmbed *embed;

	/* build an embed structure */
	embed = g_new0 (GaleonEmbed, 1);

	/* add to global list of all embeds */
	all_embeds = g_list_prepend (all_embeds, embed);

	/* don't quit, we have an embed */
	session_server_stop_timeout ();
		
	/* no content for the location or title */
	embed->site_location = NULL;
	embed->site_title = g_strdup (_("Untitled"));
	embed->site_title_utf8 = mozilla_locale_to_utf8 (embed->site_title);

	embed->notebook_close_button = NULL;
	embed->notebook_close_pixmap = NULL;

	embed->usersheet = NULL;
	
	/* initial zoom level */
	embed->zoom = 100;
	
	/* make an embedding widget and check */
	embed->mozEmbed = gtk_moz_embed_new ();
	g_assert (GTK_IS_MOZ_EMBED (embed->mozEmbed));

	/* ref/unref widget -- is this really needed? */
	/* NB: apparently it is to get rid of some warnings, but I'm
	 * still not clear why -- MattA 13/4/2001 */
	/* as of 1/6/2001, it still doens't work... ! */
	gtk_widget_ref (GTK_WIDGET (embed->mozEmbed));
	gtk_object_set_data_full (GTK_OBJECT (embed->mozEmbed), "mozEmbed",
				  GTK_WIDGET (embed->mozEmbed),
				  (GtkDestroyNotify) gtk_widget_unref);

	/* setup data so we can always find GaleonEmbed */
	gtk_object_set_data (GTK_OBJECT (embed->mozEmbed), 
			     "GaleonEmbed", embed);

	/* connect appropriate signals */
	embed_connect_signals (embed);
	
	/* set magic as this is now ready to go */
	embed->magic = GALEON_EMBED_MAGIC;

	/* done! */
	return embed;
}

/**
 * embed_get_default_url: get the default URL on new page creation
 */
gchar *
embed_get_default_url (GaleonEmbed *previous_embed)
{
	const gchar *last_page_url;
	gchar *home_page_url;
	gint page_type;

	/* find out where we're supposed to start */
	if (previous_embed == NULL)
	{
		page_type = eel_gconf_get_integer 
			(CONF_GENERAL_STARTPAGE_TYPE);
	}
	else
	{
		page_type = eel_gconf_get_integer (CONF_GENERAL_NEWPAGE_TYPE);
	}

       	/* return the appropriate page */
	if (page_type == STARTPAGE_HOME)
	{
		/* get location of home page */
		home_page_url = eel_gconf_get_string(CONF_GENERAL_HOMEPAGE);

		/* load home page, if set */
		if (home_page_url != NULL)
		{
			return home_page_url;
		}
	}

	/* get location of last page: use previous browser if one,
	 * otherwise resort to fetching it from global history */
	if (previous_embed != NULL && previous_embed->site_location != NULL)
	{
		last_page_url = previous_embed->site_location;
	}
	else
	{
		last_page_url = history_get_last_url ();
	}
	
	if (page_type == STARTPAGE_LAST && last_page_url != NULL)
	{
		/* return last page pointed at */
		return g_strdup (last_page_url);
	}

	/* even in case of error, it's a good default */
	return g_strdup ("about:blank");
}

/**
 * embed_set_visibility: set the visiblity of a single embed
 */
void
embed_set_visibility (GaleonEmbed *embed, gboolean visibility)
{
	GaleonWindow *window;

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

	/* test for hiding */
	if (!visibility && embed->is_visible)
	{
		window_hide_one_embed (window);
	}

	/* test for showing */
	if (visibility && !embed->is_visible)
	{
		/* show the widget */
		/* don't ask me why this has to be done BEFORE showing
		 * the main window, it just does... */
		gtk_widget_show (embed->mozEmbed);

		window_show_one_embed (window);
	}

	/* new status */
	embed->is_visible = visibility;
}

/**
 * embed_load_url: interpret and load a URL into a given GaleonEmbed
 */
void
embed_load_url (GaleonEmbed *embed, const gchar *url)
{
	/* check arguments */
	return_if_not_embed (embed);
	g_assert(url != NULL);

	/* update location bar */
	if (embed->site_location != NULL)
	{
		g_free (embed->site_location);
	}
	embed->site_location = g_strdup (url);
	embed_update_page_location (embed);

	/* reset title */
	/* Why is this necessary ? it causes
	   bug 59577 -- Marco */
/*	g_free (embed->site_title);
	g_free (embed->site_title_utf8);
	embed->site_title = NULL;
	embed->site_title_utf8 = NULL;
	embed->site_title = g_strdup (_("Untitled"));
	embed->site_title_utf8 = mozilla_locale_to_utf8 (embed->site_title);
	embed_update_page_title (embed);*/

	/* initialise embed whenever a document is first loaded */
	if (embed->wrapper == NULL)
	{
		embed_wrapper_init (embed);

		/* hack to work around a bunch of problems when nothing
		 * is loaded */
		gtk_object_set_data (GTK_OBJECT (embed->mozEmbed), "real_url",
				     g_strdup (url));
		gtk_moz_embed_load_url (GTK_MOZ_EMBED (embed->mozEmbed),
					"about:blank");
	}
	else
	{
		/* load the URL */
		gtk_moz_embed_load_url (GTK_MOZ_EMBED(embed->mozEmbed), url);
	}
}

void
embed_grab_focus (GaleonEmbed *embed)
{
	if (!embed) return;
	gtk_widget_grab_focus (GTK_BIN(embed->mozEmbed)->child);
}

/**
 * embed_activate_link_mouse: handle a link click, using a mouse state
 */
gboolean
embed_activate_link_mouse (GaleonEmbed *previous, GaleonEmbed **new_embed,
			   const gchar *url, GdkEventButton *event)
{
	LinkState link_state;

	/* get link state from mouse state */
	link_state = mouse_state_to_link_state (event->button, event->state);

	/* do the activation */
	return embed_activate_link (previous, new_embed, url, link_state);
}

/**
 * embed_activate_link_keyboard: handle a link click, using a keyboard state
 */
gboolean
embed_activate_link_keyboard (GaleonEmbed *previous, GaleonEmbed **new_embed,
			      const gchar *url, guint evstate)
{
	LinkState link_state;

	/* get link state from mouse state */
	link_state = key_state_to_link_state (evstate);

	/* do the activation */
	return embed_activate_link (previous, new_embed, url, link_state);
}

/**
 * embed_activate_link: generalized URL activation function
 */
gboolean
embed_activate_link (GaleonEmbed *previous, GaleonEmbed **new_embed,
		     const gchar *url, LinkState link_state)
{
	gboolean tabbed_mode, handled;
	gboolean shifted, ctrled, alted;
	GaleonEmbed *new = NULL;
	guint button;

	/* check URL is valid */
	if (url == NULL)
	{
		return FALSE;
	}

	/* parse link state */
	handled = FALSE;
	button = (link_state & LINKSTATE_BUTTON_MASK) + 1; 
	shifted = link_state & LINKSTATE_SHIFTED;
	ctrled = link_state & LINKSTATE_CTRLED;
	alted = link_state & LINKSTATE_ALTED;
	
        /* parse button */
	switch (button)
	{
	case 1:
		/* left button (or no button) */
		if (!previous) {
			new = previous = embed_create_from_url 
				(NULL, "about:blank", FALSE, TRUE);
		}
		if (shifted) {
			downloader_save_link (previous, url);
		} else {
			embed_load_url (previous, url);
		}
		handled = TRUE;
		break;

	case 2:
		/* middle button */
		if (link_state == LINKSTATE_NEWTAB) {
			tabbed_mode = TRUE;
		}
		else if (link_state == LINKSTATE_NEWWIN) {
			tabbed_mode = FALSE;
		} else {
			tabbed_mode = eel_gconf_get_boolean 
				(CONF_TABS_TABBED);
		}
		if (shifted && !(alted || ctrled)) {
			new = embed_create_from_url (previous, url,
						     FALSE, tabbed_mode);
		} else {
			new = embed_create_from_url (previous, url,
						     FALSE, !tabbed_mode);
		}
		handled = TRUE;
		break;

	case 3:
		/* context menu shown by other stuff */
		handled = FALSE;
		break;

	default:
		g_warning ("link selected with unusual button");
		handled = FALSE;
		break;
	}

	/* store return value (NULL if not handled) */
	if (new_embed != NULL) {
		*new_embed = new;
	}

	/* done */
	return handled;
}

/**
 * embed_switch_to_page: switch to the embed notebook page
 */
void 
embed_switch_to_page (GaleonEmbed *embed)
{
	GaleonWindow *window;
	gint page;

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

	/* lookup the page number */
	page = gtk_notebook_page_num (GTK_NOTEBOOK (window->notebook),
				      GTK_WIDGET (embed->mozEmbed));

	/* jump to that page */
	gtk_notebook_set_page (GTK_NOTEBOOK (window->notebook), page);
}

/**
 * embed_startup_init: initialisation that needs to occur only one time
 * before any gtkmozembed object is created
 */
void
embed_startup_init ()
{
	/* general mozilla stuff that needs doing */
	embed_single_init ();
	mozilla_register_components ();

	/* installs notifiers for some mozilla prefs */
	mozilla_notifiers_init();

	/* only register the external ftp handler if it isn't galeon */
	if (eel_gconf_get_integer(CONF_DOWNLOADING_EXTERNAL_FTP) == 1 &&
	    !preferences_galeon_used_by_gnome_for_protocol ("ftp"))
	{
		mozilla_register_FtpProtocolHandler ();
	}

	/* only register the external mailto handler if it isn't galeon.
	   Note that this is startup only, as gnome-config does not
	   support notifiers. If the user does the stupid thing while galeon
	   is running, they're SOL. */
	if (!preferences_galeon_used_by_gnome_for_protocol ("mailto"))
	{
		mozilla_register_MailtoProtocolHandler ();
	}

	/* optionally register galeon to handle ghelp protocols */
	if (eel_gconf_get_boolean(CONF_GHELP_USE_GALEON) == TRUE ||
	    preferences_galeon_used_by_gnome_for_protocol ("man") ||
	    preferences_galeon_used_by_gnome_for_protocol ("info") ||
	    preferences_galeon_used_by_gnome_for_protocol ("ghelp") ||
	    preferences_galeon_used_by_gnome_for_protocol ("gnome-help"))
	{
		mozilla_register_gHelpProtocolHandlers ();
	}
}

/**
 * embed_connect_signals: connect the necessary signals to the embed
 */
static void 
embed_connect_signals (GaleonEmbed *embed)
{
	gint i;

        /* signals to connect on each embed widget */
	static const struct
	{ 
		char *event; 
		void *func; /* should be a GtkSignalFunc or similar */
	}
	signal_connections[] =
	{
		{ "location",        mozembed_location_changed_cb  },
		{ "title",           mozembed_title_changed_cb     },
		{ "net_start",       mozembed_load_started_cb      },
		{ "net_stop",        mozembed_load_finished_cb     },
		{ "net_state",       mozembed_net_status_change_cb },
		{ "progress",        mozembed_progress_change_cb   },
		{ "link_message",    mozembed_link_message_cb      },
		{ "js_status",       mozembed_js_status_cb         },
		{ "visibility",      mozembed_visibility_cb        },
		{ "destroy_browser", mozembed_destroy_brsr_cb      },
		{ "dom_mouse_down",  mozembed_dom_mouse_down_cb    },	
		{ "dom_mouse_click", mozembed_dom_mouse_click_cb   },
		{ "dom_key_press",   mozembed_dom_key_press_cb     },
		{ "size_to",         mozembed_size_to_cb           },
		{ "new_window",      mozembed_new_window_cb        },
		{ "security_change", mozembed_security_change_cb   },

		/* terminator -- must be last in the list! */
		{ NULL, NULL } 
	};
	
	/* connect signals */
	for (i = 0; signal_connections[i].event != NULL; i++)
	{
		gtk_signal_connect_while_alive (GTK_OBJECT(embed->mozEmbed),
						signal_connections[i].event,
						signal_connections[i].func, 
						embed,
						GTK_OBJECT(embed->mozEmbed));
	}
	
	/* also connect destroy (but not with "while_alive") */
	gtk_signal_connect (GTK_OBJECT (embed->mozEmbed), "destroy",
			    GTK_SIGNAL_FUNC (mozembed_destroy_cb), embed);

	/* set gtkmozembed drag and drop destination */
	gtk_drag_dest_set (GTK_WIDGET(embed->mozEmbed), GTK_DEST_DEFAULT_ALL,
			   embed_drop_types, embed_drop_types_num_items,
			   GDK_ACTION_COPY | GDK_ACTION_MOVE |
			   GDK_ACTION_LINK | GDK_ACTION_ASK );

	/* set gtkmozembed drag and drop signals */
	gtk_signal_connect (GTK_OBJECT (embed->mozEmbed), "drag_data_received",
			    GTK_SIGNAL_FUNC (embed_drag_data_received_cb),
			    embed);

	/* set links drag signal */
	gtk_signal_connect (GTK_OBJECT (embed->mozEmbed), "drag_data_get",
			    GTK_SIGNAL_FUNC (window_drag_data_get_cb),
			    embed);
}

/**
 * embed_single_init: init the gtkmozembed Single object
 */
static void
embed_single_init (void)
{
	GtkMozEmbedSingle *single;

	/* startup done */
	gtk_moz_embed_push_startup ();
	pushed_startup = TRUE;

	/* get single */
	single = gtk_moz_embed_single_get ();
	if (single == NULL)
	{
		g_warning ("Failed to get singleton embed object!\n");
	}

	/* allow creation of orphan windows */
	gtk_signal_connect (GTK_OBJECT (single), "new_window_orphan",
			    GTK_SIGNAL_FUNC (new_window_orphan_cb),  NULL);
}

/**
 * embed_wrapper_init: call it after the first page is loaded
 */
void 
embed_wrapper_init (GaleonEmbed *embed)
{
	embed->wrapper = mozilla_wrapper_init (embed);
}

/**
 * embed_progress_clear: clear all infos about download progress
 */
void
embed_progress_clear(GaleonEmbed *embed)
{
	/* set all progress values to 0 */ 
	embed->loadPercent = 0;
	embed->bytesLoaded = 0;
	embed->maxBytesLoaded = 0;
}

/**
 * embed_save_image: save an image from an embed
 */
void 
embed_save_image (GaleonEmbed *embed, gchar *url, gboolean ask)
{
	save_url (embed, url, CONF_STATE_SAVE_IMAGE_DIR, 
		  ask, FALSE, FALSE);
}

/**
 * embed_set_image_as_background: set an image as the desktop background
 */
void embed_set_image_as_background (GaleonEmbed *embed, gchar *url)
{
	gchar *file, *path;

	/* get the filename and check */
	file = g_basename (url);
	g_return_if_fail (file != NULL);
	g_return_if_fail (strlen (file) != 0);

	/* build a path to save it in */
	path = g_strconcat (g_get_home_dir (), "/.galeon/", file, NULL);

	/* save the image */
	mozilla_save_url (embed, url, path, ACTION_SETBACKGROUND);

	/* free */
	g_free (path);
}

/**
 * embed_save_document: save this document (or frame)
 */
void embed_save_document (GaleonEmbed *embed, gboolean main)
{
	gchar *url = NULL;
		
	/* get either frame or main document */
	url = main ? mozilla_get_main_document_url (embed) :
		mozilla_get_document_url (embed);

	if (!url)
	{
		gnome_error_dialog (_("No document is loaded"));
		return;
	}

	/* save it */
	save_url (embed, url, CONF_STATE_SAVE_DIR, 
		  TRUE, TRUE, main);

	/* free */
	g_free (url);
}

/**
 * save_url: save a given URL
 * FIXME pass main here is weird
 */
void 
save_url (GaleonEmbed *embed, gchar *target, 
	  gchar *default_dir_pref, gboolean ask,
	  gboolean ask_content, gboolean main)
{
	GnomeVFSURI *uri;
	gchar *retPath = NULL;
	gchar *fileName = NULL;
	gchar *dirName;
	gchar *retDir;
	gboolean ret;
	gboolean content;

	/* Get a filename from the target url */
	uri = gnome_vfs_uri_new (target);
	if (uri)
	{
		fileName = gnome_vfs_uri_extract_short_name (uri);
		gnome_vfs_uri_unref (uri);
	}

	dirName = eel_gconf_get_string (default_dir_pref);

	if (!dirName || strcmp (dirName,"~")==0)
	{
		g_free (dirName);
		dirName = g_strdup(g_get_home_dir ());
	}

	if (ask)
	{
		ret = show_file_picker (NULL, _("Select the file to save"),
					dirName, fileName, modeSave, &retPath,
					ask_content ? &content : NULL);
	}
	else
	{
		retPath = g_concat_dir_and_file (dirName, fileName);
		ret = TRUE;
	}

	if (ret == TRUE)
	{
		uri = gnome_vfs_uri_new (retPath);

		if (uri)
		{
			retDir = gnome_vfs_uri_extract_dirname (uri);

			if (ask_content && content)
			{
				ret = save_with_content (embed, retPath, main);
			}
			else
			{
				ret = mozilla_save_url (embed, target, 
							retPath, ACTION_NONE);
			}
			save_url_message (embed, retPath, ret);

			/* set default save dir */
			eel_gconf_set_string (default_dir_pref, 
						 retDir);

			g_free (retDir);
			gnome_vfs_uri_unref (uri);
		}
	}

	g_free (dirName);
	g_free (fileName);
	g_free (retPath);
}

static
gboolean save_with_content (GaleonEmbed *embed, char *path, gboolean main)
{
	gchar *data_dir;
	gboolean ret;

	return_val_if_not_embed (embed, FALSE);

	data_dir = g_strconcat (path, "content", NULL);

	if (mkdir (data_dir, 0755) != -1)
	{
		ret = mozilla_save_document (embed, path, data_dir,
					     ACTION_NONE, main); 
	}
	else 
	{
		ret = FALSE;
	}

	g_free (data_dir);

	return ret;
}

static 
void save_url_message (GaleonEmbed *embed, char *path, gboolean result)
{
	char *temp_message;
	if (result)
	{
		temp_message = g_strdup_printf(_("Saved as %s"), path);
	}
	else
	{
		temp_message = g_strdup_printf(_("Save as %s failed"), path);
	}

	window_update_temp_message (embed->parent_window,
				    temp_message);
	g_free (temp_message);
}

/**
 * embed_view_source: view web page source 
 */
void embed_view_source (GaleonEmbed *embed, gboolean main, 
			gboolean new_window)
{
	gchar *url;
	
	if (main) {
		url = mozilla_get_main_document_url (embed);
	} 
	else {
		url = mozilla_get_document_url (embed);
	}

	if (!url)
	{
		gnome_error_dialog (_("No document is loaded"));
		return;
	}

	if (eel_gconf_get_boolean (CONF_PROGRAMS_USE_EXTERNAL_SOURCE_VIEWER))
	{
		embed_view_source_external (embed, url);
	}
	else
	{
		embed_create_from_url_view_source (embed, url, new_window);
	}
	g_free (url);
}

/**
 * embed_view_source_external: view web page source with an external viewer 
 */
static void embed_view_source_external (GaleonEmbed *embed, const gchar *url)
{
	char *filename;
	char *htmlfile;
	int result;

	filename = g_strconcat(g_get_tmp_dir(),"/galeon-viewsource-XXXXXX", NULL);

	/* get a name for the temporary file */
	result = mkstemp (filename);

	if (result == -1)
	{
		gnome_error_dialog ( _("Could not create a "
				       "temporary file"));
		return;
	}
	close (result);

	htmlfile = g_strconcat (filename, ".html", NULL);
	rename (filename, htmlfile);
	mozilla_save_url (embed, url, htmlfile, ACTION_VIEWSOURCE);

	g_free (htmlfile);
	g_free (filename);
}

/**
 * embed_close: close a GaleonEmbed
 */
void
embed_close (GaleonEmbed *embed)
{
	GaleonWindow *window;

	/* check args */
	window = embed->parent_window;
	return_if_not_window (window);
	
	/* select the new page before destroying the current one */
	embed_select_new_page (embed);

	/* stop any pending loads */
	gtk_moz_embed_stop_load (GTK_MOZ_EMBED (embed->mozEmbed));

	/* Give the focus to the new active embed */
	if (window->active_embed && (window->active_embed != embed))
	{
		embed_grab_focus (window->active_embed);
	}

	/* destroy the embedding widget -- this will also destroy 
	 * the notebook tab and its label, and remove it from 
	 * the relevant lists */
	gtk_widget_destroy (GTK_WIDGET (embed->mozEmbed));
}

/**
 * embed_open_frame: open the focused frame
 */
void 
embed_open_frame (GaleonEmbed *embed, gboolean same_embed, gboolean new_window)
{
	gchar *url;
	
	/* get document URL */
	url = mozilla_get_document_url (embed);
	if (url == NULL)
	{
		return;
	}

	/* load it */
	if (same_embed)
	{
		embed_load_url (embed, url);
	} 
	else 
	{
		embed_create_from_url (embed, url, TRUE, new_window);
	}

	/* free */
	g_free (url);
}

/*
 * embed_reload: call gtk_moz_embed_reload.
 */
void
embed_reload (GaleonEmbed *embed, int flags)
{
	gtk_moz_embed_reload (GTK_MOZ_EMBED (embed->mozEmbed), 
			      flags);
}

/**
 * embed_update_page_location: called if the page location changes, or to
 * bring the GaleonEmbed.site_location field in sync with the currently 
 * viewed page
 */
void
embed_update_page_location (GaleonEmbed *embed)
{
	const PixmapData *drag_pixmap;
	GaleonWindow *window;
	gchar *new_location;

	/* check we're currently being viewed */
	if (!embed->is_active)
		return;

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

	/* get the location string */
	new_location = embed->site_location;

	/* clear text */
	window_clear_url_entry (window);
	if (new_location != NULL && strcmp (new_location, "about:blank") != 0)
	{
		/* change the url entry text */
		window_set_url_entry (window, new_location);
	
		/* update the drag location pixmap */
		if (eel_gconf_get_boolean (CONF_BOOKMARKS_FAVICONS_ENABLED) &&
		    window->drag_pixmap != NULL)
		{
			drag_pixmap = bookmarks_get_siteicon (new_location);
			gtk_pixmap_set (GTK_PIXMAP (window->drag_pixmap), 
					drag_pixmap->pixmap, 
					drag_pixmap->mask);
		}
	}
}

/**
 * embed_update_page_title: called if the page title changes, or to bring
 * the main window title in sync with the currently viewed page
 */
void
embed_update_page_title (GaleonEmbed *embed)
{
	gchar *title_string, *full_title;
	GaleonWindow *window;

	/* set notebook label (although this might not be visible) */
	embed_update_tab_title (embed);

	/* if this page isn't being viewed, get out now */
	if (!embed->is_active)
	{
		return;
	}

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

	/* get the format string */
	title_string = eel_gconf_get_string (CONF_WINDOWS_TITLE);
	if (!title_string) return;

	/* format the full title */
	full_title = str_dup_replace (title_string, "%s", embed->site_title);

	/* set the toplevel window title to the document title */
	gtk_window_set_title (GTK_WINDOW (window->WMain), full_title);

	/* free allocated strings */
	if (full_title) g_free (full_title);
	if (title_string) g_free (title_string);  
}

/**
 * embed_update_tab_title: updates the tab to contain the page title
 */
void
embed_update_tab_title (GaleonEmbed *embed)
{
	GaleonWindow *window;
	gchar *shortened;
	GtkWidget *tab;
	gint length;
	gint style;

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

        /* shorten notebook's label */
	tab = embed->notebook_hbox;
	style = eel_gconf_get_integer (CONF_TABS_TABBED_SHORTEN_STYLE);
        switch (style)
        {
        case 0:
		/* set fixed size of the label widget */
               	shortened = embed->site_title;
                break;

        case 1: 
		/* abbreviate the text label */
                length = eel_gconf_get_integer (
			CONF_TABS_TABBED_SHORTEN_CHARS);   
                shortened = shorten_name (embed->site_title, length);
                break;

        default:
                g_assert_not_reached ();
                return;
        }

	/* if it's different than the new shortened text, change it */
	if (strcmp (GTK_LABEL (embed->notebook_label)->label, shortened) != 0)
	{
		gtk_label_set_text (GTK_LABEL (embed->notebook_label),
				    shortened);
		embed_update_tab_size (embed);
	}

	/* the menu text is the full text */
	gtk_notebook_set_menu_label_text (GTK_NOTEBOOK (window->notebook),
					  GTK_WIDGET (embed->mozEmbed),
					  embed->site_title);

	/* free allocated strings */
	if (style == 1)
	{
		g_free (shortened);
	}
}

/**
 * embed_update_tab_size: updates tab sizing policies and size
 */
void
embed_update_tab_size (GaleonEmbed *embed)
{
	GaleonWindow *window;
	GtkWidget *tab;
	gint length;
	gint style;

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

        /* shorten notebook's label */
	tab = embed->notebook_hbox;
	style = eel_gconf_get_integer (CONF_TABS_TABBED_SHORTEN_STYLE);
        switch (style)
        {
        case 0:
		length = eel_gconf_get_integer 
			(CONF_TABS_TABBED_SHORTEN_POINTS);
                gtk_widget_set_usize (GTK_WIDGET (tab), length, -2);
                break;

        case 1: 
                gtk_widget_set_usize (GTK_WIDGET (tab), -1, -1);
                break;

        default:
                g_assert_not_reached ();
                return;
        }

	/* make sure everything is resized */
	gtk_widget_queue_resize (window->notebook);
}

/**
 * embed_update_tab_status: sets the status for the notebook tab
 * label belonging to embed.  this means changing the color of the tab
 * title and changing the closebutton between the active/inactive states if
 * necessary
 */
void
embed_update_tab_status (GaleonEmbed *embed)
{
	GtkWidget *label;

	return_if_not_embed (embed);

	/* get the label */
	label = GTK_WIDGET (embed->notebook_label);
	g_return_if_fail (GTK_IS_LABEL (label));

	/* set the right colour */
	if (embed->load_started > 0)
	{
		/* loading... */
		gtk_widget_set_style (label, loading_text_style);
	}
	else if (!(embed->has_been_viewed))
	{
		/* loaded, with new content */
		gtk_widget_set_style (label, new_text_style);
	}
	else if (embed->is_active)
	{
		/* loaded, viewed */
		gtk_widget_set_rc_style (label);
	}

	/* update closebutton status */
	if (embed->notebook_close_button != NULL &&
	    embed->notebook_close_pixmap != NULL)
	{
		gboolean make_close_insensitive;

		make_close_insensitive = eel_gconf_get_boolean (
			CONF_TABS_TABBED_CLOSEBUTTON_INSENSITIVE);

		if (embed->is_active || !make_close_insensitive)
		{
			gtk_widget_hide (embed->notebook_close_pixmap);
			gtk_widget_show (embed->notebook_close_button);
		}
		else if (make_close_insensitive)
		{
			gtk_widget_hide (embed->notebook_close_button);
			gtk_widget_show (embed->notebook_close_pixmap);
		}
	}

	if (embed->notebook_close_button)
	{
		gtk_object_set_data (GTK_OBJECT (embed->notebook_close_button),
			     	     "tab_active",
				     GINT_TO_POINTER (embed->is_active));
	}
}

/**
 * embed_notebook_tab_update_close_button: the notebook tab belonging to
 * embed, is updated (if necessary) to reflect the user's preference in the
 * display of a close button.
 */
void
embed_update_tab_close_button (GaleonEmbed *embed)
{
	GaleonWindow *window;
	GtkWidget *button, *pixmap;
	gboolean show;

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

	/* check prefs */
	show = eel_gconf_get_boolean (CONF_TABS_TABBED_CLOSEBUTTON);

	/* check for case when this is being destroyed */
	if (!show && (embed->notebook_close_button != NULL ||
		      embed->notebook_close_pixmap != NULL))
	{
		gtk_widget_destroy (embed->notebook_close_button);
		embed->notebook_close_button = NULL;
		gtk_widget_destroy (embed->notebook_close_pixmap);
		embed->notebook_close_pixmap = NULL;
	}
	/* check for case where button is being created */
	else if (show)
	{
		GtkRequisition r;
		if (embed->notebook_close_button != NULL)
		{
			gtk_widget_destroy (embed->notebook_close_button);
		}
		
		if (embed->notebook_close_pixmap != NULL)
		{
			gtk_widget_destroy (embed->notebook_close_pixmap);
		}
		
		close_pix = get_theme_pixmap ("small-close.xpm", FALSE);

		/* build button */
		button = embed->notebook_close_button = tab_button_new ();
		tab_button_set_relief (TAB_BUTTON (button), GTK_RELIEF_NONE);
		pixmap = gtk_pixmap_new (close_pix->pixmap, close_pix->mask);
		gtk_widget_show (pixmap);
		gtk_container_add (GTK_CONTAINER (button), pixmap);

		/* pack button into tab */
		gtk_box_pack_start (GTK_BOX (embed->notebook_hbox), button,
				    FALSE, FALSE, 0);

		/* connect click handler */
		gtk_signal_connect (GTK_OBJECT (button), "clicked",
				    GTK_SIGNAL_FUNC (
				    	embed_tab_close_button_clicked_cb),
				    embed);

		/* get button size */
		gtk_widget_size_request (button, &r);
		
		/* build inactive close pixmap */
		pixmap = embed->notebook_close_pixmap = 
			gtk_pixmap_new (close_pix->pixmap, close_pix->mask);

		/* give it the same size as the button, to prevent tab
		 * resizing */
		gtk_widget_set_usize (pixmap, r.width, r.height);

		/* it's only used in inactive situations, so make it
		 * insensitive */
		gtk_widget_set_sensitive (pixmap, FALSE);

		/* pack button into tab */
		gtk_box_pack_start (GTK_BOX (embed->notebook_hbox), pixmap,
				    FALSE, FALSE, 0);
	}
}

/**
 * embed_go_up: go to the nth parent directory
 */
void
embed_go_up (GaleonEmbed *embed, gint levels, LinkState state)
{
	GnomeVFSURI *uri, *up_uri;
	gchar *location;

	/* use gnome-vfs to find parent */
	uri = gnome_vfs_uri_new (embed->site_location);
	if (uri == NULL)
	{
		return;
	}

	/* go upwards to find the nth level up */
	do
	{
		up_uri = gnome_vfs_uri_get_parent (uri);
		gnome_vfs_uri_unref (uri);
		uri = up_uri;

		/* this can happen if Up is selected from the menu */
		if (uri == NULL)
		{
			return;
		}
	}
	while (levels--);

	/* get the location */
	location = gnome_vfs_uri_to_string (uri, 0);
	gnome_vfs_uri_unref (uri);

	/* visit it */
	embed_activate_link (embed, NULL, location, state);

	/* free */
	g_free (location);
}

/**
 * embed_can_go_up: test to see if we can go to a parent directory
 */
gboolean
embed_can_go_up (GaleonEmbed *embed)
{
	GnomeVFSURI *uri;
	gboolean result;

	/* check embed location is valid */
	if (embed->site_location == NULL || strlen (embed->site_location) == 0)
	{
		return FALSE;
	}

	/* use gnome-vfs to find parent */
	uri = gnome_vfs_uri_new (embed->site_location);
	if (uri == NULL)
	{
		return FALSE;
	}

	result = gnome_vfs_uri_has_parent (uri);

	gnome_vfs_uri_unref (uri);

	return result;
}

/**
 * embed_set_zoom: set the zoom level for a given embed
 */
void
embed_set_zoom (GaleonEmbed *embed, gint zoom)
{
	/* sanity check */
	if (zoom == 0)
	{
		g_warning ("ignoring request to set zoom to 0");
		return;
	}

	/* check we're not already set */
	if (zoom == embed->zoom || embed->wrapper == NULL)
	{
		return;
	}

	/* set in mozilla */
	mozilla_set_zoom (embed, (float)zoom / 100.0);
	embed->zoom_auto_set = FALSE;
	embed->zoom = zoom;

	/* set in window */
	window_update_zoom (embed->parent_window);
}

/** 
 * Creates the back history menu 
 */
GtkMenu *
embed_create_back_menu (GaleonEmbed *embed)
{
	int index, count, i, level;
	char **titles;
	GtkWidget *menu = gtk_menu_new ();

	if (!mozilla_session_history_get_all_titles (embed, &titles,
						     &count, &index))
	{
		return NULL;
	}

	for (i = index - 1, level = 0; i >= 0; i--, level++) 
	{
		add_bf_menu (menu, titles[i], i, embed, level);
	}

	free_string_array (titles, count);
	return GTK_MENU(menu);
}

/**
 * Creates the forward history menu
 */
GtkMenu *
embed_create_forward_menu (GaleonEmbed *embed)
{
	int index, count, i, level;
	char **titles;
	GtkWidget *menu = gtk_menu_new ();

	if (!mozilla_session_history_get_all_titles (embed, &titles,
						     &count, &index))
	{
		return NULL;
	}	

	for (i = index + 1, level = 0; i < count; i++, level++)
	{
		add_bf_menu (menu, titles[i], i, embed, level);
	}
	
	free_string_array (titles, count);
	return GTK_MENU(menu);
}

/** 
 * Creates the multi-level up menu 
 */
GtkMenu *
embed_create_up_menu (GaleonEmbed *embed)
{
	GnomeVFSURI *uri, *up_uri;
	GtkWidget *menu, *item;
	gint level;

	/* check embed location is valid */
	if (embed->site_location == NULL || strlen (embed->site_location) == 0)
	{
		return NULL;
	}

	/* create a vfs entry for this level */
	uri = gnome_vfs_uri_new (embed->site_location);
	if (uri == NULL)
	{
		return NULL;
	}

	/* create the menu */
	menu = gtk_menu_new ();

	/* create each possible up entry */
	for (level = 0;; level++)
	{
		up_uri = gnome_vfs_uri_get_parent (uri);
		gnome_vfs_uri_unref (uri);
		uri = up_uri;
		
		/* get out of loop if no parent */
		if (uri == NULL)
		{
			break;
		}

		/* create the menu entry */
		item = new_num_accel_menu_item (level, uri->text, TRUE, menu);
		gtk_widget_show (GTK_WIDGET (item));
		gtk_object_set_user_data (GTK_OBJECT (item), embed);
		gtk_menu_append (GTK_MENU (menu), GTK_WIDGET (item));
		gtk_signal_connect (GTK_OBJECT (item), "activate",
				    up_menu_menuitem_activate_cb, 
				    GINT_TO_POINTER (level));
		gtk_signal_connect (GTK_OBJECT (item), "button_release_event",
				(GtkSignalFunc) 
					(up_menu_menuitem_button_release_cb),
				GINT_TO_POINTER (level));

		gtk_widget_lock_accelerators (item);
	}

	/* the menu is completed */
	return GTK_MENU (menu);
}

/**
 * embed_shift_tab: shifts embed by amount in the notebook
 */
void
embed_shift_tab (GaleonEmbed *embed, gint amount)
{
	GaleonWindow *window;
	GtkNotebook *notebook;
	gint page;

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

	/* reorder the tab */
	page = gtk_notebook_page_num (notebook, GTK_WIDGET (embed->mozEmbed));
	gtk_notebook_reorder_child (notebook, GTK_WIDGET (embed->mozEmbed),
				    page + amount);

	/* and update the menuitems */
	window_update_tab_controls (window);
}

/* embed_move_to_new_window: moves embed to a new window */
void
embed_move_to_new_window (GaleonEmbed *embed)
{
	GaleonWindow *window;

	return_if_not_embed (embed);

	window = window_create (GTK_MOZ_EMBED_FLAG_ALLCHROME);
	return_if_not_window (window);

	embed_move_to_existing_window (embed, window, 0);
}

/* embed_move_to_existing_window: moves embed to window, placing it on the
 * given page in the notebook */
void
embed_move_to_existing_window (GaleonEmbed *embed, GaleonWindow *window,
			       gint page)
{
	GaleonWindow *orig_window;
	GtkNotebook *notebook, *orig_notebook;
	gint orig_page;

	return_if_not_embed (embed);
	return_if_not_window (window);

	/* get the original window */
	orig_window = embed->parent_window;

	/* find where we're putting it */
	notebook = GTK_NOTEBOOK (window->notebook);

	if (orig_window == NULL)
	{
		window_add_embed (window, embed, FALSE, TRUE);
	}
	/* simple case -- moving within the same window; just reorder
	 * the page */
	else if (window == orig_window)
	{
		gtk_notebook_reorder_child (notebook, embed->mozEmbed, page);
		window_update_tab_controls (window);
	}
	/* otherwise, move the embed to the other notebook */
	else
	{
		/* get original notebook */
		orig_notebook = GTK_NOTEBOOK (orig_window->notebook);
		orig_page = gtk_notebook_page_num (orig_notebook,
						   embed->mozEmbed);

		/* add a reference to the tab so it won't be destroyed when
		 * we remove the page (the embed has already been
		 * referenced when it was created, so it doesn't need
		 * another reference) */
		gtk_object_ref (GTK_OBJECT (embed->notebook_hbox));

		/* select the new page before moving the current one */
		embed_select_new_page (embed);

		/* remove the embed from its current page */
		orig_window->embed_list =
			g_list_remove (orig_window->embed_list, embed);
		if (embed->is_visible)
		{
			window_hide_one_embed (orig_window);
		}
		gtk_notebook_remove_page (orig_notebook, orig_page);
		window_update_tab_controls (orig_window);

		/* if that was the only embed in the window,
		 * delete the window */
		if (orig_window->embed_list == NULL)
		{
			window_close (orig_window);
		}

		/* add the embed to the new window's list */
		window->embed_list = g_list_append (window->embed_list, embed);
		if (embed->is_visible)
		{
			window_show_one_embed (window);
		}
		embed->parent_window = window;

		/* add it into the destination notebook */
		gtk_notebook_insert_page (notebook, embed->mozEmbed, 
					  embed->notebook_hbox, page);

		/* switch to the dropped embed's page */
		gtk_notebook_set_page (notebook, page);

		/* update tab state */
		embed_update_tab_status (embed);

		/* and remove the tab's reference */
		gtk_object_unref (GTK_OBJECT (embed->notebook_hbox));
	}
}

/**
 * Creates a menu item with a numbered/lettered accel
 */
static GtkWidget *
new_num_accel_menu_item (gint num, gchar *origtext, gboolean lettersok, 
			 GtkWidget *menu)
{
	gchar *text = new_num_accel_str(num, origtext, lettersok);
	if (text == NULL)
		return gtk_menu_item_new_with_label (origtext);
	else
	{
		GtkWidget *item = gtk_menu_item_new_with_label ("");
		label_set_accel_text (text, GTK_BIN (item)->child, menu, item);
		g_free (text);
		return item;
	}
}

/**
 * Adds a menuitem to a back/forward history menu
 */
static void
add_bf_menu (GtkWidget *menu, char* label, int index, GaleonEmbed *embed, int level)
{
	gchar *tmp = escape_uline_accel (label);
	GtkWidget *item = new_num_accel_menu_item (level, tmp, TRUE, menu);
	g_free (tmp);

	gtk_widget_show (item);
	gtk_object_set_user_data (GTK_OBJECT (item), embed);
	gtk_menu_append (GTK_MENU (menu), item);
	gtk_signal_connect (GTK_OBJECT (item), "activate",
			    history_menu_menuitem_activate_cb, 
			    GINT_TO_POINTER (index));
	gtk_signal_connect (GTK_OBJECT (item), "button_release_event",
			(GtkSignalFunc)	
				(history_menu_menuitem_button_release_cb),
			GINT_TO_POINTER (index));
	gtk_widget_lock_accelerators (item);
}


static void
embed_select_new_page (GaleonEmbed *embed)
{
	GaleonWindow *window;
	GaleonEmbed *new_embed;
	gboolean last_embed = FALSE;

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

	/* check if this is the last embed in the window */
	last_embed = (g_list_length (window->embed_list) == 1);

	if (!last_embed && embed->is_active)
	{
		/* this logic implements the galeon Smart Tab Selection
		 * Technology (tm) as suggested by Matthew Aubury -- Nate */
		switch (embed->focus_type)
		{
		case FOCUS_ON_CREATE:
			/* 
			 * embed_list is in order of tabs most recently
			 * focused.  select the second element in list since
			 * the first still hasn't been destroyed
			 */
			new_embed = g_list_nth_data (window->embed_list, 1);
			embed_switch_to_page (new_embed);
			break;

		case FOCUS_ON_REQUEST:
			gtk_notebook_next_page 
				(GTK_NOTEBOOK (window->notebook));
			break;

		default:
			g_assert_not_reached ();
			break;
		}
	} 
}

void
embed_init_css_menu (GaleonEmbed *embed)
{
	GaleonWindow *window = embed->parent_window;
	GSList *radiogroup = NULL;
	GList *node, *list = NULL;
	GtkWidget *css_menu, *item;
	gboolean sheets = FALSE, ticked = FALSE;
	AlternateStyleSheet *current;
	gint i;

	return_if_not_embed (embed);
	return_if_not_window (window);
	if (!embed->mozEmbed) return;
	if (embed->load_started > 0) return;

	/* get stylesheet data */
	list = mozilla_get_alternate_stylesheets (embed);
	current = mozilla_get_selected_stylesheet (embed);

	/* create menu */
	css_menu = gtk_menu_new ();
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (window->csslist), css_menu);

	/* tearoff.. */
	if (gnome_preferences_get_menus_have_tearoff ())
	{
		GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
		gtk_menu_append (GTK_MENU (css_menu), tearoff);
		gtk_widget_show (tearoff);
	}

	/* process all sheets */
	for (i = 1, node = list; node; node = node->next, i++)
	{
		AlternateStyleSheet *a = (AlternateStyleSheet *) node->data;
		sheets = TRUE;

		if (strcmp (a->name, "") == 0)
		{
			item = gtk_radio_menu_item_new_with_label
					(radiogroup, _("Untitled"));
		}
		else
		{
			item = gtk_radio_menu_item_new_with_label
					(radiogroup, a->name);
		}
		radiogroup = gtk_radio_menu_item_group 
				(GTK_RADIO_MENU_ITEM (item));
		gtk_menu_append (GTK_MENU(css_menu), item);
		gtk_widget_lock_accelerators (item);
		gtk_widget_show (item);
		
		gtk_object_set_data (GTK_OBJECT (item), "embed", embed);
		gtk_object_set_data (GTK_OBJECT (item), "index",
				     GINT_TO_POINTER (i));
		gtk_signal_connect (GTK_OBJECT (item), "activate", 
				    embed_change_stylesheet_cb, a);
		gtk_signal_connect (GTK_OBJECT (item), "destroy", 
				    embed_free_stylesheet_menu_item, a);
		  
		/* tick if this is the current sheet */
		if      (current != NULL && a->sheet == current->sheet &&
		         a->type == STYLESHEET_ALTERNATE && ticked == FALSE)
		{
			GTK_CHECK_MENU_ITEM (item)->active = ticked = TRUE;
		}
		else if (current != NULL && a->type == STYLESHEET_DEFAULT &&
		         ticked == FALSE)
		{
			GTK_CHECK_MENU_ITEM (item)->active = ticked = TRUE;
		}
		else if (current == NULL && a->type == STYLESHEET_NONE &&
		         ticked == FALSE)
		{
			GTK_CHECK_MENU_ITEM (item)->active = ticked = TRUE;
		}
		else
		{
			GTK_CHECK_MENU_ITEM (item)->active = FALSE;
		}
	}

	/* set sensitivity according to whether any sheets are found */
	gtk_widget_set_sensitive (window->csslist, sheets);

	/* cleanup */
	if (current)
	{
		g_free (current->name);
		g_free (current);
	}
	g_list_free (list);
}

static void
embed_free_stylesheet_menu_item (GtkMenuItem *mi, gpointer data)
{
	AlternateStyleSheet *a = (AlternateStyleSheet *) data;
	g_free (a->name);
	g_free (a);
}

void
embed_toggle_image_blocking (const gchar *url)
{
	GnomeVFSURI *vfs_uri = gnome_vfs_uri_new (url);
	const gchar *host;
	GList *permissions, *l;
	gboolean done = FALSE;

	if (vfs_uri == NULL)
	{
		return;
	}
	
	host = gnome_vfs_uri_get_host_name (vfs_uri);
	if (host == NULL)
	{
		gnome_vfs_uri_unref (vfs_uri);
		return;
	}

	permissions = mozilla_get_permissions (IMAGEPERMISSION);

	for (l = permissions; l && done == FALSE; l = g_list_next (l))
	{
		CookieBase *b = (CookieBase *) l->data;
		
		if (strcmp (host, b->domain) == 0)
		{
			GList *remove = NULL;
			remove = g_list_append (remove, b);
			mozilla_remove_permissions (remove, IMAGEPERMISSION);
			g_list_free (remove);
			done = TRUE;
		}

		g_free (b->type);
		g_free (b->domain);
		g_free (b);
	}

	if (done == FALSE) mozilla_block_url (url);

	gnome_vfs_uri_unref (vfs_uri);
	g_list_free (permissions);
}
