/*
** 1998-12-25 -	Everybody needs a code module of their own, even the shortcuts!
*/

#include "gentoo.h"

#include <stdlib.h>

#include "dirpane.h"
#include "strutil.h"
#include "mount.h"
#include "widgets/odscrolledbox.h"
#include "buttons.h"
#include "shortcuts.h"

/* ----------------------------------------------------------------------------------------- */

/* 1998-12-28 -	Create a new, empty shortcut bank to which shortcuts can then be added. */
ShortcutBank * shc_shortcutbank_new(void)
{
	ShortcutBank	*shb;

	if((shb = malloc(sizeof *shb)) != NULL)
	{
		shb->shortcut = NULL;
		shb->sep_mode = SSM_NONE;
		shb->visible  = 1;
		shb->right    = 0;
	}
	return shb;
}

/* 1998-12-25 -	Build the default shortcut bank, the one that is used if no shortcuts have been
**		found in the configuration.
*/
ShortcutBank * shc_shortcutbank_new_default(void)
{
	ShortcutBank	*shb;
	Shortcut	*shc;

	if((shb = shc_shortcutbank_new()) != NULL)
	{
		if((shc = shc_shortcut_new()) != NULL)
		{
			str_strncpy(shc->label, "Home", sizeof shc->label);
			str_strncpy(shc->path,  "$HOME", sizeof shc->path);
			shc_shortcutbank_add_shortcut(shb, shc);
		}
		if((shc = shc_shortcut_new()) != NULL)
		{
			str_strncpy(shc->label, "Local", sizeof shc->label);
			str_strncpy(shc->path,  "/usr/local", sizeof shc->path);
			shc_shortcutbank_add_shortcut(shb, shc);
		}
		if((shc = shc_shortcut_new()) != NULL)
		{
			str_strncpy(shc->label, "/", sizeof shc->label);
			str_strncpy(shc->path,  "/", sizeof shc->path);
			shc_shortcutbank_add_shortcut(shb, shc);
		}
	}
	return shb;
}

static void copy_shortcut(gpointer a, gpointer data)
{
	Shortcut	*ns;

	if((ns = shc_shortcut_copy((Shortcut *) a)) != NULL)
		shc_shortcutbank_add_shortcut((ShortcutBank *) data, ns);
}

/* 1998-12-25 -	Create and return a copy of <shb>. The copy shares no memory with the
**		original, not even for the individual shortcuts.
*/
ShortcutBank * shc_shortcutbank_copy(ShortcutBank *shb)
{
	ShortcutBank	*nb;

	if(shb == NULL)
		return NULL;

	if((nb = shc_shortcutbank_new()) != NULL)
	{
		g_list_foreach(shb->shortcut, copy_shortcut, (gpointer) nb);
		nb->sep_mode = shb->sep_mode;
		nb->visible  = shb->visible;
		nb->right    = shb->right;
	}
	return nb;
}

/* 1998-12-25 -	Add a new shortcut to the given bank. It is added last. Long function name. */
void shc_shortcutbank_add_shortcut(ShortcutBank *shb, Shortcut *shc)
{
	if(shb != NULL && shc != NULL)
		shb->shortcut = g_list_append(shb->shortcut, shc);
}

/* 1998-12-25 -	Move given shortcut either "up" (<delta> == -1) or "down" (<delta> == 1) in bank.
**		Returns the new position of the shortcut, -1 on failure.
*/
gint shc_shortcutbank_move_shortcut(ShortcutBank *shb, Shortcut *shc, gint delta)
{
	GList	*link;
	gint	pos, np;

	if((shb == NULL) || (shc == NULL) || ((delta != -1) && (delta != 1)))
		return -1;

	link = g_list_find(shb->shortcut, (gpointer) shc);
	pos  = g_list_position(shb->shortcut, link);
	np   = pos + delta;
	shb->shortcut = g_list_remove(shb->shortcut, (gpointer) shc);
	if(np < 0)
		np = g_list_length(shb->shortcut);
	else if(np > g_list_length(shb->shortcut))
		np = 0;
	shb->shortcut = g_list_insert(shb->shortcut, shc, np);
	return np;
}

/* 1998-12-25 -	If <shc> is a part of <shb>, remove it from the bank and then destroy it. */
void shc_shortcutbank_kill_shortcut(ShortcutBank *shb, Shortcut *shc)
{
	if(shb == NULL || shc == NULL)
		return;

	shb->shortcut = g_list_remove(shb->shortcut, shc);
	shc_shortcut_destroy(shc);
}

static gint evt_shortcut_clicked(GtkWidget *wid, gpointer data)
{
	MainInfo	*min = (MainInfo *) data;
	Shortcut	*shc;
	DirPane		*dp;

	shc = (Shortcut *) gtk_object_get_user_data(GTK_OBJECT(wid));
	dp  = min->gui->cur_dir;

	mnt_entering(min, shc->path, MNT_RARELY);

	switch(shc->pane)
	{
		case SHP_CURRENT:
			dp_enter_dir(dp, shc->path);
			break;
		case SHP_OTHER:
			dp_enter_dir(dp_mirror(min, dp), shc->path);
			break;
		case SHP_LEFT:
			dp_enter_dir(&min->gui->pane[0], shc->path);
			break;
		case SHP_RIGHT:
			dp_enter_dir(&min->gui->pane[1], shc->path);
			break;
	}
	return TRUE;
}

/* 1999-01-13 -	This is connected to the vbox containing the shortcut buttons, so that when
**		that widget is destroyed, the tooltips (a pointer to which was supplied in
**		the connection, as <user>) are destroyed, too. We don't *want* to leak memory...
*/
static gint evt_vbox_destroy(GtkWidget *wid, gpointer user)
{
	gtk_object_destroy(GTK_OBJECT(user));

	return TRUE;
}

/* 1998-12-25 -	Build a GUI representation of the given shortcut bank. Basically a scrolled
**		window containing all the shortcuts, stacked on top of each other.
** 1999-01-13 -	Added support for tooltips showing a shortcut's path, pretty cool.
*/
GtkWidget * shc_shortcutbank_build(MainInfo *min, ShortcutBank *shb)
{
	Shortcut	*shc;
	GtkWidget	*scwin, *vbox, *btn;
	GtkTooltips	*tt;
	GList		*iter;
	gint		keep_tt = FALSE;

	if(shb == NULL || shb->shortcut == NULL)
		return NULL;

	scwin = od_scrolled_box_new();
	od_scrolled_box_set_num(OD_SCROLLED_BOX(scwin), btn_buttonsheet_get_height(min->cfg.buttons.sheets->data));
	gtk_widget_set_usize(scwin, 100, -1);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	if(GTK_SCROLLED_WINDOW(scwin)->hscrollbar != NULL) {
		GTK_WIDGET_UNSET_FLAGS(GTK_SCROLLED_WINDOW(scwin)->hscrollbar, GTK_CAN_FOCUS);
	}
	if(GTK_SCROLLED_WINDOW(scwin)->vscrollbar != NULL) {
		GTK_WIDGET_UNSET_FLAGS(GTK_SCROLLED_WINDOW(scwin)->vscrollbar, GTK_CAN_FOCUS);
	}

	vbox = gtk_vbox_new(FALSE, 0);
	tt = gtk_tooltips_new();

	for(iter = shb->shortcut; iter != NULL; iter = g_list_next(iter))
	{
		shc = (Shortcut *) iter->data;

		btn = gtk_button_new_with_label(shc->label);
		GTK_WIDGET_UNSET_FLAGS(btn, GTK_CAN_FOCUS);
		gtk_object_set_user_data(GTK_OBJECT(btn), (gpointer) shc);
		gtk_signal_connect(GTK_OBJECT(btn), "clicked", GTK_SIGNAL_FUNC(evt_shortcut_clicked), (gpointer) min);
		if(shc->show_tooltip && shc->path[0] != '\0')	/* Avoid empty tooltips, which only look silly. */
		{
			gtk_tooltips_set_tip(tt, btn, shc->path, NULL);
			keep_tt = TRUE;
		}
		gtk_box_pack_start(GTK_BOX(vbox), btn, TRUE, TRUE, 0);
		gtk_widget_show(btn);
	}
	gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scwin), vbox);
/*	gtk_container_add(GTK_CONTAINER(scwin), vbox);*/
	gtk_widget_show(vbox);
	if(keep_tt)
		gtk_signal_connect(GTK_OBJECT(vbox), "destroy", GTK_SIGNAL_FUNC(evt_vbox_destroy), tt);
	else
		gtk_object_destroy(GTK_OBJECT(tt));

	return scwin;
}

/* 1998-12-25 -	This is sort of clumsy, but anyway... Create and pack a GUI representation
**		of <shb> into <hbox>, together with the <other> widgetry that needs to be
**		there. Uses the <shb>'s 'sep_mode' member to determine how to do this.
**		Might end up _not_ creating any shortcuts, if the bank is not visible.
*/
void shc_shortcutbank_pack(MainInfo *min, ShortcutBank *shb, GtkWidget *hbox, GtkWidget *other)
{
	GtkWidget	*sb, *left, *right;

	if(shb == NULL || hbox == NULL || (shb->visible == 0 && other == NULL))
		return;

	if(shb->visible)
	{
		if((sb = shc_shortcutbank_build(min, shb)) != NULL)
		{
			left  = sb;
			right = other;
			if(shb->right)		/* Do we need to swap sides? */
				left = right, right = sb;

			switch(shb->sep_mode)
			{
				case SSM_NONE:
					gtk_box_pack_start(GTK_BOX(hbox), left, TRUE, TRUE, 0);
					gtk_box_pack_start(GTK_BOX(hbox), right, TRUE, TRUE, 0);
					break;
				case SSM_SIMPLE:
					{
						GtkWidget	*vsep;

						vsep = gtk_vseparator_new();
						gtk_box_pack_start(GTK_BOX(hbox), left, TRUE, TRUE, 0);
						gtk_box_pack_start(GTK_BOX(hbox), vsep, FALSE, FALSE, 5);
						gtk_widget_show(vsep);
						gtk_box_pack_start(GTK_BOX(hbox), right, TRUE, TRUE, 0);
					}
					break;
				case SSM_PANED:
					{
						GtkWidget	*hpane;

						hpane = gtk_hpaned_new();
						gtk_paned_set_handle_size(GTK_PANED(hpane), 10);
						gtk_paned_set_gutter_size(GTK_PANED(hpane), 10);
						if(left != NULL)
							gtk_paned_add1(GTK_PANED(hpane), left);
						if(right != NULL)
							gtk_paned_add2(GTK_PANED(hpane), right);
						gtk_box_pack_start(GTK_BOX(hbox), hpane, TRUE, TRUE, 0);
						gtk_widget_show(hpane);
					}
					break;
			}
			if(left != NULL)
				gtk_widget_show(left);
			if(right != NULL)
				gtk_widget_show(right);
		}
	}
	else if(other != NULL)
	{
		gtk_box_pack_start(GTK_BOX(hbox), other, TRUE, TRUE, 0);
		gtk_widget_show(other);
	}
}

/* 1998-12-25 -	Just a g_slist_foreach() callback. */
static void kill_shortcut(gpointer a, gpointer user)
{
	shc_shortcut_destroy((Shortcut *) a);
}

/* 1998-12-25 -	Clear away all the shortcuts, without actually destroying the bank. */
void shc_shortcutbank_clear(ShortcutBank *shb)
{
	if(shb != NULL)
	{
		g_list_foreach(shb->shortcut, kill_shortcut, NULL);
		g_list_free(shb->shortcut);
		shb->shortcut = NULL;
	}
}

/* 1998-12-25 -	Destroy a shortcut bank with this when you tire of it. Don't havae it
**		visible in a GUI at the same time, please.
*/
void shc_shortcutbank_destroy(ShortcutBank *shb)
{
	if(shb != NULL)
	{
		g_list_foreach(shb->shortcut, kill_shortcut, NULL);
		free(shb);
	}
}

/* ----------------------------------------------------------------------------------------- */

/* 1998-12-25 -	Create a new, empty shortcut. */
Shortcut * shc_shortcut_new(void)
{
	Shortcut	*shc;

	if((shc = malloc(sizeof *shc)) != NULL)
	{
		shc->label[0] = '\0';
		shc->path[0]  = '\0';
		shc->pane     = SHP_CURRENT;
		shc->show_tooltip = FALSE;
	}
	return shc;
}

/* 1998-12-25 -	Create a copy of <shc>, in freshly allocated memory. */
Shortcut * shc_shortcut_copy(Shortcut *shc)
{
	Shortcut	*ns;

	if(shc == NULL)
		return NULL;

	if((ns = shc_shortcut_new()) != NULL)
	{
		str_strncpy(ns->label, shc->label, sizeof ns->label);
		str_strncpy(ns->path,  shc->path,  sizeof ns->path);
		ns->pane	 = shc->pane;
		ns->show_tooltip = shc->show_tooltip;
	}
	return ns;
}

/* 1998-12-25 -	Destroy a previosly created shortcut. */
void shc_shortcut_destroy(Shortcut *shc)
{
	if(shc != NULL)
		free(shc);
}
