/*
 *  Copyright (C) 2002 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.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

/* Galeon includes */
#include "gul-gui.h"
#include "gul-string.h"
#include "hig-alert.h"
#include "eel-gconf-extensions.h"

/* system includes */
#include <ctype.h>
#include <string.h>
#include <glib/gi18n.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtkmain.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkimage.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkdialog.h>
#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtkseparatormenuitem.h>
#include <gtk/gtkiconfactory.h>
#include <gtk/gtkimagemenuitem.h>
#include <gtk/gtkcheckmenuitem.h>
#include <gtk/gtkstock.h>
#include <libgnomeui/gnome-icon-lookup.h>
#include <libgnomeui/gnome-icon-theme.h>
#include <libgnome/gnome-help.h>

static void
make_selection_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                     GList **list);

/**
 * gul_gui_menu_position_under_widget:
 */
void
gul_gui_menu_position_under_widget (GtkMenu   *menu,
				     gint      *x,
				     gint      *y,
				     gboolean  *push_in,
				     gpointer	user_data)
{
	GtkWidget *w = GTK_WIDGET (user_data);
	const GtkAllocation *allocation = &w->allocation;
	gint screen_width, screen_height;
	GtkRequisition requisition;

	gdk_window_get_origin (w->window, x, y);
	*x += allocation->x;
	*y += allocation->y + allocation->height;

	gtk_widget_size_request (GTK_WIDGET (menu), &requisition);
      
	screen_width = gdk_screen_width ();
	screen_height = gdk_screen_height ();
	  
	*x = CLAMP (*x, 0, MAX (0, screen_width - requisition.width));
	*y = CLAMP (*y, 0, MAX (0, screen_height - requisition.height));
}

/**
 * gul_gui_gtk_radio_button_get: get the active member of a radiobutton
 * group from one of the buttons in the group. This should be in GTK+!
 */
gint
gul_gui_gtk_radio_button_get (GtkRadioButton *radio_button)
{
	GtkToggleButton *toggle_button;
	gint i, length;
        GSList *list;
	
	/* get group list */
        list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_button));
        length = g_slist_length (list);

	/* iterate over list to find active button */
	for (i = 0; list != NULL; i++, list = g_slist_next (list))
	{
		/* get button and text */
		toggle_button = GTK_TOGGLE_BUTTON (list->data);
		if (gtk_toggle_button_get_active (toggle_button))
		{
			break;
		}
	}

	/* check we didn't run off end */
	g_assert (list != NULL);

	/* return index (reverse order!) */
	return (length - 1) - i;
}

/**
 * gul_gui_gtk_radio_button_set: set the active member of a radiobutton
 * group from one of the buttons in the group. This should be in GTK+!
 */
void
gul_gui_gtk_radio_button_set (GtkRadioButton *radio_button, gint index)
{
	GtkToggleButton *button;
	GSList *list;
	gint length;

	/* get the list */
        list = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio_button));

	/* check out the length */
        length = g_slist_length (list);

        /* new buttons are *preppended* to the list, so button added as first 
         * has last position in the list */
        index = (length - 1) - index;

	/* find the right button */
        button = GTK_TOGGLE_BUTTON (g_slist_nth_data (list, index));

	/* set it... this will de-activate the others in the group */
	if (gtk_toggle_button_get_active (button) == FALSE)
	{
		gtk_toggle_button_set_active (button, TRUE);
	}
}

/**
 * treeview_get_selection: get a list of selected iters
 */
GList *
gul_gui_treeview_get_selection_refs (GtkTreeView *treeview)
{
        GtkTreeSelection *selection = gtk_tree_view_get_selection (treeview);
        GList *list = NULL;

        gtk_tree_selection_selected_foreach (selection,
                                             (GtkTreeSelectionForeachFunc) make_selection_list,
                                             &list);

        return list;
}

/**
 * gul_gui_treeview_free_selection_refs: free the list returned from
 * gul_gui_treeview_get_selection_refs
 */
void
gul_gui_treeview_free_selection_refs (GList *l)
{
	g_list_foreach (l, (GFunc)gtk_tree_row_reference_free, NULL);
	g_list_free (l);
}

/**
 * make_selection_list: utl func to retreive the selection of a treeview
 * supporting multiple selection
 */
static void
make_selection_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter,
                     GList **list)
{
	GtkTreeRowReference *node;

	node = gtk_tree_row_reference_new (model, path);
        *list = g_list_append (*list, node);
}

GtkWidget * 
gul_gui_append_new_menuitem (GtkWidget  *menu,
			     const char *mnemonic,
			     GCallback   callback,
			     gpointer    data)
{
	GtkWidget *menu_item;
	
	menu_item = gtk_menu_item_new_with_mnemonic (mnemonic);
	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
			       menu_item);
	
	if (callback)
	{
		g_signal_connect (G_OBJECT (menu_item),
				  "activate",
				  callback, data);
	}

	return menu_item;
}

GtkWidget * 
gul_gui_append_new_menuitem_stock (GtkWidget  *menu,
				   const char *stock_id,
				   GCallback   callback,
				   gpointer    data)
{
	GtkWidget *menu_item;
	
	menu_item = gtk_image_menu_item_new_from_stock (stock_id, NULL);
	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
			       menu_item);
	
	if (callback)
	{
		g_signal_connect (G_OBJECT (menu_item),
				  "activate",
				  callback, data);
	}
	
	return menu_item;
}

GtkWidget *
gul_gui_append_new_menuitem_stock_icon (GtkWidget *menu, 
					const char *stock_id,
					const char *mnemonic,
					GCallback callback,
					gpointer data)
{
	GtkWidget *menu_item;
	GtkWidget *image;

	menu_item = gtk_image_menu_item_new_with_mnemonic (mnemonic);
	image = gtk_image_new_from_stock (stock_id, GTK_ICON_SIZE_MENU);
	gtk_widget_show (image);
	gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item), image);

	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
			       menu_item);
	
	if (callback)
	{
		g_signal_connect (G_OBJECT (menu_item),
				  "activate",
				  callback, data);
	}
	
	return menu_item;
}

GtkWidget * 
gul_gui_append_new_check_menuitem (GtkWidget  *menu,
				   const char *mnemonic,
				   gboolean value,
				   GCallback callback,
				   gpointer data)
{
	GtkWidget *menu_item;
	
	menu_item = gtk_check_menu_item_new_with_mnemonic (mnemonic);
	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
			       menu_item);
	
	gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menu_item), value);
	
	if (callback)
	{
		g_signal_connect (G_OBJECT (menu_item),
				  "activate",
				  callback, data);
	}
	
	return menu_item;
}

GtkWidget *
gul_gui_append_separator (GtkWidget *menu)
{
	GtkWidget *menu_item;
	
	menu_item = gtk_menu_item_new ();
	gtk_widget_show (menu_item);
	gtk_menu_shell_append (GTK_MENU_SHELL (menu),
			       menu_item);

	return menu_item;
}

gboolean 
gul_gui_confirm_overwrite_file (GtkWidget *parent, const char *filename)
{
	char *tmp, *basename, *fullname, *utf8File;
	GtkWidget *dialog;
	gboolean res;
	
	g_return_val_if_fail (filename != NULL, FALSE);

	if (!g_file_test (filename, G_FILE_TEST_EXISTS))
	{
		return TRUE;
	}

	utf8File = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
	tmp = g_path_get_basename (utf8File);
	basename = g_markup_escape_text (tmp, -1);
	g_free (tmp);
	tmp = g_markup_escape_text (utf8File, -1);
	g_free (utf8File);
	fullname = g_strdup_printf ("<tt>\"%s\"</tt>", tmp);
	g_free (tmp);

	dialog = hig_alert_new (parent ? GTK_WINDOW(parent) : NULL,
			        GTK_DIALOG_MODAL,
				HIG_ALERT_CONFIRMATION,
				NULL, NULL,
				GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
			        _("_Replace"),    GTK_RESPONSE_YES,
				NULL);

	hig_alert_set_primary_printf (HIG_ALERT(dialog), 
			              _("Replace \"%s\"?"), basename);
	hig_alert_set_secondary_printf (HIG_ALERT(dialog),
			                _("The file %s already exists.\n"
					  "Do you want to replace it?"),
					fullname);
	g_free (basename);
	g_free (fullname);

	gtk_dialog_set_default_response (GTK_DIALOG (dialog),
			                 GTK_RESPONSE_CANCEL);

	res = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES);
	gtk_widget_destroy (dialog);		

	return res;
}

/** gul_gui_widget_hide_now:
 *
 * Same as gtk_widget_hide() but also flushes pending events so that the widget
 * really gets hidden, provides quicker response when closing windows.
 * Conveniently returns %FALSE so that it can be used as a signal handler for
 * "delete-event"
 *
 * Return value: %FALSE
 */
gboolean
gul_gui_widget_hide_now (GtkWidget *widget)
{
	gtk_widget_hide (widget);
	while (gtk_events_pending ()) gtk_main_iteration ();
	return FALSE;
}

/**
 * Check if the menushell already has a taroff menuitem.
 */
static gboolean 
gul_gui_menu_shell_has_tearoff (GtkMenuShell *ms)
{
	gboolean ret = FALSE;
	GList *children;
	GList *li;

	children = gtk_container_get_children (GTK_CONTAINER (ms));
	for (li = children; li; li = li->next)
	{
		if (GTK_IS_TEAROFF_MENU_ITEM (li->data))
		{
			ret = TRUE;
			break;
		}
	}
	g_list_free (children);

	return ret;
}

/**
 * Adds a teaoroff menuitem if there isn't one already and the gconf option is set
 */
void
gul_gui_setup_tearoff (GtkMenuShell *ms)
{
	gboolean has_tearoff = eel_gconf_get_boolean ("/desktop/gnome/interface/menus_have_tearoff");

	if (has_tearoff && !gul_gui_menu_shell_has_tearoff (ms))
	{
		GtkWidget *tearoff = gtk_tearoff_menu_item_new ();
		gtk_widget_show (tearoff);
		gtk_menu_shell_prepend (ms, tearoff);
	}
}

/**
 * Adds a separated menuitem if there isn't one at the end of the menushell
 */
void
gul_gui_add_separator_conditional (GtkMenuShell *ms)
{
	GList *children;
	GList *li;
	gboolean add = TRUE;

	children = gtk_container_get_children (GTK_CONTAINER (ms));
	for (li = children; li; li = li->next)
	{
		if (!li->next && GTK_IS_SEPARATOR_MENU_ITEM (li->data))
		{
			add = FALSE;
			break;
		}
	}
	g_list_free (children);

	if (add)
	{
		GtkWidget *sep = gtk_separator_menu_item_new ();
		gtk_widget_show (sep);
		gtk_menu_shell_append (ms, sep);
	}
}

/**
 * Removes a separator menuitem if there is one at the end of the menushell
 * Needed because the menushell for the context menu persists when not visible.
 * If the context bookmarks becomes null in the interim, the separator will
 * still be left at the end of the context menu. Ugly
 */
void
gul_gui_remove_separator_conditional(GtkMenuShell *ms)
{
	GList *children;
	GList *li;
	GtkWidget *sep = NULL;

	children = gtk_container_get_children(GTK_CONTAINER(ms));
	for (li = children; li; li = li->next)
	{
		if (!li->next && GTK_IS_SEPARATOR_MENU_ITEM(li->data))
		{
			sep = GTK_WIDGET(li->data);
			break;
		}
	}
	g_list_free(children);

	if (sep)
	{
		gtk_widget_destroy(sep);
	}
}

void
gul_gui_remove_all_children (GtkContainer *c)
{
	GList *children = gtk_container_get_children (c);
	GList *li;
	for (li = children; li; li = li->next)
	{
		gtk_container_remove (c, li->data);
	}
	g_list_free (children);
}

/**
 * gul_gui_image_set_from_mime_type:
 * @image: a #GtkImage
 * @mime_type: a MIME type
 * @icon_size: a stock icon size
 *
 * Set the @image to display the icon for MIME type @mime_type.  Sample MIME
 * types are "text/plain", "application/ogg".  Sample stock sizes are
 * #GTK_ICON_SIZE_MENU, #GTK_ICON_SIZE_DIALOG.
 */
void
gul_gui_image_set_from_mime_type (GtkWidget  *image,
		                  const char *mime_type,
				  GtkIconSize icon_size)
{
	GnomeIconTheme *theme;
	char           *icon;
	GtkIconSource  *source;
	GtkIconSet     *icon_set;

	g_return_if_fail (GTK_IS_IMAGE(image));
	g_return_if_fail (mime_type != NULL);

	theme = gnome_icon_theme_new ();
	gnome_icon_theme_set_allow_svg (theme, TRUE);

	icon = gnome_icon_lookup (theme, NULL, NULL, NULL, NULL,
				  mime_type,
				  GNOME_ICON_LOOKUP_FLAGS_NONE, NULL);

	if (!g_path_is_absolute (icon))
	{
		int   width, height;
		char *path;

		if (!gtk_icon_size_lookup_for_settings 
			(gtk_widget_get_settings (GTK_WIDGET(image)),
			 icon_size, &width, &height))
		{
			width = height = -1;
		}

		path = gnome_icon_theme_lookup_icon (theme, icon, height,
				                     NULL, NULL);
		g_free (icon);
		icon = path;
	}
	g_object_unref (theme);

	/* Now that we have the icon filename, wrap it into an GtkIconSet so
	 * that we really get the desired size; the icon size in the file may
	 * still be arbitrary, in case of SVG themes, for example.
	 */

	source = gtk_icon_source_new ();
	gtk_icon_source_set_filename (source, icon);
	g_free (icon);

	icon_set = gtk_icon_set_new ();
	gtk_icon_set_add_source (icon_set, source);
	gtk_icon_source_free (source);

	gtk_image_set_from_icon_set (GTK_IMAGE(image), icon_set, icon_size);

	gtk_icon_set_unref (icon_set);
}


/**
 * Open the help browser
 */
void
gul_gui_help (GtkWindow *parent, const char *file, const char *link_id)
{
	GError *error;
	GtkWidget *dialog;

	error = NULL;
	gnome_help_display (file, link_id, &error);

	if (error) 
	{
		dialog = hig_alert_new (parent,
					(GtkDialogFlags)0,
					HIG_ALERT_ERROR,
					_("Cannot display help."),
					NULL,
					GTK_STOCK_OK,
					GTK_RESPONSE_OK,
					NULL);
		hig_alert_set_secondary_printf (HIG_ALERT (dialog),
						error->message);
		g_signal_connect (G_OBJECT (dialog), "response",
				  G_CALLBACK (gtk_widget_destroy),
				  NULL);
		gtk_widget_show (dialog);
		g_error_free (error);
	}
}
