/*
 *  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 "misc.h"
#include "embed.h"
#include "bookmarks.h"
#include "prefs.h"
#include "context.h"
#include "dialog.h"

#include <string.h>
#include <libgnomeui/gnome-preferences.h>
#include <libgnomeui/gnome-stock.h>
#include <libgnome/gnome-config.h>
#include <libgnome/gnome-i18n.h>
#include <libgnomeui/gtkpixmapmenuitem.h>
#include <gdk/gdkkeysyms.h>

static void bookmarks_menu_destroy (GaleonWindow *window);
static GtkWidget *bookmarks_menu_create_item (BookmarkItem *bm, GtkMenu *menu,
					      gboolean lock_accels);
static void bookmarks_create_file_menu (GtkMenuItem *file_menuitem,
					BookmarkItem *bookmark);
static void bookmarks_create_file_submenu (GtkMenuItem *file_menuitem, 
					   BookmarkItem *bookmark);
static void bookmarks_menu_create_recursively_cb (GtkMenuItem *menuitem,
						  gpointer data);
static void bookmarks_create_file_menu_cb (GtkMenuItem *mi, BookmarkItem *b);
GtkMenu *bookmarks_create_copy_menu_recursively (BookmarkItem *source,
						 BookmarkItem *b);
static gint bookmarks_smart_bm_dialog_single (GtkWidget *dialog, 
					      GaleonEmbed *embed,
					      GaleonEmbed **newembed,
					      BookmarkItem *bi,
					      LinkState state);
static gint bookmarks_smart_bm_dialog_multi (GtkWidget *dialog, 
					     GaleonEmbed *embed,
					     GaleonEmbed **newembed,
					     BookmarkItem *bi,
					     LinkState state);
static gboolean bookmark_has_subcategories (BookmarkItem *b);
static void bookmarks_menu_sync_accel_labels_item (GtkMenuItem *item);
static void bookmarks_init_menu_accels_recursively (BookmarkItem *b,
						    GaleonWindow *window);

/* when not zero, accel update signals will be ignored */
gint bookmarks_block_accel_update = 0;

/**
 * bookmarks_menu_create: create the bookmarks menu for a single window
 */
void
bookmarks_menu_create (GaleonWindow *window) 
{
	GtkMenuItem *add_menuitem;
	GtkMenu *menu;

	return_if_not_window (window);

	/* free dummywidgets */
	if (window->dummy_widgets) 
	{
		/* free dummywidgets */
		g_list_foreach (window->dummy_widgets, 
				(GFunc)gtk_widget_unref, NULL);
		g_list_free (window->dummy_widgets);
	}

	/* setup bookmark accel dummywidgets (also do this when no menubar) */
	window->dummy_widgets = NULL;
	bookmarks_init_menu_accels_recursively (bookmarks_root, window);

	menu = (GtkMenu *) window->bookmarks_menu;
	add_menuitem = (GtkMenuItem *) window->add_bookmark;

	window->bookmarks_tooltips = gtk_tooltips_new ();
	bookmarks_create_file_submenu (add_menuitem, bookmarks_root);
	bookmarks_menu_create_recursively (bookmarks_root, menu,
		add_menuitem, GTK_TOOLTIPS(window->bookmarks_tooltips), 
		TRUE, FALSE, FALSE);
}

/**
 * bookmarks_menu_recreate: recreate the bookmarks menu for a single window
 */
void
bookmarks_menu_recreate (GaleonWindow *window)
{
	bookmarks_menu_destroy (window);
	bookmarks_menu_create (window);
}

static void
bookmarks_menu_destroy (GaleonWindow *window)
{
	GtkWidget *bookmarks_menu;
	GtkWidget *separator;
	GList *li, *l;
	
	separator = window->bookmarks_separator;
	bookmarks_menu = window->bookmarks_menu;

	l = gtk_container_children (GTK_CONTAINER (bookmarks_menu));
	
	/* first look for the separator */
	for (li = l; li != NULL; li = li->next)
	{
		if (li->data == separator)
		{
			break;
		}
	}
	
	/* then, destroy every widget after it */
	for (li = li->next; li != NULL; li = li->next)
	{
		if (GTK_IS_WIDGET (li->data))
			gtk_container_remove (GTK_CONTAINER (bookmarks_menu),
				GTK_WIDGET (li->data));
	}

	/* destroy old tooltip */
	if (window->bookmarks_tooltips)
	{
		gtk_object_destroy (GTK_OBJECT (window->bookmarks_tooltips));
		window->bookmarks_tooltips = NULL;
	}

	g_list_free (l);
}

/**
 * bookmarks_menu_create_recursively: Create menus for bookmarks
 * @bookmark: the root bookmark of the menu
 * @menu: the menu to fill with @bookmark children
 * @file_menuitem: the menu to fill with categories (to add bookmarks)
 * @tooltips: the tooltips object to use (or NULL if not needed)
 * @options: set to TRUE if you want the special options (add bookmark here...)
 * 
 * Creates the bookmarks menu and the file submenu if necessary. If you don't
 * need to create a file submenu pass NULL as file_menuitem. 
 * (usefull for the toolbar menus).
 **/
void
bookmarks_menu_create_recursively (BookmarkItem *bookmark, GtkMenu *menu, 
				   GtkMenuItem *file_menuitem,
				   GtkTooltips *tooltips, gboolean options, 
				   gboolean disable_tearoffs,
				   gboolean lock_accels)
{
	GList *l; 
	GtkWidget *mi;
	ExtraItemsLocation opts_location = eel_gconf_get_integer
				(CONF_BOOKMARKS_XITEMS);

	g_return_if_fail (bookmark->type == BM_FOLDER || 
			  bookmark->type == BM_AUTOBOOKMARKS);

	gtk_object_set_data (GTK_OBJECT (menu), "disable_tearoffs",
			     GINT_TO_POINTER (disable_tearoffs));
	
	bookmarks_block_accel_update++;

	if (file_menuitem != NULL) 
		bookmarks_create_file_menu (file_menuitem, bookmark);
			
	if (options)
	{
		if (opts_location == EXTRA_ITEM_LOCATION_TOP_SUBMENU)
		{
			bookmarks_menu_create_extra_items (menu, bookmark, 
							   opts_location);
		}
		else if (bookmark == bookmarks_root &&
				opts_location == EXTRA_ITEM_LOCATION_CONTEXT)
		{
			mi = bookmarks_menu_create_item (bookmark, menu,
							 lock_accels);
			gtk_signal_connect 
				(GTK_OBJECT(mi),
				 "button-press-event",
				 GTK_SIGNAL_FUNC (
					 bookmarks_button_press_event_cb),
				 bookmark);
			gtk_signal_connect 
				(GTK_OBJECT(mi),
				 "button-release-event",
				 GTK_SIGNAL_FUNC (
					 bookmarks_button_release_event_cb),
				 bookmark);
			gtk_widget_show (mi);
			gtk_menu_append (menu, mi);
			mi = gtk_menu_item_new ();
			gtk_widget_set_sensitive (mi, FALSE);
			gtk_widget_show (mi);
			gtk_menu_append (menu, mi);
		}
	}

	for (l = bookmark->list; l != NULL; l = l->next) 
	{
		BookmarkItem *b = l->data;
		GtkWidget *submenu;
		gchar *loc_str;

		switch (b->type)
		{
		case BM_FOLDER:
		case BM_AUTOBOOKMARKS:
			submenu = gtk_menu_new ();
			gtk_menu_set_accel_group (GTK_MENU (submenu),
						  menu->accel_group);
			loc_str = mozilla_utf8_to_locale (b->name);
			gtk_menu_set_title (GTK_MENU (submenu), 
					    loc_str[0] != '\0' ? loc_str
					    		       : b->url);
			g_free (loc_str);
			mi = bookmarks_menu_create_item (b, menu, lock_accels);
			gtk_menu_item_set_submenu (GTK_MENU_ITEM(mi), submenu);
			gtk_signal_connect 
				(GTK_OBJECT (submenu), "button-press-event", 
				 GTK_SIGNAL_FUNC (
					bookmarks_button_press_event_cb), b);
			gtk_signal_connect 
				(GTK_OBJECT (submenu), "button-release-event", 
				 GTK_SIGNAL_FUNC (
					bookmarks_button_release_event_cb), b);
			
			gtk_object_set_data (GTK_OBJECT (mi), "bookmark", b);
			gtk_object_set_data (GTK_OBJECT (mi), "submenu",
					     submenu);
			gtk_object_set_data (GTK_OBJECT (mi), "tooltips",
					     tooltips);
			gtk_object_set_data (GTK_OBJECT (mi), "options", 
					     GINT_TO_POINTER (options));
			gtk_object_set_data (GTK_OBJECT (mi), 
					     "disable_tearoffs", 
					     GINT_TO_POINTER (
						     disable_tearoffs));
			gtk_object_set_data (GTK_OBJECT (mi), "lock_accels", 
					     GINT_TO_POINTER (lock_accels));
			
			gtk_signal_connect 
				(GTK_OBJECT (mi), "map", 
				 bookmarks_menu_create_recursively_cb,
				 NULL);
			break;
		case BM_SITE:
			mi = bookmarks_menu_create_item (b, menu, lock_accels);
			gtk_signal_connect_after
				(GTK_OBJECT (mi), "button_press_event",
				 GTK_SIGNAL_FUNC (
					 bookmarks_button_press_event_cb), b);
			gtk_signal_connect_after
				(GTK_OBJECT (mi), "button_release_event", 
				 GTK_SIGNAL_FUNC (
					 bookmarks_button_release_event_cb),
				 b);
			gtk_signal_connect_after
				(GTK_OBJECT (mi), "activate", 
				 GTK_SIGNAL_FUNC (bookmarks_activate_cb), b);
			if (tooltips)
				gtk_tooltips_set_tip
					(tooltips, GTK_WIDGET (mi),
					 b->url, NULL);
			break;
		case BM_SEPARATOR:
		default:
			mi = gtk_menu_item_new ();
			gtk_widget_set_sensitive (mi, FALSE);
			break;
		}
		gtk_widget_show (mi);
		gtk_menu_append (menu, mi);
	}

	if (options && opts_location != EXTRA_ITEM_LOCATION_TOP_SUBMENU)
		bookmarks_menu_create_extra_items (menu, bookmark,
						   opts_location);

	if (gnome_preferences_get_menus_have_tearoff ()
	    && (bookmark != bookmarks_root)
	    && !disable_tearoffs)
	{
		GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
		gtk_menu_prepend (GTK_MENU (menu), tearoff);
		gtk_widget_show (tearoff);
		gtk_signal_connect (GTK_OBJECT (tearoff),
				    "button-release-event",
				    GTK_SIGNAL_FUNC ( 
					bookmarks_button_release_event_cb),
				    NULL);
	}

	bookmarks_block_accel_update--;
}

static void
bookmarks_create_file_menu (GtkMenuItem *file_menuitem, BookmarkItem *bookmark)
{
	GList *l;
	GtkWidget *file_menu;

	bookmarks_block_accel_update++;

	if (!GTK_MENU_ITEM (file_menuitem)->submenu) {
		gtk_signal_disconnect_by_data
			(GTK_OBJECT (file_menuitem),
			 bookmark);
		bookmarks_create_file_submenu
			(file_menuitem, bookmark);
	}
	
	file_menu = GTK_MENU_ITEM (file_menuitem)->submenu;
	
	for (l = bookmark->list; l ; l = l->next) {
		BookmarkItem *b = l->data;
		if ((b->type == BM_FOLDER) || (b->type == BM_AUTOBOOKMARKS)) {
			GtkWidget *file = bookmarks_menu_create_item (b,
							GTK_MENU (file_menu),
							FALSE);
			gtk_widget_show (file);
			gtk_menu_append (GTK_MENU (file_menu), file);
			if (bookmark_has_subcategories (b)) {
				bookmarks_create_file_submenu
					(GTK_MENU_ITEM (file), b);
				gtk_signal_connect
					(GTK_OBJECT(file),
					 "map",
					 bookmarks_create_file_menu_cb,
					 b);
			} else {
				gtk_signal_connect (GTK_OBJECT(file),
						    "activate",
						    bookmarks_file_bookmark_cb,
						    b);
			}			
		} else if (b->type == BM_SEPARATOR) {
			GtkWidget *mi = gtk_menu_item_new ();
			gtk_widget_set_sensitive(mi, FALSE);
			gtk_widget_show (GTK_WIDGET (mi));
			gtk_menu_append (GTK_MENU (file_menu), 
					 GTK_WIDGET (mi));
		}
	}
	gtk_menu_reposition (GTK_MENU (file_menu));

	bookmarks_block_accel_update--;

}

static void
bookmarks_menu_create_recursively_cb (GtkMenuItem *mi, gpointer data)
{
	BookmarkItem *b = gtk_object_get_data (GTK_OBJECT (mi), "bookmark");
	GtkWidget *submenu = gtk_object_get_data (GTK_OBJECT (mi), "submenu");
	GtkTooltips *tooltips = gtk_object_get_data (GTK_OBJECT (mi), "tooltips");
	gboolean options = 
		GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (mi), "options"));
	gboolean disable_tearoffs = 
		GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (mi), 
						      "disable_tearoffs"));
	gboolean lock_accels =
		GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (mi), 
						      "lock_accels"));
	
	bookmarks_block_accel_update++;

	gtk_signal_disconnect_by_func (GTK_OBJECT (mi),  
				       bookmarks_menu_create_recursively_cb,
				       NULL);
	
	bookmarks_menu_create_recursively (b, GTK_MENU (submenu), NULL, 
					   tooltips, options,
					   disable_tearoffs,
					   lock_accels);

	gtk_menu_reposition (GTK_MENU (submenu));

	bookmarks_block_accel_update--;
}

static void
bookmarks_create_file_menu_cb (GtkMenuItem *mi, BookmarkItem *b)
{
	gtk_signal_disconnect_by_func (GTK_OBJECT (mi),  
			    bookmarks_create_file_menu_cb,
			    b);
	bookmarks_block_accel_update++;
	bookmarks_create_file_menu (mi, b);
	bookmarks_block_accel_update--;
}

static gboolean 
bookmark_has_subcategories (BookmarkItem *b) 
{
	GList *l;
	for (l = b->list; l; l = l->next) {
		BookmarkItem *bi = l->data;
		if ((bi->type == BM_FOLDER) || (bi->type == BM_AUTOBOOKMARKS))
			return TRUE;
	}
	return FALSE;
}

/**
 * Creates the extra "Add bookmark to this folder" and "Open all bookmarks
 * in this folder" menuitems if necessary and places them appropriately in
 * the bookmarks menu.
 **/
void
bookmarks_menu_create_extra_items (GtkMenu *menu, BookmarkItem *bookmark,
				   ExtraItemsLocation opts_location)
{
	GtkMenu *extra_menu;
	GtkWidget *extra_menu_item = NULL;

	g_return_if_fail (GTK_IS_MENU (menu));

	if (opts_location == EXTRA_ITEM_LOCATION_CONTEXT)
		return;

	if (opts_location == EXTRA_ITEM_LOCATION_TOP_SUBMENU || 
			opts_location == EXTRA_ITEM_LOCATION_BOTTOM_SUBMENU)
	{
		gboolean disable_tearoffs = 
			GPOINTER_TO_INT (gtk_object_get_data (GTK_OBJECT (menu),
						      "disable_tearoffs"));

		extra_menu = GTK_MENU (gtk_menu_new ());
		extra_menu_item = gtk_menu_item_new_with_label ("");
		label_set_accel_text (_("_Folder actions"), 
			GTK_BIN (extra_menu_item)->child, GTK_WIDGET (menu),
			extra_menu_item);
		gtk_menu_item_set_submenu (GTK_MENU_ITEM (extra_menu_item),
				GTK_WIDGET (extra_menu));

		if (gnome_preferences_get_menus_have_tearoff () &&
		    !disable_tearoffs){
			GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
			gtk_menu_prepend (extra_menu, tearoff);
			gtk_widget_show (tearoff);
		}
	}
	else
		extra_menu = menu;

	bookmarks_menu_create_base_extra_items (extra_menu, bookmark);
	context_menu_add_item (extra_menu, _("_Edit..."), 
			bookmarks_edit_activate_cb, bookmark,
			GNOME_STOCK_MENU_PROP, TRUE);
	context_menu_add_item (extra_menu, _("_Remove bookmark folder"),
			bookmarks_remove_activate_cb, bookmark,
			GNOME_STOCK_MENU_TRASH, 
			!(bookmark == bookmarks_root));

	if (opts_location == EXTRA_ITEM_LOCATION_TOP_SUBMENU)
	{
		gtk_menu_append (menu, GTK_WIDGET (extra_menu_item));
		context_menu_add_seperator (menu);
		gtk_widget_show (GTK_WIDGET (extra_menu_item));
	}
	else if (opts_location == EXTRA_ITEM_LOCATION_BOTTOM_SUBMENU)
	{
		context_menu_add_seperator (menu);
		gtk_menu_append (menu, GTK_WIDGET (extra_menu_item));
		gtk_widget_show (GTK_WIDGET (extra_menu_item));
	}
}
/**
 * Creates the items that are useful in both the menus and the bm editor 
 **/
void
bookmarks_menu_create_base_extra_items (GtkMenu *menu, BookmarkItem *bookmark)
{
	GList *item;
	context_menu_add_item (menu,
			_("Add _bookmark here"),
			bookmarks_add_activate_cb, bookmark, NULL, TRUE);
	context_menu_add_item (menu,
			_("Add _folder here"),
			bookmarks_add_folder_activate_cb, bookmark, NULL,
			TRUE);
	context_menu_add_item (menu,
			_("_Open whole folder in tabs"),
			bookmarks_open_all_activate_cb, bookmark, NULL, TRUE);
	
	/* connect release signal for catching middle mousebutton for
	 * opening in new tab */
	item = g_list_nth (GTK_MENU_SHELL (menu)->children,
		           g_list_length (GTK_MENU_SHELL (menu)->children) - 1);
	gtk_signal_connect (GTK_OBJECT (item->data), "button-release-event",
			    GTK_SIGNAL_FUNC (bookmarks_open_all_release_cb),
			    bookmark);
	
	context_menu_add_item (menu,
			_("Open whole folder in _windows"),
			bookmarks_open_all_in_wins_activate_cb, bookmark, 
			NULL, TRUE);
	
	/* connect release signal for catching middle mousebutton for
	 * opening in new window */
	item = g_list_nth (GTK_MENU_SHELL (menu)->children,
		           g_list_length (GTK_MENU_SHELL (menu)->children) - 1);
	gtk_signal_connect (GTK_OBJECT (item->data), "button-release-event",
			    GTK_SIGNAL_FUNC
			   	(bookmarks_open_all_in_wins_release_cb),
			    bookmark);
	
	context_menu_add_item (menu, 
			_("Open folder in My _Portal"),
			bookmarks_open_in_myportal_cb, bookmark,
			NULL, TRUE);
	context_menu_add_check_item (menu,
			_("_Show as toolbar"),
			bookmarks_create_toolbar_activate_cb, bookmark,
			bookmark->create_toolbar);
	context_menu_add_item (menu,
			_("_Set as default folder"),
			bookmarks_set_as_default_activate_cb, bookmark,
			NULL, !(default_bookmarks_root == bookmark));
	if (bookmark->type == BM_AUTOBOOKMARKS)
	{
		context_menu_add_item (menu,
			_("Regenerate _autobookmarks"),
			bookmarks_regenerate_autobookmarks_cb, bookmark,
			NULL, TRUE);
	}
}

/**
 * Creates the context menuitems and appends them to menu
 **/
void
bookmarks_item_create_extra_items (GtkMenu *extra_menu, BookmarkItem *bookmark)
{
	g_return_if_fail (GTK_IS_MENU (extra_menu));

	bookmarks_item_create_base_extra_items (extra_menu, bookmark);
	context_menu_add_item (extra_menu, _("_Edit..."), 
			bookmarks_edit_activate_cb, bookmark,
			GNOME_STOCK_MENU_PROP, TRUE);
	context_menu_add_item (extra_menu, _("_Remove bookmark"),
			bookmarks_remove_activate_cb, bookmark,
			GNOME_STOCK_MENU_TRASH, TRUE);
}
/**
 * Creates the items that are useful in both the menus and the bm editor 
 **/
void
bookmarks_item_create_base_extra_items (GtkMenu *menu, BookmarkItem *bookmark)
{
	context_menu_add_item (menu, _("Open in a new window"),
			bookmarks_open_in_new_window_activate_cb,
			bookmark, GNOME_STOCK_MENU_NEW, TRUE);
	context_menu_add_item (menu, _("Open in a new tab"),
			bookmarks_open_in_new_tab_activate_cb, bookmark,
			GNOME_STOCK_MENU_NEW, TRUE);
	context_menu_add_item (menu, _("Copy link location"),
			bookmarks_copy_link_activate_cb, bookmark,
			GNOME_STOCK_MENU_COPY, TRUE);
}

/**
 * Creates the file submenu for a bookmark catergory that has subcategories.
 * This functions does not fill the submenu.
 */
static void
bookmarks_create_file_submenu (GtkMenuItem *file_menuitem,
			       BookmarkItem *bookmark)
{
	GtkWidget *w;
	GtkWidget *file_menu = gtk_menu_new();
	gchar *loc_str;
	gchar *stripped;

	loc_str = mozilla_utf8_to_locale (bookmark->name);
	stripped = strip_uline_accel (loc_str);
	g_free (loc_str);

	bookmarks_block_accel_update++;

	gtk_menu_set_title (GTK_MENU (file_menu), stripped);
	g_free(stripped);

	gtk_menu_item_set_submenu
		(GTK_MENU_ITEM (file_menuitem),
		 file_menu);

	w = bookmarks_menu_create_item (bookmark, GTK_MENU (file_menu), FALSE);
	gtk_signal_connect (GTK_OBJECT (w), "activate", 
			    bookmarks_file_bookmark_cb, bookmark);
	gtk_widget_show (w);
	gtk_menu_append (GTK_MENU (file_menu), w);
	
	w = gtk_menu_item_new ();
	gtk_widget_set_sensitive(w, FALSE);
	gtk_widget_show (w);
	gtk_menu_append (GTK_MENU (file_menu), w);

	if (gnome_preferences_get_menus_have_tearoff ()){
		GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
		gtk_menu_prepend (GTK_MENU (file_menu), tearoff);
		gtk_widget_show (tearoff);
	}

	bookmarks_block_accel_update--;

}

/* FIXME: obsoleted
 * These two functions need rewriting or just dumping them...
 * They are useful now just for dropping utls on the bookmark menu
 */
GtkMenu*
bookmarks_create_copy_menu (BookmarkItem *source)
{
	GtkMenu *res;
	res = bookmarks_create_copy_menu_recursively (source,
						      bookmarks_root);
	if (! res) {
		BookmarkItem *item = bookmarks_root;
		GtkWidget *menu_item = bookmarks_menu_create_item (item, res,
				  				   FALSE);
		res = GTK_MENU (gtk_menu_new ());
		gtk_widget_show (menu_item);
		gtk_menu_prepend (res, menu_item);
		gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
				    bookmarks_editor_bookmark_copy_cb, item);
		gtk_object_set_user_data (GTK_OBJECT (menu_item), 
					  source);
	} else {
		BookmarkItem *item = bookmarks_root;
		GtkWidget *menu_item = bookmarks_menu_create_item (item, res,
								   FALSE);
		GtkWidget *sep = gtk_menu_item_new ();
		gtk_widget_set_sensitive(sep, FALSE);
		gtk_widget_show (menu_item);
		gtk_widget_show (sep);
		gtk_menu_prepend (res, sep);
		gtk_menu_prepend (res, menu_item);
		gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
				    bookmarks_editor_bookmark_copy_cb, item);
		gtk_object_set_user_data (GTK_OBJECT (menu_item), 
					  source);
	}
	return res;
}

GtkMenu*
bookmarks_create_copy_menu_recursively (BookmarkItem *source, BookmarkItem *b)
{
	GtkMenu *res = GTK_MENU (gtk_menu_new ());
	GList *l;
	gboolean has_sub_categories = FALSE;
	g_assert (b->type == BM_FOLDER);
	for (l = b->list; l != NULL; l = g_list_next (l)) {
		BookmarkItem *item = l->data;
		GtkWidget *menu_item, *sub_menu_item, *sep;
		if (item->type == BM_FOLDER && !(item->alias_of)) {
			GtkMenu *submenu = bookmarks_create_copy_menu_recursively 
				(source, item);
			has_sub_categories = TRUE;
			sub_menu_item = bookmarks_menu_create_item (item, res, 
								    FALSE);
			if (submenu) {
				gtk_menu_item_set_submenu 
					(GTK_MENU_ITEM (sub_menu_item), 
					 GTK_WIDGET (submenu));
				menu_item = bookmarks_menu_create_item 
					(item, submenu, FALSE);
				gtk_widget_show (menu_item);
				sep = gtk_menu_item_new ();
				gtk_widget_set_sensitive(sep, FALSE);
				gtk_widget_show (sep);
				gtk_menu_prepend (submenu, sep);
				gtk_menu_prepend (submenu, menu_item);
				gtk_signal_connect 
					(GTK_OBJECT (menu_item),
					 "activate",
					 bookmarks_editor_bookmark_copy_cb, 
					 item);
				gtk_object_set_user_data 
					(GTK_OBJECT (menu_item), 
					 source);
			} else {
				gtk_signal_connect 
					(GTK_OBJECT (sub_menu_item), 
					 "activate", 
					 bookmarks_editor_bookmark_copy_cb,
					 item);
				gtk_object_set_user_data 
					(GTK_OBJECT (sub_menu_item), 
					 source);
			}
			gtk_widget_show (sub_menu_item);
			gtk_menu_append (res, sub_menu_item);
		}
		
	}
	if (has_sub_categories) {
		return res;
	} else {
		gtk_widget_destroy (GTK_WIDGET (res));
		return NULL;
	}
}

/**
 * Returns a menuitem with the appropiate icon
 * menu must be the menu in which the item will be placed, this is required
 * for uline accel creation.
 */
/* This may be improved */
static GtkWidget *
bookmarks_menu_create_item (BookmarkItem *bm, GtkMenu *menu,
			    gboolean lock_accels)
{
	gchar *loc_str;
	GtkWidget *pixmap;
	const PixmapData *bm_icon;
	
	GtkWidget *m = gtk_pixmap_menu_item_new ();
	GtkWidget *l;
	GtkWidget *hb = gtk_hbox_new (FALSE, 0);

	g_return_val_if_fail (bm->name != NULL, NULL);

	bookmarks_block_accel_update++;

	if (bm->pixmap_data == NULL || bm->pixmap_data->pixmap == NULL)
	{
		loc_str = mozilla_utf8_to_locale (bm->name);
		l = gtk_label_new ("");
		if (strlen (loc_str) > BOOKMARKS_MENU_MAX_LENGTH)
		{
			gchar *s, *t;

			s = g_strndup(loc_str, BOOKMARKS_MENU_MAX_LENGTH);
			t = g_strconcat (s, "...", NULL);
			label_set_accel_text (t, l, GTK_WIDGET (menu), m);
			g_free (t);
			g_free (s);
		} 
		else if (loc_str[0] != '\0')
		{
			label_set_accel_text
				(loc_str, l, GTK_WIDGET (menu), m);
		}
		else
		{
			label_set_accel_text (bm->url, l, GTK_WIDGET (menu), m);
		}
		gtk_box_pack_start (GTK_BOX (hb), l, FALSE, FALSE, 2);
		g_free (loc_str);
	}
	else
	{
		GtkWidget *image = gtk_pixmap_new (bm->pixmap_data->pixmap,
						   bm->pixmap_data->mask);
		gtk_box_pack_start (GTK_BOX (hb), GTK_WIDGET (image),
				    FALSE, FALSE, 2);
	}
	
	if (bm->type == BM_FOLDER || bm->type == BM_AUTOBOOKMARKS)
	{
		if (default_bookmarks_root == bm)
			pixmap = gtk_pixmap_new 
				(default_folder_pixmap_data->pixmap,
				 default_folder_pixmap_data->mask);
		else
			pixmap = gtk_pixmap_new (folder_pixmap_data->pixmap,
						 folder_pixmap_data->mask);
	}
	else
	{
		GtkWidget *accel_label;
		gchar *str;

		if (lock_accels == FALSE)
		{
			/* as GtkAccelLabels don't seem to work properly, we 
			 * use a standard label and our own string creation 
			 * function */
			accel_label = gtk_label_new ("");
			gtk_box_pack_end (GTK_BOX (hb), accel_label, FALSE,
					  FALSE, 2);

			/* set object data */
			gtk_object_set_data (GTK_OBJECT (m), "accel_label",
					     accel_label);
		
		
			/* connect accel signals */
			gtk_signal_connect_after (GTK_OBJECT (m), 
						  "add_accelerator",
					          bookmarks_add_accel_cb, bm);
			gtk_signal_connect (GTK_OBJECT (m), 
					    "remove_accelerator",
					    bookmarks_remove_accel_cb, bm);
			gtk_signal_connect (GTK_OBJECT (m), "select",
					    bookmarks_ensure_accel_cb, bm);
		
			/* add accelerator */
			gtk_widget_add_accelerator (m, "activate",
						    menu->accel_group, 
						    bm->accel_key, 
						    bm->accel_mods, 
						    GTK_ACCEL_VISIBLE);
			
			str = string_from_accel_data 
				(m, bm->accel_key, bm->accel_mods);
			gtk_label_set_text (GTK_LABEL (accel_label), str);
			g_free (str);
		}
		else
		{
			gtk_widget_lock_accelerators (m);
		}

		bm_icon = bookmarks_get_siteicon (bm->url);
		pixmap = gtk_pixmap_new (bm_icon->pixmap, bm_icon->mask);
	}

	gtk_object_set_data (GTK_OBJECT (m), "bookmark", bm);

	gtk_widget_show_all (hb);
	gtk_container_add (GTK_CONTAINER (m), hb);

	gtk_pixmap_menu_item_set_pixmap (GTK_PIXMAP_MENU_ITEM (m), pixmap);
	if (gnome_preferences_get_menus_have_icons ())
		gtk_widget_show (pixmap);

	bookmarks_block_accel_update--;

	return m;
}

/**
 * bookmarks_smart_bm_dialog: prompt user to complete the URL
 */
void
bookmarks_smart_bm_dialog (GaleonEmbed *embed, GaleonEmbed **newembed,
			   BookmarkItem *bi, LinkState state)
{	
	gchar *loc_str;
	GtkWidget *dialog;
	gchar *s = bi->url, *strippedname;
	int num_parameters = 0;
	int dlg_status = 0;

	g_return_if_fail (embed != NULL);
	
	loc_str = bi->name;
	strippedname = strip_uline_accel (loc_str);
	dialog = gnome_dialog_new (strippedname,
				   GNOME_STOCK_BUTTON_OK,
				   GNOME_STOCK_BUTTON_CANCEL,
				   NULL);
	gnome_dialog_set_default (GNOME_DIALOG(dialog), 0);
	
	if (bi->pixmap_data == NULL)
	{
		GtkWidget *label;
		label = gtk_label_new (strippedname);
		gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), 
				    label, TRUE, TRUE, 0);
	}
	else
	{
		GtkWidget *image = gtk_pixmap_new (bi->pixmap_data->pixmap,
						   bi->pixmap_data->mask);
		gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), 
				    GTK_WIDGET (image), TRUE, FALSE, 0);
	}
	g_free (strippedname);

	gtk_window_set_wmclass (GTK_WINDOW (dialog), "bookmarks_getstring",
				"galeon_browser");
	
	/* count %s in the URL */
	s = bi->url;
	while ((s = strstr(s, "%s"))) {
		++num_parameters;
		++s;
	}

	if (num_parameters == 1)
	{
		dlg_status = bookmarks_smart_bm_dialog_single (dialog, embed,
							newembed, bi, state);
	}
	else if (num_parameters > 1)
	{
		dlg_status = bookmarks_smart_bm_dialog_multi (dialog, embed,
							newembed, bi, state);
	}
	if (dlg_status != -1)
		gnome_dialog_close (GNOME_DIALOG (dialog));
}

/**
 * bookmarks_smart_bm_dialog_single: prompt user to resolve smart bookmark with
 * exactly one parameter
 * Returns: return value of the dialog 
 */
static gint
bookmarks_smart_bm_dialog_single (GtkWidget *dialog, GaleonEmbed *embed, 
				  GaleonEmbed **newembed, BookmarkItem *bi,
				  LinkState state)
{
	GtkWidget *entry;
	gchar *s, *tmp, *result, *text, *translated_text;
	gint dlg_status;

	entry = gtk_entry_new ();
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), 
			    entry, TRUE, TRUE, 0);
	
	gtk_widget_grab_focus (GTK_WIDGET (entry));
			    
	gnome_dialog_editable_enters (GNOME_DIALOG(dialog),
				      GTK_EDITABLE(entry));
	gtk_widget_show_all (dialog);

	/* set parent and level in case of fulscreeen */
	dialog_set_parent (dialog, embed->parent_window->WMain);

	if ((dlg_status = dialog_run (GNOME_DIALOG (dialog), FALSE)) == 0) {
		s = strstr (bi->url, "%s");
		g_return_val_if_fail (s, 0);

		tmp = g_strndup (bi->url, s - bi->url);
		text = gtk_entry_get_text (GTK_ENTRY (entry));
		translated_text = bookmarks_translate_string (text);
		result = g_strconcat (tmp, translated_text, s+2, NULL);

		embed_activate_link (embed, newembed, result, state);

		g_free (tmp);
		g_free (translated_text);
		g_free (result);
	}
	return dlg_status;
}

/**
 * bookmarks_smart_bm_dialog_multi: prompt user to resolve smart bookmark with
 * 2 or more parameters
 * Returns: return value of the dialog
 */
static gint
bookmarks_smart_bm_dialog_multi (GtkWidget *dialog, GaleonEmbed *embed, 
				 GaleonEmbed **newembed, BookmarkItem *bi,
				 LinkState state)
{
	gchar *start, *point, *tmp, *result;
	int i, dlg_status;
	GtkWidget *seperator, *entry, *label;
	GPtrArray *entries;

	seperator = gtk_hseparator_new();
	gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), 
			    seperator, TRUE, TRUE, 0);

	entries = g_ptr_array_new();

	start = bi->url;
	while(1)
	{
		point = strstr(start, "%s");
		if (point == start)
		{
			/* user-input part of URL */
			entry = gtk_entry_new ();
			gtk_box_pack_start
					(GTK_BOX (GNOME_DIALOG (dialog)->vbox),
					 entry, TRUE, TRUE, 0);
			g_ptr_array_add(entries, (gpointer) entry);
			start = point + 2;
			continue;
		}
		if (point == NULL)
			point = start + strlen(start);
		if (point == start)
			break;
		
		/* static part of URL */
		tmp = g_strndup(start, point-start);
		label = gtk_label_new (tmp);
		g_free(tmp);
		gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox), 
				    label, TRUE, TRUE, 0);
		g_ptr_array_add(entries, (gpointer) label);
		start = point;
	}

	/* set focus on the first entry */
	for (i = 0; i < entries->len; ++i) {
		if (GTK_IS_ENTRY (entries->pdata[i]))
		{
			gtk_widget_grab_focus (GTK_WIDGET (entries->pdata[i]));
			break;
		}
	}

	/* activating (hitting enter) on the last entry should select "OK" */
	for (i = entries->len - 1; i >= 0; --i) {
		if (GTK_IS_ENTRY (entries->pdata[i]))
		{
			gnome_dialog_editable_enters
				(GNOME_DIALOG(dialog), 
				 GTK_EDITABLE(entries->pdata[i]));
			break;
		}
	}

	gtk_widget_show_all (dialog);

	/* set parent and level in case of fulscreeen */
	dialog_set_parent (dialog, embed->parent_window->WMain);

	if ((dlg_status = dialog_run (GNOME_DIALOG (dialog), FALSE)) == 0)
	{
		/* user selected OK, construct URL and go there */
		result = g_strnfill(0,0);
		for (i = 0; i < entries->len; ++i)
		{
			gchar *text = NULL, *translated_text = NULL;

			if (GTK_IS_ENTRY (entries->pdata[i]))
			{
				text = gtk_entry_get_text 
					(GTK_ENTRY (entries->pdata[i]));
				translated_text = bookmarks_translate_string
						   (text);
				tmp = g_strconcat (result, translated_text,
						   NULL);
				g_free (translated_text);
			}
			else
			{
				gtk_label_get 
					(GTK_LABEL (entries->pdata[i]), &text);
				tmp = g_strconcat (result, text, NULL);
			}
			g_free (result);
			result = tmp;
		}

		embed_activate_link (embed, newembed, result, state);

		g_free(result);
	}
	g_ptr_array_free (entries, FALSE);
	return dlg_status;
}

/**
 * bookmarks_init_menu_accels_recursively: recursively add accelerators
 * for menuitems. We use a dummy widget as the real menus are created
 * on the fly, not on startup.
 */
static void
bookmarks_init_menu_accels_recursively (BookmarkItem *b, GaleonWindow *window)
{
	GList *item;
	GtkWidget *dummy;
 
	if (b->alias_of)
		return; /* the real one should have been added already */
	
	switch (b->type)
	{
	case BM_FOLDER:
	case BM_AUTOBOOKMARKS:
		for (item = b->list; item != NULL; item = item->next)
		{
			bookmarks_init_menu_accels_recursively (item->data,
								window);
		}
		break;

	case BM_SITE:  
		if (!((b->accel_key == GDK_VoidSymbol) || (b->accel_key == 0)))
		{
			dummy = gtk_menu_item_new ();
			
			window->dummy_widgets =
				g_list_append (window->dummy_widgets, dummy);
			gtk_widget_ref (dummy);
			gtk_object_set_data 
				(GTK_OBJECT (dummy), "GaleonWindow", 
				 window);
			gtk_object_set_data 
				(GTK_OBJECT (dummy), "bookmark", b);
			
			gtk_signal_connect (GTK_OBJECT (dummy), "activate",
					    GTK_SIGNAL_FUNC 
					    (bookmarks_activate_cb), b);
			
			gtk_widget_add_accelerator 
				(dummy, "activate",
				 GNOME_APP (window->WMain)->accel_group,
				 b->accel_key, b->accel_mods,
				 GTK_ACCEL_VISIBLE);

			gtk_signal_connect (GTK_OBJECT (dummy), 
					    "remove_accelerator",
					    bookmarks_remove_dummy_accel_cb,
					    b);
		}
		break;

	case BM_SEPARATOR:
		break;
	}
}

/**
 * Updates the accel labels of the current window
 * The menus of other windows are recreated
 */
void 
bookmarks_menu_sync_accel_labels (void)
{
	GList *li;

	bookmarks_block_accel_update++;
	for (li = all_windows; li != NULL; li = li->next)
	{
		GaleonWindow *window = (GaleonWindow *)(li->data);
		GtkMenuShell *menu;

		menu = GTK_MENU_SHELL (window->bookmarks_menu);

		if (!menu)
			continue;

		if (menu->active)
		{
			/* this menu is currently being browsed */
			GList *lj, *l;
			GtkWidget *separator;
	
			separator = window->bookmarks_separator;

			l = gtk_container_children (GTK_CONTAINER (menu));
	
			/* look for the separator */
			for (lj = l; lj != NULL; lj = lj->next)
			{
				if (lj->data == separator)
					break;
			}
	
			/* update the submenu of every widget after it */
			for (lj = lj->next; lj != NULL; lj = lj->next)
				if (GTK_IS_MENU_ITEM (lj->data))
					bookmarks_menu_sync_accel_labels_item
						(lj->data);
			
			g_list_free (l);
		}
		else
		{
			/* we can safely recreate it */
			bookmarks_menu_recreate (window);
		}
	}
	bookmarks_block_accel_update--;
}

static void
bookmarks_menu_sync_accel_labels_item (GtkMenuItem *item)
{
	GList *l, *li;
	GtkWidget *submenu = item->submenu;
	BookmarkItem *bm;
	bm = gtk_object_get_data (GTK_OBJECT (item), "bookmark");

	if (bm == NULL)
		/* this is not a bookmark menu item */
		return;
	
	if (!submenu) 
	{
		/* does not have submenus to clean */
		/* resync the label ... */
		GtkWidget *l;
		gchar *str;
		
		l = gtk_object_get_data (GTK_OBJECT (item), "accel_label");
		if (l == NULL)
			/* nothing to update */
			return;
		
		str = string_from_accel_data (GTK_WIDGET (item), bm->accel_key, 
					      bm->accel_mods);
		gtk_label_set_text (GTK_LABEL (l), str);
		g_free (str);
	
		return;
	}

	l = gtk_container_children (GTK_CONTAINER (submenu));
	for (li = l; li != NULL; li = li->next)
		if (GTK_IS_MENU_ITEM (li->data))
			bookmarks_menu_sync_accel_labels_item (li->data);
	g_list_free (l);
}
