/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/**
 * 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 of the
 * License, 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.
 *
 * Authors:
 *   James Willcox <jwillcox@cs.indiana.edu>
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include <libgnomevfs/gnome-vfs.h>
#ifndef USE_STABLE_LIBGNOMEUI
#include <libgnomeui/gnome-icon-theme.h>
#endif
#include <gconf/gconf-client.h>
#include "egg-recent-model.h"
#include "egg-recent-view.h"
#include "egg-recent-view-gtk.h"
#include "egg-recent-util.h"
#include "egg-recent-item.h"

struct _EggRecentViewGtk {
	GObject parent_instance;	/* We emit signals */

	GtkWidget *menu;
	GtkWidget *start_menu_item;

	gboolean leading_sep;
	gboolean trailing_sep;

	gulong changed_cb_id;

	gchar *uid;

	gboolean show_icons;
	gboolean show_numbers;
#ifndef USE_STABLE_LIBGNOMEUI
	GnomeIconTheme *theme;
#endif

	GtkTooltips *tooltips;
	EggRecentViewGtkTooltipFunc tooltip_func;
	gpointer tooltip_func_data;

	EggRecentModel *model;
	GConfClient *client;
	GtkIconSize icon_size;
};



struct _EggRecentViewGtkMenuData {
	EggRecentViewGtk *view;
	EggRecentItem *item;
};

typedef struct _EggRecentViewGtkMenuData EggRecentViewGtkMenuData;

enum {
	ACTIVATE,
	LAST_SIGNAL
};

/* GObject properties */
enum {
	PROP_BOGUS,
	PROP_MENU,
	PROP_START_MENU_ITEM,
	PROP_SHOW_ICONS,
	PROP_SHOW_NUMBERS
};

static guint view_signals[LAST_SIGNAL] = { 0 };


static void
egg_recent_view_gtk_clear (EggRecentViewGtk *view)
{
	GList *menu_children;
	GList *p;
	GObject *menu_item;
	gint *menu_data=NULL;

	g_return_if_fail (view->menu != NULL);

	menu_children = gtk_container_get_children (GTK_CONTAINER (view->menu));

	p = menu_children;
	while (p != NULL) {
		menu_item = (GObject *)p->data;

		menu_data = (gint *)g_object_get_data (menu_item,
						       view->uid);
	
		if (menu_data) {
			gtk_container_remove (GTK_CONTAINER (view->menu),
					     GTK_WIDGET (menu_item));

		}
		
		p = p->next;
	}
}


static gint
egg_recent_view_gtk_find_menu_offset (EggRecentViewGtk *view)
{
	gint i;
	GList *menu_children;
	GList *p;
	GtkWidget *menu_item;
	gint menu_loc=-1;

	g_return_val_if_fail (view, 0);

	menu_children = GTK_MENU_SHELL (view->menu)->children;

	i = 0;
	p = menu_children;
	while (p != NULL) {
		menu_item = (GtkWidget *)p->data;

		if (menu_item == view->start_menu_item) {
			menu_loc = i;
			break;
		}

		p = p->next;
		i++;
	}

	return menu_loc;
}

static void
egg_recent_view_gtk_menu_cb (GtkWidget *menu, gpointer data)
{
	EggRecentViewGtkMenuData *md = (EggRecentViewGtkMenuData *) data;
	EggRecentItem *item;

	g_return_if_fail (md);
	g_return_if_fail (md->item);
	g_return_if_fail (md->view);
	g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (md->view));

	item = md->item;
	
	egg_recent_item_ref (item);
	
	g_signal_emit (G_OBJECT(md->view), view_signals[ACTIVATE], 0,
		       item);

	egg_recent_item_unref (item);
}

static void
egg_recent_view_gtk_destroy_cb (gpointer data, GClosure *closure)
{
	EggRecentViewGtkMenuData *md = data;

	egg_recent_item_unref (md->item);
	g_free (md);
}

static GtkWidget *
egg_recent_view_gtk_new_menu_item (EggRecentViewGtk *view,
				   EggRecentItem *item,
				   gint index)
{
	GtkWidget *menu_item;
	EggRecentViewGtkMenuData *md=(EggRecentViewGtkMenuData *)g_malloc (sizeof (EggRecentViewGtkMenuData));
	gchar *text;
	gchar *basename;
	gchar *escaped;
	gchar *uri;

	g_return_val_if_fail (view, NULL);

	if (item != NULL) {
		gchar *mime_type;
		GtkWidget *image;
		GdkPixbuf *pixbuf;

		uri = egg_recent_item_get_uri_for_display (item);
		if (uri == NULL)
			return NULL;
		
		basename = g_path_get_basename (uri);
		escaped = egg_recent_util_escape_underlines (basename);
		g_free (basename);
		g_free (uri);

		if (view->show_numbers) {
			/* avoid having conflicting mnemonics */
			if (index >= 10)
				text = g_strdup_printf ("%d.  %s", index,
							escaped);
			else
				text = g_strdup_printf ("_%d.  %s", index,
							escaped);
			g_free (escaped);
		} else {
			text = escaped;
		}


		mime_type = egg_recent_item_get_mime_type (item);
#ifndef USE_STABLE_LIBGNOMEUI
		{
			int width, height;

			gtk_icon_size_lookup_for_settings
					(gtk_widget_get_settings (view->menu),
					 view->icon_size,
					 &width, &height);

			pixbuf = egg_recent_util_get_icon (view->theme, uri,
							   mime_type,
							   height);
		}
#else
		pixbuf = NULL;
#endif
		image = gtk_image_new_from_pixbuf (pixbuf);
		if (pixbuf)
			g_object_unref (pixbuf);

		if (view->show_icons)
			gtk_widget_show (image);

		menu_item = gtk_image_menu_item_new_with_mnemonic (text);
		gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
					       image);

		md->view = view;
		md->item = item;

		egg_recent_item_ref (md->item);

		g_signal_connect_data (G_OBJECT (menu_item), "activate",
				 G_CALLBACK (egg_recent_view_gtk_menu_cb),
				 md,
				 (GClosureNotify)egg_recent_view_gtk_destroy_cb,
				 0);

		g_free (mime_type);
		g_free (text);
	}
	else {
		menu_item = gtk_separator_menu_item_new ();
	}
	
	g_return_val_if_fail (menu_item, NULL);

	/**
	 * this is a tag so we can distinguish our menu items
	 * from others that may be in the menu.
	 */
	g_object_set_data (G_OBJECT (menu_item),
			   view->uid,
			   GINT_TO_POINTER (1));


	gtk_widget_show (menu_item);

	return menu_item;
}

static void
egg_recent_view_gtk_add_to_menu (EggRecentViewGtk *view,
				 EggRecentItem *item,
				 gint display,
				 gint index)
{
	GtkWidget *menu_item;
	gint menu_offset;
	
	g_return_if_fail (view);
	g_return_if_fail (view->menu);

	menu_offset = egg_recent_view_gtk_find_menu_offset (view);

	menu_item = egg_recent_view_gtk_new_menu_item (view, item, display);

	if (view->tooltip_func != NULL && menu_item != NULL) {
		view->tooltip_func (view->tooltips, menu_item,
				    item, view->tooltip_func_data);
	}
	
	if (menu_item)
		gtk_menu_shell_insert (GTK_MENU_SHELL (view->menu), menu_item,
			       menu_offset+index);
}




static void
egg_recent_view_gtk_set_list (EggRecentViewGtk *view, GList *list)
{
	EggRecentItem *item;
	GList *p;
	gint display=1;
	gint index=1;

	g_return_if_fail (view);

	egg_recent_view_gtk_clear (view);

	if (view->leading_sep) {
		egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
		index++;
	}

	p = list;
	while (p != NULL) {
		item = (EggRecentItem *)p->data;

		egg_recent_view_gtk_add_to_menu (view, item, display, index);

		p = p->next;
		display++;
		index++;
	}

	if (view->trailing_sep)
		egg_recent_view_gtk_add_to_menu (view, NULL, display, index);
}

static void
model_changed_cb (EggRecentModel *model, GList *list, EggRecentViewGtk *view)
{
	if (list != NULL)
		egg_recent_view_gtk_set_list (view, list);
	else
		egg_recent_view_gtk_clear (view);
}

static EggRecentModel *
egg_recent_view_gtk_get_model (EggRecentView *view_parent)
{
	EggRecentViewGtk *view;
	
	g_return_val_if_fail (view_parent != NULL, NULL);
	view = EGG_RECENT_VIEW_GTK (view_parent);
	return view->model;
}

static void
egg_recent_view_gtk_set_model (EggRecentView *view_parent,
				 EggRecentModel *model)
{
	EggRecentViewGtk *view;
	
	g_return_if_fail (view_parent != NULL);
	view = EGG_RECENT_VIEW_GTK (view_parent);

	if (view->model != NULL) {
		g_object_unref (view->model);
		g_signal_handler_disconnect (G_OBJECT (model),
					     view->changed_cb_id);
	}
	
	view->model = model;
	g_object_ref (view->model);

	view->changed_cb_id = g_signal_connect_object (G_OBJECT (model),
						"changed",
						G_CALLBACK (model_changed_cb),
						view, 0);

	egg_recent_model_changed (view->model);
}

void
egg_recent_view_gtk_set_leading_sep (EggRecentViewGtk *view, gboolean val)
{
	view->leading_sep = val;

	egg_recent_view_gtk_clear (view);

	if (view->model)
		egg_recent_model_changed (view->model);
}

void
egg_recent_view_gtk_set_trailing_sep (EggRecentViewGtk *view, gboolean val)
{
	view->trailing_sep = val;

	egg_recent_view_gtk_clear (view);

	if (view->model)
		egg_recent_model_changed (view->model);
}

static void
egg_recent_view_gtk_set_property (GObject *object,
			   guint prop_id,
			   const GValue *value,
			   GParamSpec *pspec)
{
	EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);

	switch (prop_id)
	{
		case PROP_MENU:
			egg_recent_view_gtk_set_menu (view,
					       GTK_WIDGET (g_value_get_object (value)));
		break;
		case PROP_START_MENU_ITEM:
			egg_recent_view_gtk_set_start_menu_item (view,
					g_value_get_object (value));
		break;
		case PROP_SHOW_ICONS:
			egg_recent_view_gtk_show_icons (view,
					g_value_get_boolean (value));
		break;
		case PROP_SHOW_NUMBERS:
			egg_recent_view_gtk_show_numbers (view,
					g_value_get_boolean (value));
		break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
egg_recent_view_gtk_get_property (GObject *object,
			   guint prop_id,
			   GValue *value,
			   GParamSpec *pspec)
{
	EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);

	switch (prop_id)
	{
		case PROP_MENU:
			g_value_set_object (value, view->menu);
		break;
		case PROP_START_MENU_ITEM:
			g_value_set_object (value, view->start_menu_item);
		break;
		case PROP_SHOW_ICONS:
			g_value_set_boolean (value, view->show_icons);
		break;
		case PROP_SHOW_NUMBERS:
			g_value_set_boolean (value, view->show_numbers);
		break;
		default:
			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
		break;
	}
}

static void
egg_recent_view_gtk_finalize (GObject *object)
{
	EggRecentViewGtk *view = EGG_RECENT_VIEW_GTK (object);

	g_signal_handler_disconnect (G_OBJECT (view->model),
				     view->changed_cb_id);

	g_free (view->uid);

	g_object_unref (view->menu);
	g_object_unref (view->model);
#ifndef USE_STABLE_LIBGNOMEUI
	g_object_unref (view->theme);
#endif
	g_object_unref (view->client);

	g_object_unref (view->tooltips);
}

static void
egg_recent_view_gtk_class_init (EggRecentViewGtkClass * klass)
{
	GObjectClass *object_class;

	object_class = G_OBJECT_CLASS (klass);

	object_class->set_property = egg_recent_view_gtk_set_property;
	object_class->get_property = egg_recent_view_gtk_get_property;
	object_class->finalize     = egg_recent_view_gtk_finalize;

	view_signals[ACTIVATE] = g_signal_new ("activate",
			G_OBJECT_CLASS_TYPE (object_class),
			G_SIGNAL_RUN_LAST,
			G_STRUCT_OFFSET (EggRecentViewGtkClass, activate),
			NULL, NULL,
			g_cclosure_marshal_VOID__BOXED,
			G_TYPE_NONE, 1,
			EGG_TYPE_RECENT_ITEM);

	g_object_class_install_property (object_class,
					 PROP_MENU,
					 g_param_spec_object ("menu",
						 	      "Menu",
							      "The GtkMenuShell this object will update.",
							      gtk_menu_get_type(),
							      G_PARAM_READWRITE));
	g_object_class_install_property (object_class,
					 PROP_START_MENU_ITEM,
					 g_param_spec_object ("start-menu-item",
						 	      "Start Menu Item",
							      "The menu item that precedes where are menu items will go",
							      gtk_menu_item_get_type (),
							      G_PARAM_READWRITE));

	g_object_class_install_property (object_class,
					 PROP_SHOW_ICONS,
					 g_param_spec_boolean ("show-icons",
					   "Show Icons",
					   "Whether or not to show icons",
					   FALSE,
					   G_PARAM_READWRITE));

	g_object_class_install_property (object_class,
					 PROP_SHOW_NUMBERS,
					 g_param_spec_boolean ("show-numbers",
					   "Show Numbers",
					   "Whether or not to show numbers",
					   TRUE,
					   G_PARAM_READWRITE));

	klass->activate = NULL;
}

static void
egg_recent_view_init (EggRecentViewClass *iface)
{
	iface->do_get_model = egg_recent_view_gtk_get_model;
	iface->do_set_model = egg_recent_view_gtk_set_model;
}

static void
show_menus_changed_cb (GConfClient *client,
		       guint cnxn_id,
		       GConfEntry *entry,
		       EggRecentViewGtk *view)
{
	GConfValue *value;

	value = gconf_entry_get_value (entry);

	g_return_if_fail (value->type == GCONF_VALUE_BOOL);

	egg_recent_view_gtk_show_icons (view,
				gconf_value_get_bool (value));

}

#ifndef USE_STABLE_LIBGNOMEUI
static void
theme_changed_cb (GnomeIconTheme *theme, EggRecentViewGtk *view)
{
	if (view->model != NULL)
		egg_recent_model_changed (view->model);
}
#endif

static void
egg_recent_view_gtk_init (EggRecentViewGtk * view)
{
	view->client = gconf_client_get_default ();

	view->show_icons =
		gconf_client_get_bool (view->client,
			"/desktop/gnome/interface/menus_have_icons",
			NULL);

	gconf_client_add_dir (view->client, "/desktop/gnome/interface",
			      GCONF_CLIENT_PRELOAD_NONE,
			      NULL);
	gconf_client_notify_add (view->client,
			"/desktop/gnome/interface/menus_have_icons",
			(GConfClientNotifyFunc)show_menus_changed_cb,
			view, NULL, NULL);


	view->leading_sep = FALSE;
	view->trailing_sep = FALSE;

	view->uid = egg_recent_util_get_unique_id ();
#ifndef USE_STABLE_LIBGNOMEUI
	view->theme = gnome_icon_theme_new ();
	gnome_icon_theme_set_allow_svg (view->theme, TRUE);
	g_signal_connect_object (view->theme, "changed",
				 G_CALLBACK (theme_changed_cb), view, 0);
#endif
	view->tooltips = gtk_tooltips_new ();
	g_object_ref (view->tooltips);
	gtk_object_sink (GTK_OBJECT (view->tooltips));
	view->tooltip_func = NULL;
	view->tooltip_func_data = NULL;

	view->icon_size = GTK_ICON_SIZE_MENU;
}

void
egg_recent_view_gtk_set_icon_size (EggRecentViewGtk *view,
				   GtkIconSize icon_size)
{
	if (view->icon_size != icon_size) {
		view->icon_size = icon_size;
		egg_recent_model_changed (view->model);
	} else {
		view->icon_size = icon_size;
	}
}

GtkIconSize
egg_recent_view_gtk_get_icon_size (EggRecentViewGtk *view)
{
	return view->icon_size;
}

void
egg_recent_view_gtk_show_icons (EggRecentViewGtk *view, gboolean show)
{
	view->show_icons = show;

	if (view->model)
		egg_recent_model_changed (view->model);
}

void
egg_recent_view_gtk_show_numbers (EggRecentViewGtk *view, gboolean show)
{
	view->show_numbers = show;

	if (view->model)
		egg_recent_model_changed (view->model);
}

void
egg_recent_view_gtk_set_tooltip_func (EggRecentViewGtk *view,
				      EggRecentViewGtkTooltipFunc func,
				      gpointer user_data)
{
	view->tooltip_func = func;
	view->tooltip_func_data = user_data;
	
	if (view->model)
		egg_recent_model_changed (view->model);
}

/**
 * egg_recent_view_gtk_set_menu:
 * @view: A EggRecentViewGtk object.
 * @menu: The GtkMenuShell to put the menu items in.
 *
 * Use this function to change the GtkMenuShell that the recent
 * documents appear in.
 *
 */
void
egg_recent_view_gtk_set_menu (EggRecentViewGtk *view,
				GtkWidget *menu)
{
	g_return_if_fail (view);
	g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
	g_return_if_fail (menu);

	if (view->menu != NULL)
		g_object_unref (view->menu);
	
	view->menu = menu;
	g_object_ref (view->menu);
}

/**
 * egg_recent_view_gtk_set_start_menu_item:
 * @view: A EggRecentViewGtk object.
 * @start_menu_item: The menu item that appears just before where our menu
 * items should appear
 *
 */
void
egg_recent_view_gtk_set_start_menu_item (EggRecentViewGtk *view,
					 GtkWidget *menu_item)
{
	g_return_if_fail (view);
	g_return_if_fail (EGG_IS_RECENT_VIEW_GTK (view));
	
	view->start_menu_item = menu_item;
}

/**
 * egg_recent_view_gtk_get_menu:
 * @view: A EggRecentViewGtk object.
 *
 */
GtkWidget *
egg_recent_view_gtk_get_menu (EggRecentViewGtk *view)
{
	return view->menu;
}

/**
 * egg_recent_view_gtk_get_start_menu_item
 * @view: A EggRecentViewGtk object.
 *
 */
GtkWidget *
egg_recent_view_gtk_get_start_menu_item (EggRecentViewGtk *view)
{
	return view->start_menu_item;
}


/**
 * egg_recent_view_gtk_new:
 * @appname: The name of your application.
 * @limit:  The maximum number of items allowed.
 *
 * This creates a new EggRecentViewGtk object.
 *
 * Returns: a EggRecentViewGtk object
 */
EggRecentViewGtk *
egg_recent_view_gtk_new (GtkWidget *menu, GtkWidget *start_menu_item)
{
	EggRecentViewGtk *view;

	g_return_val_if_fail (menu, NULL);

	view = EGG_RECENT_VIEW_GTK (g_object_new (egg_recent_view_gtk_get_type (),
					   "start-menu-item",
					   start_menu_item,
					   "menu", menu,
					   "show-numbers", TRUE, NULL));

	g_return_val_if_fail (view, NULL);
	
	return view;
}

/**
 * egg_recent_view_gtk_get_type:
 * @:
 *
 * This returns a GType representing a EggRecentViewGtk object.
 *
 * Returns: a GType
 */
GType
egg_recent_view_gtk_get_type (void)
{
	static GType egg_recent_view_gtk_type = 0;

	if(!egg_recent_view_gtk_type) {
		static const GTypeInfo egg_recent_view_gtk_info = {
			sizeof (EggRecentViewGtkClass),
			NULL, /* base init */
			NULL, /* base finalize */
			(GClassInitFunc)egg_recent_view_gtk_class_init, /* class init */
			NULL, /* class finalize */
			NULL, /* class data */
			sizeof (EggRecentViewGtk),
			0,
			(GInstanceInitFunc) egg_recent_view_gtk_init
		};

		static const GInterfaceInfo view_info =
		{
			(GInterfaceInitFunc) egg_recent_view_init,
			NULL,
			NULL
		};

		egg_recent_view_gtk_type = g_type_register_static (G_TYPE_OBJECT,
							"EggRecentViewGtk",
							&egg_recent_view_gtk_info, 0);
		g_type_add_interface_static (egg_recent_view_gtk_type,
					     EGG_TYPE_RECENT_VIEW,
					     &view_info);
	}

	return egg_recent_view_gtk_type;
}

