/* X-Chat
 * Copyright (C) 1998 Peter Zelezny.
 *
 * 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
 */
#define _FILE_OFFSET_BITS 64	/* allow selection of large files */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>

#include <gtk/gtk.h>
#include <gtk/gtkbutton.h>
#include <gtk/gtkclist.h>
#include <gtk/gtkscrolledwindow.h>
#include <gtk/gtkmessagedialog.h>
#include <gtk/gtkwindow.h>
#include <gtk/gtkhbox.h>
#include <gtk/gtkimage.h>
#include <gtk/gtktooltips.h>
#include <gtk/gtklabel.h>
#include <gtk/gtkentry.h>
#include <gtk/gtkstock.h>
#include <gtk/gtkspinbutton.h>
#include <gtk/gtkclipboard.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtktreeselection.h>
#include <gtk/gtkcellrenderertext.h>
#include <gtk/gtkcellrenderertoggle.h>
#include <gtk/gtkversion.h>
#include <gtk/gtkfilechooserdialog.h>

#include <ekg/xmalloc.h>	/* xstrcmp */

#include "main.h"
#include "gtkutil.h"

#if 0
#include "pixmaps.h"

/* gtkutil.c, just some gtk wrappers */

extern void path_part(char *file, char *path, int pathlen);


struct file_req {
	GtkWidget *dialog;
	void *userdata;
	filereqcallback callback;
	int flags;		/* FRF_* flags */
};

static char last_dir[256] = "";


static void
gtkutil_file_req_destroy(GtkWidget *wid, struct file_req *freq)
{
	freq->callback(freq->userdata, NULL);
	free(freq);
}

static void
gtkutil_check_file(char *file, struct file_req *freq)
{
	struct stat st;
	int axs = FALSE;

	path_part(file, last_dir, sizeof(last_dir));

	/* check if the file is readable or writable */
	if (freq->flags & FRF_WRITE) {
		if (access(last_dir, W_OK) == 0)
			axs = TRUE;
	} else {
		if (stat(file, &st) != -1) {
			if (!S_ISDIR(st.st_mode) || (freq->flags & FRF_CHOOSEFOLDER))
				axs = TRUE;
		}
	}

	if (axs) {
		char *utf8_file;
		/* convert to UTF8. It might be converted back to locale by
		   server.c's g_convert */
		utf8_file = xchat_filename_to_utf8(file, -1, NULL, NULL, NULL);
		if (utf8_file) {
			freq->callback(freq->userdata, utf8_file);
			g_free(utf8_file);
		} else {
			fe_message("Filename encoding is corrupt.", FE_MSG_ERROR);
		}
	} else {
		if (freq->flags & FRF_WRITE)
			fe_message(_("Cannot write to that file."), FE_MSG_ERROR);
		else
			fe_message(_("Cannot read that file."), FE_MSG_ERROR);
	}
}

static void
gtkutil_file_req_done(GtkWidget *wid, struct file_req *freq)
{
	GSList *files, *cur;
	GtkFileChooser *fs = GTK_FILE_CHOOSER(freq->dialog);

	if (freq->flags & FRF_MULTIPLE) {
		files = cur = gtk_file_chooser_get_filenames(fs);
		while (cur) {
			gtkutil_check_file(cur->data, freq);
			g_free(cur->data);
			cur = cur->next;
		}
		if (files)
			g_slist_free(files);
	} else {
		if (freq->flags & FRF_CHOOSEFOLDER)
			gtkutil_check_file(gtk_file_chooser_get_current_folder(fs), freq);
		else
			gtkutil_check_file(gtk_file_chooser_get_filename(fs), freq);
	}

	/* this should call the "destroy" cb, where we free(freq) */
	gtk_widget_destroy(freq->dialog);
}

static void
gtkutil_file_req_response(GtkWidget *dialog, gint res, struct file_req *freq)
{
	switch (res) {
	case GTK_RESPONSE_ACCEPT:
		gtkutil_file_req_done(dialog, freq);
		break;

	case GTK_RESPONSE_CANCEL:
		/* this should call the "destroy" cb, where we free(freq) */
		gtk_widget_destroy(freq->dialog);
	}
}

void
gtkutil_file_req(const char *title, void *callback, void *userdata, char *filter, int flags)
{
	struct file_req *freq;
	GtkWidget *dialog;
	extern char *get_xdir_fs(void);

	if (flags & FRF_WRITE) {
		dialog = gtk_file_chooser_dialog_new(title, NULL,
						     GTK_FILE_CHOOSER_ACTION_SAVE,
						     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
						     GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL);
		if (filter && filter[0]) {	/* filter becomes initial name when saving */
			char temp[1024];
			path_part(filter, temp, sizeof(temp));
			gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), temp);
			gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog),
							  file_part(filter));
		}
#if GTK_CHECK_VERSION(2,8,0)
		if (!(flags & FRF_NOASKOVERWRITE))
			gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog),
								       TRUE);
#endif
	} else
		dialog = gtk_file_chooser_dialog_new(title, NULL,
						     GTK_FILE_CHOOSER_ACTION_OPEN,
						     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
						     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	if (flags & FRF_MULTIPLE)
		gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog), TRUE);
	if (last_dir[0])
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), last_dir);
	if (flags & FRF_ADDFOLDER)
		gtk_file_chooser_add_shortcut_folder(GTK_FILE_CHOOSER(dialog),
						     get_xdir_fs(), NULL);
	if (flags & FRF_CHOOSEFOLDER) {
		gtk_file_chooser_set_action(GTK_FILE_CHOOSER(dialog),
					    GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
		gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), filter);
	} else {
		if (filter && (flags & FRF_FILTERISINITIAL))
			gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), filter);
	}

	freq = malloc(sizeof(struct file_req));
	freq->dialog = dialog;
	freq->flags = flags;
	freq->callback = callback;
	freq->userdata = userdata;

	g_signal_connect(G_OBJECT(dialog), "response",
			 G_CALLBACK(gtkutil_file_req_response), freq);
	g_signal_connect(G_OBJECT(dialog), "destroy",
			 G_CALLBACK(gtkutil_file_req_destroy), (gpointer) freq);
	gtk_widget_show(dialog);
}

void
gtkutil_destroy(GtkWidget *igad, GtkWidget *dgad)
{
	gtk_widget_destroy(dgad);
}

static void
gtkutil_get_str_response(GtkDialog * dialog, gint arg1, gpointer entry)
{
	void (*callback) (int cancel, char *text, void *user_data);
	char *text;
	void *user_data;

	text = (char *)gtk_entry_get_text(GTK_ENTRY(entry));
	callback = g_object_get_data(G_OBJECT(dialog), "cb");
	user_data = g_object_get_data(G_OBJECT(dialog), "ud");

	switch (arg1) {
	case GTK_RESPONSE_REJECT:
		callback(TRUE, text, user_data);
		gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	case GTK_RESPONSE_ACCEPT:
		callback(FALSE, text, user_data);
		gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	}
}

static void
gtkutil_str_enter(GtkWidget *entry, GtkWidget *dialog)
{
	gtk_dialog_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
}

void
fe_get_str(char *msg, char *def, void *callback, void *userdata)
{
	GtkWidget *dialog;
	GtkWidget *entry;
	GtkWidget *hbox;
	GtkWidget *label;

	dialog = gtk_dialog_new_with_buttons(msg, NULL, 0,
					     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
					     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dialog)->vbox), TRUE);
	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
	hbox = gtk_hbox_new(TRUE, 0);

	g_object_set_data(G_OBJECT(dialog), "cb", callback);
	g_object_set_data(G_OBJECT(dialog), "ud", userdata);

	entry = gtk_entry_new();
	g_signal_connect(G_OBJECT(entry), "activate", G_CALLBACK(gtkutil_str_enter), dialog);
	gtk_entry_set_text(GTK_ENTRY(entry), def);
	gtk_box_pack_end(GTK_BOX(hbox), entry, 0, 0, 0);

	label = gtk_label_new(msg);
	gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 0);

	g_signal_connect(G_OBJECT(dialog), "response",
			 G_CALLBACK(gtkutil_get_str_response), entry);

	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);

	gtk_widget_show_all(dialog);
}

static void
gtkutil_get_number_response(GtkDialog * dialog, gint arg1, gpointer spin)
{
	void (*callback) (int cancel, int value, void *user_data);
	int num;
	void *user_data;

	num = gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(spin));
	callback = g_object_get_data(G_OBJECT(dialog), "cb");
	user_data = g_object_get_data(G_OBJECT(dialog), "ud");

	switch (arg1) {
	case GTK_RESPONSE_REJECT:
		callback(TRUE, num, user_data);
		gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	case GTK_RESPONSE_ACCEPT:
		callback(FALSE, num, user_data);
		gtk_widget_destroy(GTK_WIDGET(dialog));
		break;
	}
}

void
fe_get_int(char *msg, int def, void *callback, void *userdata)
{
	GtkWidget *dialog;
	GtkWidget *spin;
	GtkWidget *hbox;
	GtkWidget *label;
	GtkAdjustment *adj;

	dialog = gtk_dialog_new_with_buttons(msg, NULL, 0,
					     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
					     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
	gtk_box_set_homogeneous(GTK_BOX(GTK_DIALOG(dialog)->vbox), TRUE);
	gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_MOUSE);
	hbox = gtk_hbox_new(TRUE, 0);

	g_object_set_data(G_OBJECT(dialog), "cb", callback);
	g_object_set_data(G_OBJECT(dialog), "ud", userdata);

	spin = gtk_spin_button_new(NULL, 1, 0);
	adj = gtk_spin_button_get_adjustment((GtkSpinButton *) spin);
	adj->lower = 0;
	adj->upper = 1024;
	adj->step_increment = 1;
	gtk_adjustment_changed(adj);
	gtk_spin_button_set_value((GtkSpinButton *) spin, def);
	gtk_box_pack_end(GTK_BOX(hbox), spin, 0, 0, 0);

	label = gtk_label_new(msg);
	gtk_box_pack_end(GTK_BOX(hbox), label, 0, 0, 0);

	g_signal_connect(G_OBJECT(dialog), "response",
			 G_CALLBACK(gtkutil_get_number_response), spin);

	gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), hbox);

	gtk_widget_show_all(dialog);
}

#endif

GtkWidget *
gtkutil_button(GtkWidget *box, char *stock, char *tip, void *callback,
	       void *userdata, char *labeltext)
{
	GtkWidget *wid, *img, *bbox;

	wid = gtk_button_new();

	if (labeltext) {
		gtk_button_set_label(GTK_BUTTON(wid), labeltext);
		gtk_button_set_image(GTK_BUTTON(wid),
				     gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU));
		gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
		if (box)
			gtk_container_add(GTK_CONTAINER(box), wid);
	} else {
		bbox = gtk_hbox_new(0, 0);
		gtk_container_add(GTK_CONTAINER(wid), bbox);
		gtk_widget_show(bbox);

		img = gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU);
		if (!xstrcmp(stock, GTK_STOCK_GOTO_LAST))
			gtk_widget_set_usize(img, 10, 6);
		gtk_container_add(GTK_CONTAINER(bbox), img);
		gtk_widget_show(img);
		gtk_box_pack_start(GTK_BOX(box), wid, 0, 0, 0);
	}

	g_signal_connect(G_OBJECT(wid), "clicked", G_CALLBACK(callback), userdata);
	gtk_widget_show(wid);
	if (tip)
		add_tip(wid, tip);
	return wid;
}

#if 0

void
gtkutil_label_new(char *text, GtkWidget *box)
{
	GtkWidget *label = gtk_label_new(text);
	gtk_container_add(GTK_CONTAINER(box), label);
	gtk_widget_show(label);
}

GtkWidget *
gtkutil_entry_new(int max, GtkWidget *box, void *callback, gpointer userdata)
{
	GtkWidget *entry = gtk_entry_new_with_max_length(max);
	gtk_container_add(GTK_CONTAINER(box), entry);
	if (callback)
		g_signal_connect(G_OBJECT(entry), "changed", G_CALLBACK(callback), userdata);
	gtk_widget_show(entry);
	return entry;
}

GtkWidget *
gtkutil_clist_new(int columns, char *titles[],
		  GtkWidget *box, int policy,
		  void *select_callback, gpointer select_userdata,
		  void *unselect_callback, gpointer unselect_userdata, int selection_mode)
{
	GtkWidget *clist, *win;

	win = gtk_scrolled_window_new(0, 0);
	gtk_container_add(GTK_CONTAINER(box), win);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win), GTK_POLICY_AUTOMATIC, policy);
	gtk_widget_show(win);

	if (titles)
		clist = gtk_clist_new_with_titles(columns, titles);
	else
		clist = gtk_clist_new(columns);

	gtk_clist_set_selection_mode(GTK_CLIST(clist), selection_mode);
	gtk_clist_column_titles_passive(GTK_CLIST(clist));
	gtk_container_add(GTK_CONTAINER(win), clist);
	if (select_callback) {
		g_signal_connect(G_OBJECT(clist), "select_row",
				 G_CALLBACK(select_callback), select_userdata);
	}
	if (unselect_callback) {
		g_signal_connect(G_OBJECT(clist), "unselect_row",
				 G_CALLBACK(unselect_callback), unselect_userdata);
	}
	gtk_widget_show(clist);

	return clist;
}

int
gtkutil_clist_selection(GtkWidget *clist)
{
	if (GTK_CLIST(clist)->selection)
		return GPOINTER_TO_INT(GTK_CLIST(clist)->selection->data);
	return -1;
}

static int
int_compare(const int *elem1, const int *elem2)
{
	return (*elem1) - (*elem2);
}

int
gtkutil_clist_multiple_selection(GtkWidget *clist, int **rows, const int max_rows)
{
	int i = 0;
	GList *tmp_clist;
	*rows = malloc(sizeof(int) * max_rows);
	memset(*rows, -1, max_rows * sizeof(int));

	for (tmp_clist = GTK_CLIST(clist)->selection;
	     tmp_clist && i < max_rows; tmp_clist = tmp_clist->next, i++) {
		(*rows)[i] = GPOINTER_TO_INT(tmp_clist->data);
	}
	qsort(*rows, i, sizeof(int), (void *)int_compare);
	return i;

}

#endif

void
add_tip(GtkWidget *wid, char *text)
{
	static GtkTooltips *tip = NULL;
	if (!tip)
		tip = gtk_tooltips_new();
	gtk_tooltips_set_tip(tip, wid, text, 0);
}

#if 0

void
show_and_unfocus(GtkWidget *wid)
{
	GTK_WIDGET_UNSET_FLAGS(wid, GTK_CAN_FOCUS);
	gtk_widget_show(wid);
}

#endif

void
gtkutil_set_icon(GtkWidget *win)
{
#if 0
	gtk_window_set_icon(GTK_WINDOW(win), pix_xchat);
#endif
}

extern GtkWidget *parent_window;	/* maingui.c */


GtkWidget *
gtkutil_window_new(char *title, char *role, int width, int height, int flags)
{
	GtkWidget *win;

	win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtkutil_set_icon(win);
	gtk_window_set_title(GTK_WINDOW(win), title);
	gtk_window_set_default_size(GTK_WINDOW(win), width, height);
	gtk_window_set_role(GTK_WINDOW(win), role);
	if (flags & 1)
		gtk_window_set_position(GTK_WINDOW(win), GTK_WIN_POS_MOUSE);
	if ((flags & 2) && parent_window) {
		gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_DIALOG);
		gtk_window_set_transient_for(GTK_WINDOW(win), GTK_WINDOW(parent_window));
	}

	return win;
}

#if 0

/* pass NULL as selection to paste to both clipboard & X11 text */
void
gtkutil_copy_to_clipboard(GtkWidget *widget, GdkAtom selection, const gchar * str)
{
	GtkWidget *win;
	GtkClipboard *clip, *clip2;

	win = gtk_widget_get_toplevel(GTK_WIDGET(widget));
	if (GTK_WIDGET_TOPLEVEL(win)) {
		int len = strlen(str);

		if (selection) {
			clip = gtk_widget_get_clipboard(win, selection);
			gtk_clipboard_set_text(clip, str, len);
		} else {
			/* copy to both primary X selection and clipboard */
			clip = gtk_widget_get_clipboard(win, GDK_SELECTION_PRIMARY);
			clip2 = gtk_widget_get_clipboard(win, GDK_SELECTION_CLIPBOARD);
			gtk_clipboard_set_text(clip, str, len);
			gtk_clipboard_set_text(clip2, str, len);
		}
	}
}

/* Treeview util functions */

GtkWidget *
gtkutil_treeview_new(GtkWidget *box, GtkTreeModel * model, GtkTreeCellDataFunc mapper, ...)
{
	GtkWidget *win, *view;
	GtkCellRenderer *renderer = NULL;
	GtkTreeViewColumn *col;
	va_list args;
	int col_id = 0;
	GType type;
	char *title, *attr;

	win = gtk_scrolled_window_new(0, 0);
	gtk_container_add(GTK_CONTAINER(box), win);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(win),
				       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
	gtk_widget_show(win);

	view = gtk_tree_view_new_with_model(model);
	/* the view now has a ref on the model, we can unref it */
	g_object_unref(G_OBJECT(model));
	gtk_container_add(GTK_CONTAINER(win), view);

	va_start(args, mapper);
	for (col_id = va_arg(args, int); col_id != -1; col_id = va_arg(args, int)) {
		type = gtk_tree_model_get_column_type(model, col_id);
		switch (type) {
		case G_TYPE_BOOLEAN:
			renderer = gtk_cell_renderer_toggle_new();
			attr = "active";
			break;
		case G_TYPE_STRING:	/* fall through */
		default:
			renderer = gtk_cell_renderer_text_new();
			attr = "text";
			break;
		}

		title = va_arg(args, char *);
		if (mapper) {	/* user-specified function to set renderer attributes */
			col = gtk_tree_view_column_new_with_attributes(title, renderer, NULL);
			gtk_tree_view_column_set_cell_data_func(col, renderer, mapper,
								GINT_TO_POINTER(col_id), NULL);
		} else {
			/* just set the typical attribute for this type of renderer */
			col = gtk_tree_view_column_new_with_attributes(title, renderer,
								       attr, col_id, NULL);
		}
		gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
	}

	va_end(args);

	return view;
}

gboolean
gtkutil_treemodel_string_to_iter(GtkTreeModel * model, gchar * pathstr, GtkTreeIter * iter_ret)
{
	GtkTreePath *path = gtk_tree_path_new_from_string(pathstr);
	gboolean success;

	success = gtk_tree_model_get_iter(model, iter_ret, path);
	gtk_tree_path_free(path);
	return success;
}

/*gboolean
gtkutil_treeview_get_selected_iter (GtkTreeView *view, GtkTreeIter *iter_ret)
{
	GtkTreeModel *store;
	GtkTreeSelection *select;
	
	select = gtk_tree_view_get_selection (view);
	return gtk_tree_selection_get_selected (select, &store, iter_ret);
}*/

gboolean
gtkutil_treeview_get_selected(GtkTreeView * view, GtkTreeIter * iter_ret, ...)
{
	GtkTreeModel *store;
	GtkTreeSelection *select;
	gboolean has_selected;
	va_list args;

	select = gtk_tree_view_get_selection(view);
	has_selected = gtk_tree_selection_get_selected(select, &store, iter_ret);

	if (has_selected) {
		va_start(args, iter_ret);
		gtk_tree_model_get_valist(store, iter_ret, args);
		va_end(args);
	}

	return has_selected;
}

#endif
