/*
 *  Copyright (C) 2003  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 "gul-toolbutton.h"
#include "gul-gui.h"
#include "galeon-marshal.h"
#include "galeon-debug.h"

#include <gtk/gtkarrow.h>
#include <gtk/gtkmain.h>
#include <gtk/gtkmenu.h>
#include <gtk/gtkhbox.h>
#include <gdk/gdkkeysyms.h>
#include <glib/gi18n.h>

/**
 * Private data
 */
struct _GulToolbuttonPrivate
{
	GtkWidget *menu;
	GtkWidget *arrow_widget;
	GtkWidget *button;
	gboolean show_arrow;
};

enum {
	PROP_0,
	PROP_SHOW_ARROW,
};


enum GulToolbuttonSignalsEnum {
	GUL_TOOLBUTTON_MENU_ACTIVATED,
	GUL_TOOLBUTTON_LAST_SIGNAL
};
static gint GulToolbuttonSignals[GUL_TOOLBUTTON_LAST_SIGNAL];

static GObjectClass *parent_class = NULL;



static void 
popup_menu_under_arrow (GulToolbutton *b, GdkEventButton *event)
{
	GulToolbuttonPrivate *p = b->priv;
	GtkMenuShell *menu = gul_toolbutton_get_menu (GUL_TOOLBUTTON (b));
			
	if (menu)
	{
		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (p->arrow_widget), TRUE);
		g_signal_emit (b, GulToolbuttonSignals[GUL_TOOLBUTTON_MENU_ACTIVATED], 0);
		gtk_menu_popup (GTK_MENU (menu), NULL, NULL, gul_gui_menu_position_under_widget, 
				p->arrow_widget, 
				event ? event->button : 0, 
				event ? event->time : gtk_get_current_event_time ());
	}
}

static void
menu_deactivated_cb (GtkMenuShell *ms, GulToolbutton *b)
{
	GulToolbuttonPrivate *p = b->priv;
	
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (p->arrow_widget), FALSE);
}

static gboolean
arrow_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, GulToolbutton *b)
{
	popup_menu_under_arrow (b, event);
	return TRUE;
}

static gboolean
arrow_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, GulToolbutton *b)
{
	if (event->keyval == GDK_space
	    || event->keyval == GDK_KP_Space
	    || event->keyval == GDK_Return
	    || event->keyval == GDK_KP_Enter
	    || event->keyval == GDK_Menu)
	{
		popup_menu_under_arrow (b, NULL);
	}

	return FALSE;
}

static void
state_changed_cb (GtkWidget *widget, GtkStateType previous_state,
		  GulToolbutton *b)
{
	GulToolbuttonPrivate *p = b->priv;
	GtkWidget *button;
	GtkStateType state = GTK_WIDGET_STATE (widget);

	if (!p->arrow_widget) return;

	button = (widget == p->arrow_widget) ? p->button : p->arrow_widget;

	g_signal_handlers_block_by_func
		         (G_OBJECT (button),
		          G_CALLBACK (state_changed_cb), b);
	
	if (state == GTK_STATE_PRELIGHT &&
	    previous_state != GTK_STATE_ACTIVE)
	{
		gtk_widget_set_state (button, state);
	}
	else if (state == GTK_STATE_NORMAL)
	{
		 gtk_widget_set_state (button, state);
	}
	else if (state == GTK_STATE_ACTIVE)
	{
		gtk_widget_set_state (button, GTK_STATE_NORMAL);
	}

	g_signal_handlers_unblock_by_func
		         (G_OBJECT (button),
		          G_CALLBACK (state_changed_cb), b);
}


static gboolean
button_button_press_event_cb (GtkWidget *widget, GdkEventButton *event, 
			      GulToolbutton *b)
{
	GulToolbuttonPrivate *p = b->priv;

	if (event->button == 3 && p->menu)
	{
		g_signal_emit (b, GulToolbuttonSignals[GUL_TOOLBUTTON_MENU_ACTIVATED], 0);
		gtk_menu_popup (GTK_MENU (p->menu), NULL, NULL, NULL, b, 
				event ? event->button : 0, 
				event ? event->time : gtk_get_current_event_time ());
		return TRUE;
	}
	
	return FALSE;
}

static void 
button_button_popup_menu_cb (GtkWidget *w, GulToolbutton *b)
{
	GulToolbuttonPrivate *p = b->priv;

	if (p->menu)
	{
		g_signal_emit (b, GulToolbuttonSignals[GUL_TOOLBUTTON_MENU_ACTIVATED], 0);
		gtk_menu_popup (GTK_MENU (p->menu), NULL, NULL, 
				gul_gui_menu_position_under_widget, b, 0,
				gtk_get_current_event_time ());
	}
}

static gboolean
gul_toolbutton_set_tooltip (GtkToolItem *tool_item,
			    GtkTooltips *tooltips,
			    const gchar *tip_text,
			    const gchar *tip_private)
{
	GulToolbuttonPrivate *p = GUL_TOOLBUTTON (tool_item)->priv;
	GtkWidget *button = gul_toolbutton_get_button (GUL_TOOLBUTTON (tool_item));
	
	gtk_tooltips_set_tip (tooltips, button, tip_text, tip_private);
	if (p->arrow_widget)
	{
		gtk_tooltips_set_tip (tooltips, p->arrow_widget, tip_text, tip_private);
	}
	
	return TRUE;
}

static void
gul_toolbutton_build (GulToolbutton *button)
{
	GulToolbuttonPrivate *p = button->priv;
	GtkWidget *hbox;
	GtkWidget *arrow;
	GtkWidget *arrow_button;
	GtkWidget *real_button;
	GtkMenuShell *menu;

	p->menu = gtk_menu_new ();
	g_object_ref (p->menu);
	gtk_object_sink (GTK_OBJECT (p->menu));

	hbox = gtk_hbox_new (FALSE, 0);
	gtk_widget_show (hbox);
	real_button = GTK_BIN (button)->child;
	g_object_ref (real_button);
	gtk_container_remove (GTK_CONTAINER (button), real_button);
	gtk_container_add (GTK_CONTAINER (hbox), real_button);
	gtk_container_add (GTK_CONTAINER (button), hbox);
	g_object_unref (real_button);

	arrow_button = gtk_toggle_button_new ();
	if (p->show_arrow)
		gtk_widget_show (arrow_button);

	arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
	gtk_widget_show (arrow);
	gtk_button_set_relief (GTK_BUTTON (arrow_button), GTK_RELIEF_NONE);
	gtk_container_add (GTK_CONTAINER (arrow_button), arrow);

	gtk_box_pack_end (GTK_BOX (hbox), arrow_button,
			  FALSE, FALSE, 0);

	p->button = real_button;
	p->arrow_widget = arrow_button;

	menu = gul_toolbutton_get_menu (GUL_TOOLBUTTON (button));

	g_signal_connect (menu, "deactivate",
			  G_CALLBACK (menu_deactivated_cb), button);

	g_signal_connect (real_button, "state_changed",
			  G_CALLBACK (state_changed_cb),
			  button);
	g_signal_connect (arrow_button, "state_changed",
			  G_CALLBACK (state_changed_cb),
			  button);
	g_signal_connect (arrow_button, "key_press_event",
			  G_CALLBACK (arrow_key_press_event_cb),
			  button);
	g_signal_connect (arrow_button, "button_press_event",
			  G_CALLBACK (arrow_button_press_event_cb),
			  button);
	g_signal_connect (GTK_WIDGET (real_button), "button-press-event",
			  G_CALLBACK (button_button_press_event_cb),
			  button);
	g_signal_connect (GTK_WIDGET (real_button), "popup-menu",
			  G_CALLBACK (button_button_popup_menu_cb),
			  button);
}

static void
gul_toolbutton_init (GulToolbutton *button)
{
	GulToolbuttonPrivate *p = g_new0 (GulToolbuttonPrivate, 1);
	button->priv = p;

	
	gtk_tool_item_set_homogeneous (GTK_TOOL_ITEM (button), FALSE);
	gul_toolbutton_build (button);
}


static void
gul_toolbutton_finalize_impl (GObject *o)
{
	GulToolbutton *b = GUL_TOOLBUTTON (o);
	GulToolbuttonPrivate *p = b->priv;

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

	g_free (p);
	
	LOG ("GulToolbutton finalized");
	
	G_OBJECT_CLASS (parent_class)->finalize (o);
}

static void
gul_toolbutton_set_property (GObject *object,
			    guint prop_id,
			    const GValue *value,
			    GParamSpec *pspec)
{
	GulToolbutton *button = GUL_TOOLBUTTON (object);

	switch (prop_id)
	{
	case PROP_SHOW_ARROW:
		{
			gboolean v = g_value_get_boolean (value);
			gul_toolbutton_set_show_arrow (button, v);
		}
		break;
	default: 
		break;
	}
}

static void
gul_toolbutton_get_property (GObject *object,
			     guint prop_id,
			     GValue *value,
			     GParamSpec *pspec)
{
	GulToolbutton *button = GUL_TOOLBUTTON (object);
	
	switch (prop_id)
	{
	case PROP_SHOW_ARROW:
		g_value_set_boolean (value, button->priv->show_arrow);
		break;
	default: 
		break;
	}
}


static void
gul_toolbutton_class_init (GulToolbuttonClass *klass)
{
	GObjectClass *object_class;
	GtkToolItemClass *toolitem_class;
	
	parent_class = g_type_class_peek_parent (klass);
	
	object_class = (GObjectClass *) klass;
	toolitem_class = (GtkToolItemClass *) klass;

	object_class->finalize     = gul_toolbutton_finalize_impl;
	object_class->set_property = gul_toolbutton_set_property;
	object_class->get_property = gul_toolbutton_get_property;

	toolitem_class->set_tooltip = gul_toolbutton_set_tooltip;

	g_object_class_install_property (object_class,
					 PROP_SHOW_ARROW,
					 g_param_spec_boolean ("show_arrow",
							       _("Show Arrow"),
							       _("Show the dropdown arrow."),
							       FALSE,
							       G_PARAM_READWRITE));

	GulToolbuttonSignals[GUL_TOOLBUTTON_MENU_ACTIVATED] = g_signal_new (
		"menu-activated", G_OBJECT_CLASS_TYPE (klass),  
		G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
		G_STRUCT_OFFSET (GulToolbuttonClass, menu_activated), 
		NULL, NULL, 
		galeon_marshal_VOID__VOID,
		G_TYPE_NONE, 0);
}

GType
gul_toolbutton_get_type (void)
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof (GulToolbuttonClass),
			NULL,
			NULL,
			(GClassInitFunc) gul_toolbutton_class_init,
			NULL,
			NULL,
			sizeof (GulToolbutton),
			0,
			(GInstanceInitFunc) gul_toolbutton_init,
		};

		type = g_type_register_static (GTK_TYPE_TOOL_BUTTON, "GulToolbutton",
				               &info, 0);
	}
	return type;
}

GulToolbutton *
gul_toolbutton_new (void)
{
	GulToolbutton *ret = g_object_new (GUL_TYPE_TOOLBUTTON, NULL);
	
	return ret;
}

GtkMenuShell *
gul_toolbutton_get_menu (GulToolbutton *b)
{
	return GTK_MENU_SHELL (b->priv->menu);
}

GtkWidget *
gul_toolbutton_get_button (GulToolbutton *b)
{
	return GTK_WIDGET (b->priv->button);
}

void
gul_toolbutton_set_show_arrow (GulToolbutton *b, gboolean val)
{
	b->priv->show_arrow = val;
	if (val)
	{
		gtk_widget_show (b->priv->arrow_widget);
	}
	else
	{
		gtk_widget_hide (b->priv->arrow_widget);
	}
}
