/*
 *  Copyright (C) 2002  Ricardo Fernández Pascual
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

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

#include "bookmarks-gtk-menu-item.h"
#include "bookmarks-gtk-menu.h"
#include "gul-gobject-misc.h"
#include "galeon-marshal.h"
#include "bookmarks-dnd.h"
#include "gul-string.h"
#include "galeon-debug.h"
#include "bookmarks-context-menu.h"

#include <glib/gi18n.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkaccellabel.h>
#include <gtk/gtktooltips.h>
#include <string.h>

/**
 * Private data
 */
struct _GbGtkMenuItemPrivate {
	GbBookmark *bookmark;
	GbGtkMenu *submenu;
	guint rebuild_timeout;
	GbLocationSource *location_source;

	gboolean ignore_next_activation : 1; /* little hack */
};

#define REBUILD_TIMEOUT 500

/**
 * Private functions, only availble from this file
 */
static void		gb_gtk_menu_item_class_init			(GbGtkMenuItemClass *klass);
static void		gb_gtk_menu_item_init				(GbGtkMenuItem *e);
static void		gb_gtk_menu_item_finalize_impl			(GObject *o);
static void		gb_gtk_menu_item_modied_cb			(GbBookmark *bm, GbGtkMenuItem *gm);
static void		gb_gtk_menu_item_rebuild			(GbGtkMenuItem *gm);
static void 		gb_gtk_menu_item_rebuild_with_timeout		(GbGtkMenuItem *gm);
static gboolean		gb_gtk_menu_item_rebuild_timeout_cb		(gpointer data);
static void		gb_gtk_menu_item_activate_cb			(GbGtkMenuItem *bmi);
static gboolean		gb_gtk_menu_item_button_press_cb		(GbGtkMenuItem *bmi, GdkEventButton *event);
static void		gb_gtk_menu_item_menu_event_after_cb		(GtkWidget *wid, GdkEventAny *event, 
									 GbGtkMenuItem *bmi);
static void		gb_gtk_menu_item_bookmark_activated_cb	 	(GObject *sender,
									 GbBookmarkEventActivated *ev,
									 GbGtkMenuItem *bmi);
static void             gb_gtk_menu_item_style_set_impl                 (GtkWidget *widget,
									 GtkStyle *previous_style);

static gpointer gtk_image_menu_item_class;

enum GbGtkMenuItemSignalsEnum {
	GB_GTK_MENU_ITEM_BOOKMARK_ACTIVATED,
	GB_GTK_MENU_ITEM_LAST_SIGNAL
};
static gint GbGtkMenuItemSignals[GB_GTK_MENU_ITEM_LAST_SIGNAL];
static GtkTooltips *_gb_gtk_menu_item_tooltips = NULL;

/**
 * GtkMenu object
 */

MAKE_GET_TYPE (gb_gtk_menu_item, "GbGtkMenuItem", GbGtkMenuItem, gb_gtk_menu_item_class_init,
	       gb_gtk_menu_item_init, GTK_TYPE_IMAGE_MENU_ITEM);

static void
gb_gtk_menu_item_class_init (GbGtkMenuItemClass *klass)
{
	G_OBJECT_CLASS (klass)->finalize = gb_gtk_menu_item_finalize_impl;
	gtk_image_menu_item_class = g_type_class_peek_parent (klass);

	GTK_WIDGET_CLASS (klass)->style_set = gb_gtk_menu_item_style_set_impl;

	GbGtkMenuItemSignals[GB_GTK_MENU_ITEM_BOOKMARK_ACTIVATED] = g_signal_new (
		"bookmark-activated", G_OBJECT_CLASS_TYPE (klass),  
		G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
                G_STRUCT_OFFSET (GbGtkMenuItemClass, gb_gtk_menu_item_bookmark_activated), 
		NULL, NULL, 
		g_cclosure_marshal_VOID__POINTER,
		G_TYPE_NONE, 1, G_TYPE_POINTER);

}

static void 
gb_gtk_menu_item_init (GbGtkMenuItem *m)
{
	GbGtkMenuItemPrivate *p = g_new0 (GbGtkMenuItemPrivate, 1);
	m->priv = p;

	if (!_gb_gtk_menu_item_tooltips)
	{
		_gb_gtk_menu_item_tooltips = gtk_tooltips_new ();
		g_object_add_weak_pointer (G_OBJECT (_gb_gtk_menu_item_tooltips),
					   (gpointer)&_gb_gtk_menu_item_tooltips);
	}
	g_object_ref (_gb_gtk_menu_item_tooltips);
	gtk_object_sink (GTK_OBJECT(_gb_gtk_menu_item_tooltips));
}

static void
gb_gtk_menu_item_finalize_impl (GObject *o)
{
	GbGtkMenuItem *gm = GB_GTK_MENU_ITEM (o);
	GbGtkMenuItemPrivate *p = gm->priv;

	LOG ("Finalizing GbGtkMenuItem");

	gb_gtk_menu_item_set_location_source (gm, NULL);
	g_signal_handlers_disconnect_matched (p->bookmark, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, gm);

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}

	if (p->submenu)
	{
		g_object_unref (p->submenu);
	}

	g_object_unref (G_OBJECT (p->bookmark));
	g_free (p);

	g_object_unref (_gb_gtk_menu_item_tooltips);

	G_OBJECT_CLASS (gtk_image_menu_item_class)->finalize (o);
}

static void    
gb_gtk_menu_item_style_set_impl (GtkWidget *widget,
				 GtkStyle *previous_style)
{
	/* Hack courtesy of Owen, see bug 111560 */
	GtkStyle* style = gtk_rc_get_style_by_paths (gtk_widget_get_settings (widget),
						     "GtkWindow.GtkImageMenuItem.GtkLabel",
						     "GtkWindow.GtkImageMenuItem.GtkLabel",
						     GTK_TYPE_LABEL);
	
	gtk_widget_set_style (GTK_BIN (widget)->child, style);

	if (GTK_WIDGET_CLASS (gtk_image_menu_item_class)->style_set)
		GTK_WIDGET_CLASS (gtk_image_menu_item_class)->style_set (widget, previous_style);
}

GbGtkMenuItem *
gb_gtk_menu_item_new (GbBookmark *bookmark)
{
	GbGtkMenuItem *ret = g_object_new (GB_TYPE_GTK_MENU_ITEM, NULL);
	GbGtkMenuItemPrivate *p = ret->priv;

	p->bookmark = bookmark;
	g_object_ref (bookmark);

	g_signal_connect (bookmark, "modified", 
			  G_CALLBACK (gb_gtk_menu_item_modied_cb), ret);
	g_signal_connect (ret, "activate",
			  G_CALLBACK (gb_gtk_menu_item_activate_cb), NULL);

	if (GB_IS_SITE (bookmark))
	{
		g_signal_connect (ret, "button_press_event", 
				  G_CALLBACK (gb_gtk_menu_item_button_press_cb), NULL);

		gb_bookmark_dnd_drag_source_set (GTK_WIDGET(ret), bookmark);
	}

	gb_gtk_menu_item_rebuild (ret);
	return ret;
}

static void
gb_gtk_menu_item_set_tooltip (GbGtkMenuItem *bmi, GbBookmark *bm, const char *visible_name)
{
	char *parts[2+1] = {NULL};
	int n = 0;

	if (strcmp (visible_name, bm->name) != 0)
	{
		/* truncated for menu, show full name */
		parts[n++] = bm->name;
	}

	if (bm->notes && bm->notes[0] != '\0')
	{
		parts[n++] = bm->notes;
	}

	if (n > 0)
	{
		char *tooltip;

		tooltip = g_strjoinv("\n\n", parts);
		gtk_tooltips_set_tip (_gb_gtk_menu_item_tooltips,
				      GTK_WIDGET(bmi),
				      tooltip, NULL);
		g_free (tooltip);
	}
}

static void
gb_gtk_menu_item_build_menuitem (GbGtkMenuItem *bmi)
{
	GbGtkMenuItemPrivate *p = bmi->priv;
	GbBookmark *bm = p->bookmark;
	gchar *name = gul_string_shorten (GB_BOOKMARK (bm)->name, GB_GTK_MENU_ITEM_NAME_MAX_LENGTH);
	GdkPixbuf *pixbuf = gb_bookmark_get_icon (bm);
	
	if (GTK_BIN (bmi)->child)
	{
		gtk_label_set_text (GTK_LABEL (GTK_BIN (bmi)->child), name);
		
	}
	else
	{
		GtkWidget *accel_label;
		accel_label = gtk_accel_label_new (name);
		gtk_misc_set_alignment (GTK_MISC (accel_label), 0.0, 0.5);
		gtk_container_add (GTK_CONTAINER (bmi), accel_label);
		gtk_accel_label_set_accel_widget (GTK_ACCEL_LABEL (accel_label), GTK_WIDGET (bmi));
		gtk_widget_show (accel_label);
	}

	gb_gtk_menu_item_set_tooltip (bmi, bm, name);
	g_free (name);
	
	if (pixbuf)
	{
		GtkWidget *image = gtk_image_new_from_pixbuf (pixbuf);
		gtk_widget_show (image);
		g_object_unref (G_OBJECT (pixbuf));
		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (bmi), image);
	}
	
	if (GB_IS_FOLDER (bm) && !gtk_menu_item_get_submenu (GTK_MENU_ITEM (bmi)))
	{
		GtkWidget *menu = gtk_menu_new ();
		gtk_menu_item_set_submenu (GTK_MENU_ITEM (bmi), menu);
		g_signal_connect (menu, "event-after", G_CALLBACK (gb_gtk_menu_item_menu_event_after_cb), bmi);
	}
}

void 
gb_gtk_menu_item_fill_submenu (GbGtkMenuItem *bmi, gboolean children)
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	if (GB_IS_FOLDER (p->bookmark) && !p->submenu)
	{
		GtkWidget *menu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (bmi));

		g_return_if_fail (GTK_IS_MENU_SHELL (menu));
		
		p->submenu = gb_gtk_menu_new (GB_FOLDER (p->bookmark), GTK_MENU_SHELL (menu));
		gb_gtk_menu_set_location_source (p->submenu, p->location_source);
		
		g_signal_connect (p->submenu, "bookmark-activated", 
				  G_CALLBACK (gb_gtk_menu_item_bookmark_activated_cb), bmi);
	}

	if (children && p->submenu)
	{
		gb_gtk_menu_fill_children_submenus (p->submenu);
	}
}

static void
gb_gtk_menu_item_popup_deactivated_cb (GbContextMenu *cm, GbGtkMenuItem *bmi)
{
	if (GTK_IS_MENU_SHELL (GTK_WIDGET (bmi)->parent))
	{
		gtk_menu_shell_deactivate (GTK_MENU_SHELL (GTK_WIDGET (bmi)->parent));
	}
}

void
gb_gtk_menu_item_set_location_source (GbGtkMenuItem *bmi, GbLocationSource *src)
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	if (p->location_source)
	{
		g_object_remove_weak_pointer (G_OBJECT (p->location_source),
					      (gpointer *) &p->location_source);
	}

	p->location_source = src;

	if (p->location_source)
	{
		g_object_add_weak_pointer (G_OBJECT (p->location_source), 
					   (gpointer *) &p->location_source);
	}

	if (p->submenu)
	{
		gb_gtk_menu_set_location_source (p->submenu, src);
	}
}

static gboolean
gb_gtk_menu_item_rebuild_timeout_cb (gpointer data)
{
	GbGtkMenuItem *bmi = data;
	GbGtkMenuItemPrivate *p = bmi->priv;
	LOG ("GbGtkMenuItem rebuild timeout");

	p->rebuild_timeout = 0;

	gb_gtk_menu_item_rebuild (bmi);
	return FALSE;
}

static void 
gb_gtk_menu_item_rebuild_with_timeout (GbGtkMenuItem *bmi) 
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	if (p->rebuild_timeout)
	{
		g_source_remove (p->rebuild_timeout);
	}
	
	p->rebuild_timeout = g_timeout_add (REBUILD_TIMEOUT, 
					    gb_gtk_menu_item_rebuild_timeout_cb, bmi);
}

static void
gb_gtk_menu_item_rebuild (GbGtkMenuItem *bmi)
{
	LOG ("gb_gtk_menu_item_rebuild called");
	gb_gtk_menu_item_build_menuitem (bmi);
}

static void
gb_gtk_menu_item_modied_cb (GbBookmark *bm, GbGtkMenuItem *bmi)
{
	gb_gtk_menu_item_rebuild_with_timeout (bmi);
}

static void
gb_gtk_menu_item_activate_cb (GbGtkMenuItem *bmi)
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	gb_gtk_menu_item_fill_submenu (bmi, TRUE);

	if (p->ignore_next_activation)
	{
		p->ignore_next_activation = FALSE;
		return;
	}
	
	if (GB_IS_SITE (p->bookmark))
	{
		gb_activated_emit (bmi, GbGtkMenuItemSignals[GB_GTK_MENU_ITEM_BOOKMARK_ACTIVATED],
			           p->bookmark, GB_SITE (p->bookmark)->url, GB_BAF_DEFAULT);
	}
}

static void
gb_gtk_menu_item_bookmark_activated_cb (GObject *sender,
					GbBookmarkEventActivated *ev,
					GbGtkMenuItem *bmi)
{
	g_signal_emit (bmi, GbGtkMenuItemSignals[GB_GTK_MENU_ITEM_BOOKMARK_ACTIVATED], 0, ev);
}

static gboolean
gb_gtk_menu_item_button_press_cb (GbGtkMenuItem *bmi, GdkEventButton *event)
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	if (event->button == 3)
	{
		GbContextMenu *cm = gb_context_menu_new_quick (p->bookmark, p->location_source, G_OBJECT (bmi));
		g_signal_connect (cm, "deactivated", G_CALLBACK (gb_gtk_menu_item_popup_deactivated_cb), bmi);
		gb_context_menu_popup (cm, event);
		g_object_unref (cm);
		return TRUE;
	}
	else if (event->button == 2)
	{
		const gchar *url = GB_IS_SITE (p->bookmark) ? GB_SITE (p->bookmark)->url : NULL;

		gtk_menu_shell_deactivate (GTK_MENU_SHELL (GTK_WIDGET (bmi)->parent));

		gb_activated_event (bmi, p->bookmark, url, GB_BAF_NEW_TAB_OR_WINDOW, (GdkEvent *) event);

		/* avoid activating this bookmark */
		p->ignore_next_activation = TRUE;

		return TRUE;
	}
	return FALSE;
}

static void
gb_gtk_menu_item_popdown_menus (GbGtkMenuItem *wid)
{
	GbGtkMenuItemPrivate *p = wid->priv;
	GtkWidget *parent = GTK_WIDGET (wid)->parent;
	if (parent && GTK_IS_MENU_SHELL (parent))
	{
		/* avoid activating this bookmark */
		p->ignore_next_activation = TRUE;
		gtk_menu_shell_activate_item (GTK_MENU_SHELL (parent), GTK_WIDGET (wid), TRUE);
	}
}

static void
gb_gtk_menu_item_menu_event_after_cb (GtkWidget *wid, GdkEventAny *event, 
				      GbGtkMenuItem *bmi)
{
	GbGtkMenuItemPrivate *p = bmi->priv;

	if (event->type == GDK_BUTTON_PRESS
	    && (((GdkEventButton *) event)->button == 3)
	    /* this is a bit hackish, avoids popping more than one menu */
	    && !GTK_MENU_SHELL (wid)->active_menu_item) 
	{
		GbContextMenu *cm = gb_context_menu_new_quick (p->bookmark, p->location_source, G_OBJECT (bmi));
		g_signal_connect (cm, "deactivated", G_CALLBACK (gb_gtk_menu_item_popup_deactivated_cb), bmi);
		gb_context_menu_popup (cm, (GdkEventButton *) event);
		g_object_unref (cm);
	}
	else if (event->type == GDK_BUTTON_PRESS
		 && (((GdkEventButton *) event)->button == 2)
		/* this is a bit hackish, avoids opening parent folders */
		 && !GTK_MENU_SHELL (wid)->active_menu_item)
	{
		const gchar *url = GB_IS_SITE (p->bookmark) ? GB_SITE (p->bookmark)->url : NULL;
		
		gb_gtk_menu_item_popdown_menus (bmi);
		gb_activated_event (bmi, p->bookmark, url, GB_BAF_NEW_TAB_OR_WINDOW, (GdkEvent *) event);
	}
}

