/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2003 Takuro Ashie
 *
 *  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.
 *
 *  $Id: prefs_key_accel.c 2863 2007-03-14 06:43:16Z ikezoe $
 */

/*
 * Based on EggAccelDialog.
 * Credit here.
 */

#include "prefs_key_accel.h"

#include <string.h>
#include <glib/gi18n.h>

#include "kazehakase.h"
#include "glib-utils.h"


#define DATA_KEY "KzPrefsKeyAccel::info"


enum
{
	TERMINATOR = -1,
	COLUMN_PATH,
	COLUMN_ACCEL,
	COLUMN_LABEL,
	N_COLUMNS
};

typedef struct _KzPrefsKeyAccel
{
	GtkWidget *main_vbox;

	GtkListStore *accel_store;

	GtkWidget *accel_view;

	GtkWidget *shift_toggle;
	GtkWidget *ctrl_toggle;
	GtkWidget *alt_toggle;
	GtkWidget *key_entry;

	GtkWidget *set_button;
	GtkWidget *reset_button;
	GtkWidget *grab_button;

	GtkWidget *ok_button;
} KzPrefsKeyAccel;


static GtkWidget *prefs_key_accel_create   (void);
static void       prefs_key_accel_response (GtkWidget *widget,
					    gint       response);

static KzPrefsWinPageEntry prefs_entry =
{
	/* .path          = */ N_("/Key Accelerator"),
	/* .priority_hint = */ 0,
	/* .ui_level      = */ ~KZ_UI_LEVEL_BEGINNER,
	/* .create        = */ prefs_key_accel_create,
	/* .response      = */ prefs_key_accel_response,
};


static void
accel_map_foreach (gpointer     data,
		   const gchar *accel_path,
		   guint        accel_key,
		   guint        accel_mods,
		   gboolean     changed)
{
	KzPrefsKeyAccel *self = data;
	GtkTreeIter iter;
	gchar *accel_name;
	const gchar *label = NULL;

	gtk_list_store_append(self->accel_store, &iter);
	if (accel_key)
		accel_name = gtk_accelerator_name(accel_key, accel_mods);
	else
		accel_name = g_strdup("");

	if (accel_path)
		label = strchr(accel_path, '>');
	label = label ? label + 1 : accel_path;

	gtk_list_store_set(self->accel_store, &iter,
			   COLUMN_PATH,  accel_path,
			   COLUMN_ACCEL, accel_name,
			   COLUMN_LABEL, label,
			   TERMINATOR);
	if (accel_name)
		g_free(accel_name);
}


static void
egg_accel_dialog_rescan_accels (KzPrefsKeyAccel *self)
{
	g_return_if_fail(self);

	gtk_list_store_clear(self->accel_store);
	gtk_accel_map_foreach(self, accel_map_foreach);
}


/* make sure the currently selected accel is up to date */
static void
refresh_selected_row (KzPrefsKeyAccel *self)
{
	GtkTreeView *treeview;
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	char *accel_path, *orig_accel_name;
	GtkAccelKey key;

	g_return_if_fail(self);

	treeview = GTK_TREE_VIEW(self->accel_view);
	selection = gtk_tree_view_get_selection(treeview);
	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) return;

	/* get the accel path for the selected row */
	gtk_tree_model_get(GTK_TREE_MODEL(self->accel_store), &iter,
			   COLUMN_PATH, &accel_path,
			   COLUMN_ACCEL, &orig_accel_name,
			   TERMINATOR);
	if (!accel_path) return;

	if (gtk_accel_map_lookup_entry(accel_path, &key))
	{
		char *accel_name;

		accel_name = gtk_accelerator_name(key.accel_key,
						  key.accel_mods);
		if (g_strcmp(accel_name, orig_accel_name))
		{
			gtk_list_store_set(self->accel_store, &iter,
					   COLUMN_ACCEL, accel_name,
					   TERMINATOR);
		}
		g_free(accel_name);
	}

	g_free(accel_path);
	if (orig_accel_name)
		g_free(orig_accel_name);
}


static void
accel_path_selection_changed (GtkTreeSelection *selection,
			      KzPrefsKeyAccel  *self)
{
	GtkTreeIter iter;
	char *accel_path;
	GtkAccelKey key;

	/* just make sure the selected row is up to date */
	refresh_selected_row(self);

	if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) return;

	/* get the accel path for the selected row */
	gtk_tree_model_get(GTK_TREE_MODEL(self->accel_store), &iter,
			   COLUMN_PATH, &accel_path,
			   TERMINATOR);
	if (!accel_path) return;

	if (gtk_accel_map_lookup_entry(accel_path, &key))
	{
		GtkToggleButton *toggle[3];
		gchar *keyname;

		toggle[0] = GTK_TOGGLE_BUTTON(self->shift_toggle);
		toggle[1] = GTK_TOGGLE_BUTTON(self->ctrl_toggle);
		toggle[2] = GTK_TOGGLE_BUTTON(self->alt_toggle);

		gtk_toggle_button_set_active
			(toggle[0], (key.accel_mods & GDK_SHIFT_MASK));
		gtk_toggle_button_set_active
			(toggle[1], (key.accel_mods & GDK_CONTROL_MASK));
		gtk_toggle_button_set_active
			(toggle[2], (key.accel_mods & GDK_MOD1_MASK));

		keyname = gdk_keyval_name(key.accel_key);

		if (keyname)
			gtk_entry_set_text(GTK_ENTRY(self->key_entry), keyname);
		else
			gtk_entry_set_text(GTK_ENTRY(self->key_entry), "");
	}

	g_free(accel_path);
}


static void
accel_path_set (GtkWidget *button, KzPrefsKeyAccel *self)
{
	GtkTreeView *treeview;
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	char *accel_path;
	gboolean changed = FALSE;

	GdkModifierType accel_mods = 0;
	const gchar *key_name;
	guint accel_key = 0;

	g_return_if_fail(self);

	treeview = GTK_TREE_VIEW(self->accel_view);
	selection = gtk_tree_view_get_selection(treeview);
	if (!gtk_tree_selection_get_selected(selection, NULL, &iter))
		goto ERROR_OUT;

	/* get the accel path for the selected row */
	gtk_tree_model_get(GTK_TREE_MODEL(self->accel_store), &iter,
			   COLUMN_PATH, &accel_path,
			   TERMINATOR);
	if (!accel_path) goto ERROR_OUT;

	/* get modifiers */
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->shift_toggle)))
		accel_mods |= GDK_SHIFT_MASK;
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->ctrl_toggle)))
		accel_mods |= GDK_CONTROL_MASK;
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(self->alt_toggle)))
		accel_mods |= GDK_MOD1_MASK;

	key_name = gtk_entry_get_text (GTK_ENTRY (self->key_entry));
	/* check to see if entyr is empty -- if so, unset accel */
	if (key_name && *key_name)
	{
		accel_key = gdk_keyval_from_name (key_name);

		if (accel_key)
		{
			changed = gtk_accel_map_change_entry(accel_path,
							     accel_key,
							     accel_mods,
							     TRUE);
		}
	}
	else
	{
		changed = gtk_accel_map_change_entry (accel_path,
						      0, 0, TRUE);
	}

	g_free (accel_path);

ERROR_OUT:
	if (!changed)
		gdk_beep ();
	accel_path_selection_changed (selection, self);
}


static void
accel_path_reset (GtkWidget *button, KzPrefsKeyAccel *self)
{
	GtkTreeView *treeview;
	GtkTreeSelection *selection;
	GtkTreeIter iter;
	gboolean changed = FALSE;

	g_return_if_fail(self);

	g_message("don't know how to reset to defaults :(");
	return;

	treeview = GTK_TREE_VIEW(self->accel_view);
	selection = gtk_tree_view_get_selection(treeview);
	if (gtk_tree_selection_get_selected (selection, NULL, &iter))
	{
		char *accel_path;

		/* get the accel path for the selected row */
		gtk_tree_model_get(GTK_TREE_MODEL(self->accel_store), &iter,
				   COLUMN_PATH, &accel_path,
				   TERMINATOR);
		if (accel_path)
		{
			changed = gtk_accel_map_change_entry(accel_path,
							     0, 0, TRUE);
			g_free(accel_path);
		}
	}
	if (!changed)
		gdk_beep();
	accel_path_selection_changed(selection, self);
}


static gboolean
cb_grab_win_key_press (GtkWidget *widget,
		       GdkEventKey *event,
		       KzPrefsKeyAccel *self)
{
	g_object_set_data(G_OBJECT(widget),
			  "KzPrefsKeyAccel::key_pressd",
			  "TRUE");

	return TRUE;
}


static gboolean
grab_key_value (GtkWidget *widget, GdkEventKey *event, KzPrefsKeyAccel *self)
{
	const gchar *name, *pressed;

	pressed = g_object_get_data(G_OBJECT(widget),
				    "KzPrefsKeyAccel::key_pressd");
	if (!pressed) return TRUE;

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->shift_toggle),
				     event->state & GDK_SHIFT_MASK);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->ctrl_toggle),
				     event->state & GDK_CONTROL_MASK);
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->alt_toggle),
				     event->state & GDK_MOD1_MASK);

	name = gdk_keyval_name(event->keyval);
	gtk_entry_set_text(GTK_ENTRY(self->key_entry), "");
	gtk_entry_set_text(GTK_ENTRY(self->key_entry), name);

	gtk_widget_destroy(widget);

	return TRUE;
}


static void
cb_cancel_clicked (GtkButton *button, GtkWidget *widget)
{
	gtk_widget_destroy(widget);
}


static void
accel_key_grab (GtkWidget *button, KzPrefsKeyAccel *self)
{
	GtkWidget *toplevel = gtk_widget_get_toplevel(self->main_vbox);
	GtkWidget *widget = gtk_window_new(GTK_WINDOW_POPUP);
	GtkWidget *vbox = gtk_vbox_new(FALSE, 0), *hbox;
	GtkWidget *label, *but;

	label = gtk_label_new(_("<span weight=\"bold\" size=\"large\">"
				"Please press any key to grab..."
				"</span>"));

	gtk_window_set_transient_for(GTK_WINDOW(widget), GTK_WINDOW(toplevel));
	gtk_window_set_position(GTK_WINDOW(widget),
				GTK_WIN_POS_CENTER_ON_PARENT);
	gtk_window_set_default_size(GTK_WINDOW(widget), 300, 50);
	g_signal_connect(widget, "key-press-event",
			 G_CALLBACK(cb_grab_win_key_press), self);
	g_signal_connect(widget, "key-release-event",
			 G_CALLBACK(grab_key_value), self);

	gtk_container_set_border_width(GTK_CONTAINER(vbox), 5);
	gtk_container_add(GTK_CONTAINER(widget), vbox);

	gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 2);

	hbox = gtk_hbox_new(TRUE, 0);
	gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 4);
	gtk_widget_show(hbox);

	but = gtk_button_new_with_label(_("Cancel"));
	gtk_box_pack_start(GTK_BOX(hbox), but, TRUE, TRUE, 5);
	g_signal_connect(but, "clicked",
			 G_CALLBACK(cb_cancel_clicked), widget);
	gtk_widget_show(but);

	gtk_widget_show(label);
	gtk_widget_show(vbox);
	gtk_widget_show(widget);

	gtk_grab_add(widget);
	gdk_keyboard_grab (GTK_WIDGET (widget)->window,
			   TRUE, GDK_CURRENT_TIME);

	gdk_window_raise(widget->window);
}


static void
prefs_key_accel_destroy (gpointer data)
{
	KzPrefsKeyAccel *self = data;

	if (self->accel_store)
	{
		g_object_unref(self->accel_store);
		self->accel_store = NULL;
	}
	g_free(self);
}


static GtkWidget *
prefs_key_accel_create (void)
{
	KzPrefsKeyAccel *self = g_new0(KzPrefsKeyAccel, 1);
	GtkWidget *main_vbox;
	GtkTreeView *treeview;
	GtkTreeModel *model;
	GtkTreeViewColumn *col;
	GtkCellRenderer *renderer;
	GtkTreeSortable *sortable;
	GtkWidget *swin;
	GtkWidget *hbox, *label;

	main_vbox = gtk_vbox_new(FALSE, 0);
	self->main_vbox = main_vbox;
	g_object_set_data_full(G_OBJECT(main_vbox), DATA_KEY,
			       self, (GDestroyNotify) prefs_key_accel_destroy);

	label = kz_prefs_ui_utils_create_title(_("Key Accelerator"));
	gtk_box_pack_start(GTK_BOX(main_vbox), label,
			   FALSE, FALSE, 0);
	gtk_widget_show(label);

	/* description */
	hbox = gtk_hbox_new(FALSE, 0);
	gtk_box_pack_start(GTK_BOX(main_vbox), hbox,
			   FALSE, FALSE, 5);
	gtk_widget_show(hbox);

	label = gtk_label_new(_("Key accelerator will be applied immediatly "
				"when the \"Set\" button is pressed."));
	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
	gtk_box_pack_start(GTK_BOX(hbox), label,
			   FALSE, FALSE, 10);
	gtk_widget_show(label);

	/* set up the list store for all the accelerators */
	self->accel_store = gtk_list_store_new(3,
					       G_TYPE_STRING,
					       G_TYPE_STRING,
					       G_TYPE_STRING);
	model = GTK_TREE_MODEL(self->accel_store);
	sortable = GTK_TREE_SORTABLE(self->accel_store);
	gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);

	egg_accel_dialog_rescan_accels(self);


	swin = gtk_scrolled_window_new(NULL, NULL);
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swin),
				       GTK_POLICY_AUTOMATIC,
				       GTK_POLICY_AUTOMATIC);
        gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swin),
					    GTK_SHADOW_IN);
	gtk_container_set_border_width(GTK_CONTAINER(swin), 5);
	gtk_box_pack_start(GTK_BOX(main_vbox), swin, TRUE, TRUE, 0);
	gtk_widget_show(swin);

	/* set up a two column view of the model in browse selection mode */
	self->accel_view = gtk_tree_view_new_with_model(model);
	treeview = GTK_TREE_VIEW(self->accel_view);
	gtk_tree_view_set_rules_hint(treeview, TRUE);
	gtk_tree_selection_set_mode(gtk_tree_view_get_selection(treeview),
				    GTK_SELECTION_BROWSE);
	g_signal_connect(gtk_tree_view_get_selection(treeview), "changed",
			 G_CALLBACK(accel_path_selection_changed), self);

	renderer = gtk_cell_renderer_text_new();
	g_object_set(G_OBJECT(renderer), "xalign", 0.0, NULL);
	gtk_tree_view_insert_column_with_attributes(treeview, -1,
						    _("Path"), renderer,
						    "text", COLUMN_LABEL,
						    NULL);
	col = gtk_tree_view_get_column(treeview, 0);
	gtk_tree_view_column_set_sort_column_id(col, 0);
	gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_fixed_width (col, 280);
	gtk_tree_view_column_set_resizable(col, TRUE);

	renderer = gtk_cell_renderer_text_new();
	g_object_set (G_OBJECT (renderer), "xalign", 0.0, NULL);
	gtk_tree_view_insert_column_with_attributes(treeview, -1,
						    _("Accel"), renderer,
						    "text", COLUMN_ACCEL,
						    NULL);
	col = gtk_tree_view_get_column(treeview, 1);
	gtk_tree_view_column_set_sort_column_id(col, 1);
	gtk_tree_view_column_set_sizing(col, GTK_TREE_VIEW_COLUMN_FIXED);
	gtk_tree_view_column_set_resizable(col, TRUE);

	gtk_container_add(GTK_CONTAINER(swin), self->accel_view);
	gtk_widget_show(self->accel_view);

	hbox = gtk_hbox_new(FALSE, 0);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
	gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, TRUE, 0);
	gtk_widget_show(hbox);

	/* widgets for editing accels */
	self->shift_toggle = gtk_check_button_new_with_mnemonic(_("S_hift"));
	gtk_box_pack_start(GTK_BOX(hbox), self->shift_toggle,
			   FALSE, FALSE, 2);
	gtk_widget_show(self->shift_toggle);

	self->ctrl_toggle = gtk_check_button_new_with_mnemonic(_("_Ctrl"));
	gtk_box_pack_start(GTK_BOX(hbox), self->ctrl_toggle,
			   FALSE, FALSE, 2);
	gtk_widget_show(self->ctrl_toggle);

	self->alt_toggle = gtk_check_button_new_with_mnemonic(_("_Alt"));
	gtk_box_pack_start(GTK_BOX(hbox), self->alt_toggle,
			   FALSE, FALSE, 2);
	gtk_widget_show(self->alt_toggle);

	self->key_entry = gtk_entry_new();
	gtk_widget_set_size_request(self->key_entry, 50, -1);
	gtk_box_pack_start(GTK_BOX(hbox), self->key_entry,
			   TRUE, TRUE, 2);
	gtk_widget_show(self->key_entry);

	hbox = gtk_hbutton_box_new();
	gtk_button_box_set_layout(GTK_BUTTON_BOX(hbox), GTK_BUTTONBOX_END);
	gtk_container_set_border_width(GTK_CONTAINER(hbox), 5);
	gtk_box_pack_start(GTK_BOX(main_vbox), hbox, FALSE, TRUE, 0);
	gtk_widget_show(hbox);

	/* buttons for changing path */
	self->set_button = gtk_button_new_with_mnemonic(_("_Set"));
	gtk_box_pack_start(GTK_BOX(hbox), self->set_button,
			   FALSE, FALSE, 2);
	gtk_widget_show(self->set_button);

	self->reset_button = gtk_button_new_with_mnemonic(_("_Reset"));
	gtk_box_pack_start(GTK_BOX(hbox), self->reset_button,
			   FALSE, FALSE, 2);
	/*gtk_widget_show (self->reset_button);*/

	self->grab_button = gtk_button_new_with_mnemonic(_("_Grab..."));
	gtk_box_pack_start(GTK_BOX(hbox), self->grab_button,
			   FALSE, FALSE, 2);
	gtk_widget_show(self->grab_button);

	g_signal_connect(self->set_button, "clicked",
			 G_CALLBACK(accel_path_set), self);
	g_signal_connect(self->reset_button, "clicked",
			 G_CALLBACK(accel_path_reset), self);
	g_signal_connect(self->grab_button, "clicked",
			 G_CALLBACK(accel_key_grab), self);

	return main_vbox;
}


static void
prefs_key_accel_response (GtkWidget *widget, gint response)
{
	KzPrefsKeyAccel *self = g_object_get_data(G_OBJECT(widget), DATA_KEY);

	g_return_if_fail(self);

	switch (response) {
	case GTK_RESPONSE_ACCEPT:
	case GTK_RESPONSE_APPLY:
		break;
	case GTK_RESPONSE_REJECT:
		break;
	case KZ_RESPONSE_UI_LEVEL_MEDIUM:
		break;
	case KZ_RESPONSE_UI_LEVEL_EXPERT:
		break;
	case KZ_RESPONSE_UI_LEVEL_CUSTOM:
		break;
	default:
		break;
	}
}


KzPrefsWinPageEntry *
prefs_key_accel_get_entry (gint idx)
{
	if (idx == 0)
		return &prefs_entry;
	else
		return NULL;
}
