changeset 2336:ad45d65e9ae7

Hotkey Plugin: Complete GUI rework. Ability to use more than one binding for the same action.
author Sascha Hlusiak <contact@saschahlusiak.de>
date Wed, 23 Jan 2008 19:37:05 +0100
parents 6b9d5a8b509e
children 107c1fed3d92
files src/hotkey/grab.c src/hotkey/gui.c src/hotkey/plugin.c src/hotkey/plugin.h
diffstat 4 files changed, 548 insertions(+), 326 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotkey/grab.c	Wed Jan 23 14:12:02 2008 +0100
+++ b/src/hotkey/grab.c	Wed Jan 23 19:37:05 2008 +0100
@@ -93,96 +93,96 @@
 }
 
 /* grab required keys */
-static void grab_key(HotkeyConfiguration hotkey, Display *xdisplay, Window x_root_window)
+static void grab_key(const HotkeyConfiguration *hotkey, Display *xdisplay, Window x_root_window)
 {
-	unsigned int modifier = hotkey.mask & ~(numlock_mask | capslock_mask | scrolllock_mask);
+	unsigned int modifier = hotkey->mask & ~(numlock_mask | capslock_mask | scrolllock_mask);
 	
-	if (hotkey.key == 0) return;
+	if (hotkey->key == 0) return;
 
-	if (hotkey.type == TYPE_KEY)
+	if (hotkey->type == TYPE_KEY)
 	{
-		XGrabKey (xdisplay, hotkey.key, modifier, x_root_window,
+		XGrabKey (xdisplay, hotkey->key, modifier, x_root_window,
 			False, GrabModeAsync, GrabModeAsync);
 		
 		if (modifier == AnyModifier)
 			return;
 		
 		if (numlock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | numlock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (capslock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | capslock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | capslock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (scrolllock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | scrolllock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | scrolllock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (numlock_mask && capslock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (numlock_mask && scrolllock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | numlock_mask | scrolllock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (capslock_mask && scrolllock_mask)
-			XGrabKey (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask,
+			XGrabKey (xdisplay, hotkey->key, modifier | capslock_mask | scrolllock_mask,
 				x_root_window,
 				False, GrabModeAsync, GrabModeAsync);
 		
 		if (numlock_mask && capslock_mask && scrolllock_mask)
-			XGrabKey (xdisplay, hotkey.key,
+			XGrabKey (xdisplay, hotkey->key,
 				modifier | numlock_mask | capslock_mask | scrolllock_mask,
 				x_root_window, False, GrabModeAsync,
 				GrabModeAsync);
 	}
-	if (hotkey.type == TYPE_MOUSE)
+	if (hotkey->type == TYPE_MOUSE)
 	{
-		XGrabButton (xdisplay, hotkey.key, modifier, x_root_window,
+		XGrabButton (xdisplay, hotkey->key, modifier, x_root_window,
 			False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (modifier == AnyModifier)
 			return;
 		
 		if (numlock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | numlock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (capslock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | capslock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | capslock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (scrolllock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | scrolllock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | scrolllock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (numlock_mask && capslock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (numlock_mask && scrolllock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | numlock_mask | scrolllock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (capslock_mask && scrolllock_mask)
-			XGrabButton (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask,
+			XGrabButton (xdisplay, hotkey->key, modifier | capslock_mask | scrolllock_mask,
 				x_root_window,
 				False, ButtonPressMask, GrabModeAsync, GrabModeAsync, None, None);
 		
 		if (numlock_mask && capslock_mask && scrolllock_mask)
-			XGrabButton (xdisplay, hotkey.key,
+			XGrabButton (xdisplay, hotkey->key,
 				modifier | numlock_mask | capslock_mask | scrolllock_mask,
 				x_root_window, False, ButtonPressMask, GrabModeAsync,
 				GrabModeAsync, None, None);
@@ -194,6 +194,7 @@
 	Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
 	Window x_root_window = GDK_WINDOW_XID(gdk_get_default_root_window());
 	PluginConfig* plugin_cfg = get_config();
+	HotkeyConfiguration *hotkey;
 	XErrorHandler old_handler = 0;
 
 	if (grabbed) return;
@@ -203,21 +204,13 @@
 	old_handler = XSetErrorHandler (x11_error_handler);
 
 	get_offending_modifiers(xdisplay);
+	hotkey = &(plugin_cfg->first);
+	while (hotkey)
+	{
+		grab_key(hotkey, xdisplay, x_root_window);
+		hotkey = hotkey->next;
+	}
 
-	grab_key(plugin_cfg->mute, xdisplay, x_root_window);
-	grab_key(plugin_cfg->vol_up, xdisplay, x_root_window);
-	grab_key(plugin_cfg->vol_down, xdisplay, x_root_window);
-	grab_key(plugin_cfg->play, xdisplay, x_root_window);
-	grab_key(plugin_cfg->pause, xdisplay, x_root_window);
-	grab_key(plugin_cfg->stop, xdisplay, x_root_window);
-	grab_key(plugin_cfg->prev_track, xdisplay, x_root_window);
-	grab_key(plugin_cfg->next_track, xdisplay, x_root_window);
-	grab_key(plugin_cfg->jump_to_file, xdisplay, x_root_window);
-	grab_key(plugin_cfg->forward, xdisplay, x_root_window);
-	grab_key(plugin_cfg->backward, xdisplay, x_root_window);
-	grab_key(plugin_cfg->toggle_win, xdisplay, x_root_window);
-	grab_key(plugin_cfg->show_aosd, xdisplay, x_root_window);
-	
 	XSync(xdisplay, False);
 	XSetErrorHandler (old_handler);
 
@@ -227,67 +220,67 @@
 
 
 /* grab required keys */
-static void ungrab_key(HotkeyConfiguration hotkey, Display* xdisplay, Window x_root_window)
+static void ungrab_key(HotkeyConfiguration *hotkey, Display* xdisplay, Window x_root_window)
 {
-	unsigned int modifier = hotkey.mask & ~(numlock_mask | capslock_mask | scrolllock_mask);
+	unsigned int modifier = hotkey->mask & ~(numlock_mask | capslock_mask | scrolllock_mask);
 	
-	if (hotkey.key == 0) return;
+	if (hotkey->key == 0) return;
 	
-	if (hotkey.type == TYPE_KEY)
+	if (hotkey->type == TYPE_KEY)
 	{
-		XUngrabKey (xdisplay, hotkey.key, modifier, x_root_window);
+		XUngrabKey (xdisplay, hotkey->key, modifier, x_root_window);
 		
 		if (modifier == AnyModifier)
 			return;
 		
 		if (numlock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | numlock_mask, x_root_window);
 	
 		if (capslock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | capslock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | capslock_mask, x_root_window);
 	
 		if (scrolllock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | scrolllock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | scrolllock_mask, x_root_window);
 	
 		if (numlock_mask && capslock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask, x_root_window);
 	
 		if (numlock_mask && scrolllock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | numlock_mask | scrolllock_mask, x_root_window);
 	
 		if (capslock_mask && scrolllock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | capslock_mask | scrolllock_mask, x_root_window);
 	
 		if (numlock_mask && capslock_mask && scrolllock_mask)
-			XUngrabKey (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
+			XUngrabKey (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
 	}
-	if (hotkey.type == TYPE_MOUSE)
+	if (hotkey->type == TYPE_MOUSE)
 	{
-		XUngrabButton (xdisplay, hotkey.key, modifier, x_root_window);
+		XUngrabButton (xdisplay, hotkey->key, modifier, x_root_window);
 		
 		if (modifier == AnyModifier)
 			return;
 		
 		if (numlock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | numlock_mask, x_root_window);
 	
 		if (capslock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | capslock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | capslock_mask, x_root_window);
 	
 		if (scrolllock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | scrolllock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | scrolllock_mask, x_root_window);
 	
 		if (numlock_mask && capslock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask, x_root_window);
 	
 		if (numlock_mask && scrolllock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | scrolllock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | numlock_mask | scrolllock_mask, x_root_window);
 	
 		if (capslock_mask && scrolllock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | capslock_mask | scrolllock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | capslock_mask | scrolllock_mask, x_root_window);
 	
 		if (numlock_mask && capslock_mask && scrolllock_mask)
-			XUngrabButton (xdisplay, hotkey.key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
+			XUngrabButton (xdisplay, hotkey->key, modifier | numlock_mask | capslock_mask | scrolllock_mask, x_root_window);
 	}
 }
 
@@ -297,6 +290,7 @@
 	Display* xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
 	Window x_root_window = GDK_WINDOW_XID(gdk_get_default_root_window());
 	PluginConfig* plugin_cfg = get_config();
+	HotkeyConfiguration *hotkey;
 
 	if (!grabbed) return;
 	if (!xdisplay) return;
@@ -306,20 +300,13 @@
 
 	get_offending_modifiers(xdisplay);
 
-	ungrab_key(plugin_cfg->mute, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->vol_up, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->vol_down, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->play, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->pause, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->stop, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->prev_track, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->next_track, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->jump_to_file, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->forward, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->backward, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->toggle_win, xdisplay, x_root_window);
-	ungrab_key(plugin_cfg->show_aosd, xdisplay, x_root_window);
-	
+	hotkey = &(plugin_cfg->first);
+	while (hotkey)
+	{
+		ungrab_key(hotkey, xdisplay, x_root_window);
+		hotkey = hotkey->next;
+	}
+
 	XSync(xdisplay, False);
 	XSetErrorHandler (old_handler);
 
@@ -332,20 +319,45 @@
 	   GdkEvent *event,
 	   gpointer data)
 {
+	HotkeyConfiguration *hotkey;
+	hotkey = &(get_config()->first);
 	switch (((XEvent*)xevent)->type)
 	{
 	case KeyPress:
 		{
 			XKeyEvent *keyevent = (XKeyEvent*)xevent;
-			if (handle_keyevent(keyevent->keycode, keyevent->state & ~(scrolllock_mask | numlock_mask | capslock_mask), TYPE_KEY))
-				return GDK_FILTER_REMOVE;
+			while (hotkey)
+			{
+				if ((hotkey->key == keyevent->keycode) &&
+				    (hotkey->mask == (keyevent->state & ~(scrolllock_mask | numlock_mask | capslock_mask))) &&
+				    (hotkey->type == TYPE_KEY))
+				{
+					if (handle_keyevent(hotkey->event))
+						return GDK_FILTER_REMOVE;
+					break;
+				}
+
+				hotkey = hotkey->next;
+			}
 			break;
 		}
 	case ButtonPress:
 		{
 			XButtonEvent *buttonevent = (XButtonEvent*)xevent;
-			if (handle_keyevent(buttonevent->button, buttonevent->state, TYPE_MOUSE))
-				return GDK_FILTER_REMOVE;
+			while (hotkey)
+			{
+				if ((hotkey->key == buttonevent->button) &&
+				    (hotkey->mask == (buttonevent->state & ~(scrolllock_mask | numlock_mask | capslock_mask))) &&
+				    (hotkey->type == TYPE_MOUSE))
+				{
+					if (handle_keyevent(hotkey->event))
+						return GDK_FILTER_REMOVE;
+					break;
+				}
+
+				hotkey = hotkey->next;
+			}
+
 			break;
 		}
 	default:
--- a/src/hotkey/gui.c	Wed Jan 23 14:12:02 2008 +0100
+++ b/src/hotkey/gui.c	Wed Jan 23 19:37:05 2008 +0100
@@ -46,36 +46,41 @@
 #include "gui.h"
 #include "grab.h"
 
-
-typedef struct {
+typedef struct _KeyControls {
 	GtkWidget *keytext;
-	HotkeyConfiguration hotkey;
-} KeyControls;
+	GtkWidget *table;
+	GtkWidget *button;
+	GtkWidget *combobox;
 
-typedef struct {
-	KeyControls play;
-	KeyControls stop;
-	KeyControls pause;
-	KeyControls prev_track;
-	KeyControls next_track;
-	KeyControls vol_up;
-	KeyControls vol_down;
-	KeyControls mute;
-	KeyControls jump_to_file;
-	KeyControls forward;
-	KeyControls backward;
-	KeyControls toggle_win;
-	KeyControls show_aosd;
-} ConfigurationControls;
-
+	HotkeyConfiguration hotkey;
+	struct _KeyControls *next, *prev, *first;
+} KeyControls;
 
 
 static void clear_keyboard (GtkWidget *widget, gpointer data);
+static void add_callback (GtkWidget *widget, gpointer data);
 static void cancel_callback (GtkWidget *widget, gpointer data);
 static void destroy_callback (GtkWidget *widget, gpointer data);
 static void ok_callback (GtkWidget *widget, gpointer data);
 
 
+static gchar* event_desc[EVENT_MAX] = {
+	[EVENT_PREV_TRACK] = N_("Previous Track"),
+	[EVENT_PLAY] = N_("Play"),
+	[EVENT_PAUSE] = N_("Pause/Resume"),
+	[EVENT_STOP] = N_("Stop"),
+	[EVENT_NEXT_TRACK] = N_("Next Track"),
+	[EVENT_FORWARD] = N_("Forward 5 Seconds"),
+	[EVENT_BACKWARD] = N_("Rewind 5 Seconds"),
+	[EVENT_MUTE] = N_("Mute"),
+	[EVENT_VOL_UP] = N_("Volume Up"),
+	[EVENT_VOL_DOWN] = N_("Volume Down"),
+	[EVENT_JUMP_TO_FILE] = N_("Jump to File"),
+	[EVENT_TOGGLE_WIN] = N_("Toggle Player Windows"),
+	[EVENT_SHOW_AOSD] = N_("Show On-Screen-Display")
+};
+
+
 static void set_keytext (GtkWidget *entry, gint key, gint mask, gint type)
 {
 	gchar *text = NULL;
@@ -132,6 +137,15 @@
 	int mod;
 
 	if (event->keyval == GDK_Tab) return FALSE;
+	if (event->keyval == GDK_Escape && ((event->state & ~GDK_LOCK_MASK) == 0)) return FALSE;
+	if (event->keyval == GDK_Return && ((event->state & ~GDK_LOCK_MASK) == 0)) return FALSE;
+	if (event->keyval == GDK_ISO_Left_Tab)
+	{
+		set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
+		return FALSE;
+	}
+	if (event->keyval == GDK_Up && ((event->state & ~GDK_LOCK_MASK) == 0)) return FALSE;
+	if (event->keyval == GDK_Down && ((event->state & ~GDK_LOCK_MASK) == 0)) return FALSE;
 
 	mod = 0;
 	is_mod = 0;
@@ -155,7 +169,10 @@
 		controls->hotkey.key = event->hardware_keycode;
 		controls->hotkey.mask = mod;
 		controls->hotkey.type = TYPE_KEY;
-	} else controls->hotkey.key = 0;
+		if (controls->next == NULL)
+			add_callback (NULL, (gpointer) controls);
+		else gtk_widget_grab_focus(GTK_WIDGET(controls->next->keytext));
+	}
 
 	set_keytext(controls->keytext, is_mod ? 0 : event->hardware_keycode, mod, TYPE_KEY);
 	return TRUE;
@@ -167,11 +184,9 @@
                            gpointer user_data)
 {
 	KeyControls *controls = (KeyControls*) user_data;
-	if (controls->hotkey.key == 0) {
-		controls->hotkey.mask = 0;
-		return TRUE;
-	}
+	if (!gtk_widget_is_focus(widget)) return FALSE;
 	set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
+
 	return TRUE;
 }
 
@@ -221,6 +236,9 @@
 	controls->hotkey.mask = mod;
         controls->hotkey.type = TYPE_MOUSE;
 	set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
+	if (controls->next == NULL) 
+		add_callback (NULL, (gpointer) controls);
+
 	return TRUE;
 }
 
@@ -263,37 +281,58 @@
 	controls->hotkey.mask = mod;
         controls->hotkey.type = TYPE_MOUSE;
 	set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
+	if (controls->next == NULL)
+		add_callback (NULL, (gpointer) controls);
 	return TRUE;
 }
 
-static void add_event_controls(GtkWidget *table, 
-				KeyControls *controls, 
+KeyControls* add_event_controls(KeyControls* list,
+				GtkWidget *table, 
 				int row, 
-				char* descr,
-				char* tooltip,
-				HotkeyConfiguration hotkey)
+				HotkeyConfiguration *hotkey)
 {
-	GtkWidget *label;
-	GtkWidget *button;
+	KeyControls *controls;
+	int i;
+
+	controls = (KeyControls*) g_malloc(sizeof(KeyControls));
+	controls->next = NULL;
+	controls->prev = list;
+	controls->first = list->first;
+	controls->table = table;
+	list->next = controls;
 
-	controls->hotkey.key = hotkey.key;
-	controls->hotkey.mask = hotkey.mask;
-	controls->hotkey.type = hotkey.type;
-	if (controls->hotkey.key == 0)
+	if (hotkey)
+	{
+		controls->hotkey.key = hotkey->key;
+		controls->hotkey.mask = hotkey->mask;
+		controls->hotkey.type = hotkey->type;
+		controls->hotkey.event = hotkey->event;
+		if (controls->hotkey.key == 0)
+			controls->hotkey.mask = 0;
+	} else {
+		controls->hotkey.key = 0;
 		controls->hotkey.mask = 0;
+		controls->hotkey.type = TYPE_KEY;
+		controls->hotkey.event = 0;
+	}
 
-	label = gtk_label_new (_(descr));
-	gtk_table_attach (GTK_TABLE (table), label, 0, 1, row, row+1, 
-			(GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
-	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
-	gtk_misc_set_padding (GTK_MISC (label), 3, 3);
-	
+	controls->combobox = gtk_combo_box_new_text();
+	for (i=0;i<EVENT_MAX;i++)
+	{
+		gtk_combo_box_append_text( GTK_COMBO_BOX(controls->combobox), event_desc[i] );
+	}
+	gtk_combo_box_set_active( GTK_COMBO_BOX(controls->combobox), controls->hotkey.event);
+	gtk_table_attach (GTK_TABLE (table), controls->combobox, 0, 1, row, row+1, 
+			(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+
+
 	controls->keytext = gtk_entry_new ();
 	gtk_table_attach (GTK_TABLE (table), controls->keytext, 1, 2, row, row+1, 
 			(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
 	gtk_entry_set_editable (GTK_ENTRY (controls->keytext), FALSE);
 
-	set_keytext(controls->keytext, hotkey.key, hotkey.mask, hotkey.type);
+
+	set_keytext(controls->keytext, controls->hotkey.key, controls->hotkey.mask, controls->hotkey.type);
 	g_signal_connect((gpointer)controls->keytext, "key_press_event",
                          G_CALLBACK(on_entry_key_press_event), controls);
 	g_signal_connect((gpointer)controls->keytext, "key_release_event",
@@ -303,25 +342,23 @@
 	g_signal_connect((gpointer)controls->keytext, "scroll_event",
                          G_CALLBACK(on_entry_scroll_event), controls);
 
-	button = gtk_button_new_with_label (_("None"));
-	gtk_table_attach (GTK_TABLE (table), button, 2, 3, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
-	g_signal_connect (G_OBJECT (button), "clicked",
+
+	controls->button = gtk_button_new();
+	gtk_button_set_image( GTK_BUTTON(controls->button),
+                        gtk_image_new_from_stock( GTK_STOCK_DELETE , GTK_ICON_SIZE_BUTTON));
+	gtk_table_attach (GTK_TABLE (table), controls->button, 2, 3, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
+	g_signal_connect (G_OBJECT (controls->button), "clicked",
 			G_CALLBACK (clear_keyboard), controls);
 
-	if (tooltip != NULL) {
-		GtkTooltips *tip = gtk_tooltips_new();
-		gtk_tooltips_set_tip(tip, controls->keytext, tooltip, NULL);
-		gtk_tooltips_set_tip(tip, button, tooltip, NULL);
-		gtk_tooltips_set_tip(tip, label, tooltip, NULL);
-	}
+	gtk_widget_grab_focus(GTK_WIDGET(controls->keytext));
+	return controls;
 }
 
 void show_configure ()
 {
-	ConfigurationControls *controls;
+	KeyControls *first_controls, *current_controls;
 	GtkWidget *window;
-	GtkWidget *main_vbox, *vbox;
-	GtkWidget *hbox;
+	GtkWidget *main_vbox, *hbox;
 	GtkWidget *alignment;
 	GtkWidget *frame;
 	GtkWidget *label;
@@ -329,6 +366,8 @@
 	GtkWidget *table;
 	GtkWidget *button_box, *button;
 	PluginConfig* plugin_cfg;
+	HotkeyConfiguration *hotkey, temphotkey;
+	int i;
 	
 	load_config ( );
 
@@ -336,19 +375,11 @@
 
 	ungrab_keys();
 	
-	controls = (ConfigurationControls*)g_malloc(sizeof(ConfigurationControls));
-	if (!controls)
-	{
-		printf ("Faild to allocate memory for ConfigurationControls structure!\n"
-			"Aborting!");
-		return;
-	}
-	
 	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
 	gtk_window_set_title (GTK_WINDOW (window), _("Global Hotkey Plugin Configuration"));
-	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER_ALWAYS);
+	gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
 	gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG);
-	gtk_window_set_resizable (GTK_WINDOW (window), FALSE);
+	gtk_window_set_resizable (GTK_WINDOW (window), TRUE);
 	gtk_container_set_border_width (GTK_CONTAINER (window), 5);
 	
 	main_vbox = gtk_vbox_new (FALSE, 4);
@@ -361,156 +392,215 @@
 	gtk_container_add (GTK_CONTAINER (alignment), hbox);
 	image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
 	gtk_box_pack_start (GTK_BOX (hbox), image, FALSE, TRUE, 0);
-	label = gtk_label_new (_("Press a key combination inside a text field."));
+	label = gtk_label_new (_("Press a key combination inside a text field.\nYou can also bind mouse buttons."));
 	gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
 	gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
 	
 	label = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (label), _("<b>Playback:</b>"));
+	gtk_label_set_markup (GTK_LABEL (label), _("Hotkeys:"));
 	frame = gtk_frame_new (NULL);
 	gtk_frame_set_label_widget (GTK_FRAME (frame), label);
-	gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
+	gtk_box_pack_start (GTK_BOX (main_vbox), frame, TRUE, TRUE, 0);
 	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
-	alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
+	alignment = gtk_alignment_new (0, 0, 1, 0);
 	gtk_container_add (GTK_CONTAINER (frame), alignment);
 	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
-	vbox = gtk_vbox_new (FALSE, 2);
-	gtk_container_add (GTK_CONTAINER (alignment), vbox);
+
+	table = gtk_table_new (1, 3, FALSE);
+	gtk_container_add (GTK_CONTAINER (alignment), table);
+
+	gtk_table_set_col_spacings (GTK_TABLE (table), 2);
+	gtk_table_set_row_spacings (GTK_TABLE (table), 0);
+
 	label = gtk_label_new (NULL);
-	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
 	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
 	gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
 	gtk_label_set_markup (GTK_LABEL (label), 
-			_("<i>Configure keys which controls Audacious playback.</i>"));
-	table = gtk_table_new (4, 3, FALSE);
-	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 2);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 2);
-
-	/* prev track */
-	add_event_controls(table, &controls->prev_track, 0, _("Previous Track:"), NULL, 
-			plugin_cfg->prev_track);
-
-	add_event_controls(table, &controls->play, 1, _("Play:"), NULL, 
-			plugin_cfg->play);
-
-	add_event_controls(table, &controls->pause, 2, _("Pause/Resume:"), NULL,
-			plugin_cfg->pause);
-
-	add_event_controls(table, &controls->stop, 3, _("Stop:"), NULL,
-			plugin_cfg->stop);
-
-	add_event_controls(table, &controls->next_track, 4, _("Next Track:"), NULL,
-			plugin_cfg->next_track);
-
-	add_event_controls(table, &controls->forward, 5, _("Forward 5 sec.:"), NULL,
-			plugin_cfg->forward);
-
-	add_event_controls(table, &controls->backward, 6, _("Rewind 5 sec.:"), NULL,
-			plugin_cfg->backward);
-
+			_("<b>Action:</b>"));
+	gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1, 
+			(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
 
 	label = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (label), _("<b>Volume Control:</b>"));
-	frame = gtk_frame_new (NULL);
-	gtk_frame_set_label_widget (GTK_FRAME (frame), label);
-	gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
-	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
-	alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
-	gtk_container_add (GTK_CONTAINER (frame), alignment);
-	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
-	vbox = gtk_vbox_new (FALSE, 2);
-	gtk_container_add (GTK_CONTAINER (alignment), vbox);
-	label = gtk_label_new (NULL);
-	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
 	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
 	gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
 	gtk_label_set_markup (GTK_LABEL (label), 
-			_("<i>Configure keys which controls music volume.</i>"));
-	table = gtk_table_new (3, 3, FALSE);
-	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 2);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 2);
-
-	add_event_controls(table, &controls->mute, 0, _("Mute:"),NULL, 
-			plugin_cfg->mute);
-
-	add_event_controls(table, &controls->vol_up, 1, _("Volume Up:"), NULL,
-			plugin_cfg->vol_up);
-
-	add_event_controls(table, &controls->vol_down, 2, _("Volume Down:"), NULL,
-			plugin_cfg->vol_down);
+			_("<b>Key Binding:</b>"));
+	gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1, 
+			(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
 
 
-	label = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (label), _("<b>Player:</b>"));
-	frame = gtk_frame_new (NULL);
-	gtk_frame_set_label_widget (GTK_FRAME (frame), label);
-	gtk_box_pack_start (GTK_BOX (main_vbox), frame, FALSE, TRUE, 0);
-	gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
-	alignment = gtk_alignment_new (0.5, 0.5, 1, 1);
-	gtk_container_add (GTK_CONTAINER (frame), alignment);
-	gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 3, 3, 3, 3);
-	vbox = gtk_vbox_new (FALSE, 2);
-	gtk_container_add (GTK_CONTAINER (alignment), vbox);
-	label = gtk_label_new (NULL);
-	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, TRUE, 0);
-	gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_CENTER);
-	gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
-	gtk_label_set_markup (GTK_LABEL (label), 
-			_("<i>Configure keys which control the player.</i>"));
-	table = gtk_table_new (3, 3, FALSE);
-	gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
-	gtk_table_set_col_spacings (GTK_TABLE (table), 2);
-	gtk_table_set_row_spacings (GTK_TABLE (table), 2);
+	hotkey = &(plugin_cfg->first);
+	i = 1;
+	first_controls = (KeyControls*) g_malloc(sizeof(KeyControls));
+	first_controls->next = NULL;
+	first_controls->prev = NULL;
+	first_controls->table = table;
+	first_controls->button = NULL;
+	first_controls->combobox = NULL;
+	first_controls->keytext = NULL;
+	first_controls->first = first_controls;
+	first_controls->hotkey.key = 0;
+	first_controls->hotkey.mask = 0;
+	first_controls->hotkey.event = 0;
+	first_controls->hotkey.type = TYPE_KEY;
+	current_controls = first_controls;
+	if (hotkey -> key != 0)
+	{
+		while (hotkey)
+		{
+			current_controls = add_event_controls(current_controls, table, i, hotkey);
+			hotkey = hotkey->next;
+			i++;
+		}
+	}
+	temphotkey.key = 0;
+	temphotkey.mask = 0;
+	temphotkey.type = TYPE_KEY;
+	if (current_controls != first_controls)
+		temphotkey.event = current_controls->hotkey.event+1;
+	else temphotkey.event = 0;
+	if (temphotkey.event >= EVENT_MAX) temphotkey.event = 0;
+	add_event_controls(current_controls, table, i, &temphotkey);
 
-	add_event_controls(table, &controls->jump_to_file, 0, _("Jump to File:"), NULL,
-			plugin_cfg->jump_to_file);
+
 
-	add_event_controls(table, &controls->toggle_win, 1, _("Toggle Player Windows:"), NULL,
-			plugin_cfg->toggle_win);
-
-	add_event_controls(table, &controls->show_aosd, 2, _("Show On-Screen-Display:"), 
-			_("For this, the Audacious OSD plugin must be activated."),
-			plugin_cfg->show_aosd);
+	hbox = gtk_hbox_new(FALSE, 0);
+	gtk_box_pack_start (GTK_BOX (main_vbox), hbox, FALSE, TRUE, 0);
 
 	button_box = gtk_hbutton_box_new ( );
-	gtk_box_pack_start (GTK_BOX (main_vbox), button_box, FALSE, TRUE, 6);
+	gtk_box_pack_start (GTK_BOX (hbox), button_box, FALSE, TRUE, 0);
+	gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_START);
+	gtk_box_set_spacing (GTK_BOX (button_box), 4);
+
+	button = gtk_button_new_from_stock (GTK_STOCK_ADD);
+	gtk_container_add (GTK_CONTAINER (button_box), button);
+	g_signal_connect (G_OBJECT (button), "clicked",
+			G_CALLBACK (add_callback), first_controls);
+
+	button_box = gtk_hbutton_box_new ( );
+	gtk_box_pack_start (GTK_BOX (hbox), button_box, TRUE, TRUE, 0);
 	gtk_button_box_set_layout (GTK_BUTTON_BOX (button_box), GTK_BUTTONBOX_END);
 	gtk_box_set_spacing (GTK_BOX (button_box), 4);
-	
+
 	button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
 	gtk_container_add (GTK_CONTAINER (button_box), button);
 	g_signal_connect (G_OBJECT (button), "clicked",
-			G_CALLBACK (cancel_callback), controls);
-	
+			G_CALLBACK (cancel_callback), NULL);
+
 	button = gtk_button_new_from_stock (GTK_STOCK_OK);
 	gtk_container_add (GTK_CONTAINER (button_box), button);
 	g_signal_connect (G_OBJECT (button), "clicked",
-			G_CALLBACK (ok_callback), controls);
+			G_CALLBACK (ok_callback), first_controls);
 
 	g_signal_connect (G_OBJECT (window), "destroy",
-		G_CALLBACK (destroy_callback), controls);
+		G_CALLBACK (destroy_callback), first_controls);
 
 	gtk_widget_show_all (GTK_WIDGET (window));
 }
 
 static void clear_keyboard (GtkWidget *widget, gpointer data)
 {
-	KeyControls *spins = (KeyControls*)data;
-	spins->hotkey.key = 0;
-	spins->hotkey.mask = 0;
-	spins->hotkey.type = TYPE_KEY;
-	set_keytext(spins->keytext, 0, 0, TYPE_KEY);
+	KeyControls *controls= (KeyControls*)data;
+	if ((controls->next == NULL) && (controls->prev->keytext == NULL))
+	{
+		controls->hotkey.key = 0;
+		controls->hotkey.mask = 0;
+		controls->hotkey.type = TYPE_KEY;
+		set_keytext(controls->keytext, 0, 0, TYPE_KEY);
+		gtk_combo_box_set_active( GTK_COMBO_BOX(controls->combobox), 0);
+		return;
+	}
+
+	if (controls->prev)
+	{
+		KeyControls* c;
+		GtkWidget* table;
+		int row;
+
+		gtk_widget_destroy(GTK_WIDGET(controls->button));
+		gtk_widget_destroy(GTK_WIDGET(controls->keytext));
+		gtk_widget_destroy(GTK_WIDGET(controls->combobox)); 
+
+		row=0;
+		c = controls->first;
+		while (c) {
+			if (c == controls) break;
+			row++;
+			c = c->next;
+		}
+		c = controls->next;
+		controls->prev->next = controls->next;
+		if (controls->next)
+			controls->next->prev = controls->prev;
+		g_free(controls);
+		if (c) table = c->table; else table = NULL;
+		while (c)
+		{
+			g_object_ref(c->combobox);
+			g_object_ref(c->keytext);
+			g_object_ref(c->button);
+
+			gtk_container_remove( GTK_CONTAINER(c->table) , c->combobox);
+			gtk_container_remove( GTK_CONTAINER(c->table) , c->keytext);
+			gtk_container_remove( GTK_CONTAINER(c->table) , c->button);
+
+			gtk_table_attach (GTK_TABLE (c->table), c->combobox, 0, 1, row, row+1, 
+					(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+			gtk_table_attach (GTK_TABLE (c->table), c->keytext, 1, 2, row, row+1, 
+					(GtkAttachOptions) (GTK_FILL|GTK_EXPAND), (GtkAttachOptions) (GTK_EXPAND), 0, 0);
+			gtk_table_attach (GTK_TABLE (c->table), c->button, 2, 3, row, row+1, (GtkAttachOptions) (GTK_FILL), (GtkAttachOptions) (0), 0, 0);
+
+			g_object_unref(c->combobox);
+			g_object_unref(c->keytext);
+			g_object_unref(c->button);
+
+			c = c->next;
+			row++;
+		}
+		if (table)
+			gtk_widget_show_all (GTK_WIDGET (table));
+
+		return;
+	}
+}
+
+void add_callback (GtkWidget *widget, gpointer data)
+{
+	KeyControls* controls = (KeyControls*)data;
+	HotkeyConfiguration temphotkey;
+	int count;
+	if (controls == NULL) return;
+	if ((controls->next == NULL)&&(controls->hotkey.event+1 == EVENT_MAX)) return;
+	controls = controls->first;
+	if (controls == NULL) return;
+	count = 1;
+	while (controls->next) {
+		controls = controls->next;
+		count = count + 1;
+	}
+	temphotkey.key = 0;
+	temphotkey.mask = 0;
+	temphotkey.type = TYPE_KEY;
+	temphotkey.event = controls->hotkey.event+1;
+	if (temphotkey.event >= EVENT_MAX) temphotkey.event = 0;
+	gtk_table_resize(GTK_TABLE(controls->table), count, 3);
+	add_event_controls(controls, controls->table, count, &temphotkey);
+	gtk_widget_show_all (GTK_WIDGET (controls->table));
 }
 
 void destroy_callback (GtkWidget *widget, gpointer data)
 {
+	KeyControls* controls = (KeyControls*)data;
 	if (is_loaded())
 	{
 		grab_keys ();
 	}
-	if (data) g_free(data);
+	while (controls) {
+		KeyControls *old;
+		old = controls;
+		controls = controls->next;
+		g_free(old);
+	}
 }
 
 void cancel_callback (GtkWidget *widget, gpointer data)
@@ -520,23 +610,42 @@
 
 void ok_callback (GtkWidget *widget, gpointer data)
 {
-	ConfigurationControls *controls= (ConfigurationControls*)data;
+	KeyControls *controls = (KeyControls*)data;
 	PluginConfig* plugin_cfg = get_config();
+	HotkeyConfiguration *hotkey;
+	
+	hotkey = &(plugin_cfg->first);
+	hotkey = hotkey->next;
+	while (hotkey)
+	{
+		HotkeyConfiguration * old;
+		old = hotkey;
+		hotkey = hotkey->next;
+		free(old);
+	}
+	plugin_cfg->first.next = NULL;
+	plugin_cfg->first.key = 0;
+	plugin_cfg->first.event = 0;
+	plugin_cfg->first.mask = 0;
 
-	plugin_cfg->play = controls->play.hotkey;
-	plugin_cfg->pause = controls->pause.hotkey;
-	plugin_cfg->stop= controls->stop.hotkey;
-	plugin_cfg->prev_track= controls->prev_track.hotkey;
-	plugin_cfg->next_track = controls->next_track.hotkey;
-	plugin_cfg->forward = controls->forward.hotkey;
-	plugin_cfg->backward = controls->backward.hotkey;
-	plugin_cfg->vol_up= controls->vol_up.hotkey;
-	plugin_cfg->vol_down = controls->vol_down.hotkey;
-	plugin_cfg->mute = controls->mute.hotkey;
-	plugin_cfg->jump_to_file= controls->jump_to_file.hotkey;
-	plugin_cfg->toggle_win = controls->toggle_win.hotkey;
-	plugin_cfg->show_aosd = controls->show_aosd.hotkey;
-	
+	hotkey = &(plugin_cfg->first);
+	while (controls)
+	{
+		if (controls->hotkey.key) {
+			if (hotkey->key) {
+				hotkey->next = (HotkeyConfiguration*)
+					malloc(sizeof (HotkeyConfiguration));
+				hotkey = hotkey->next;
+				hotkey->next = NULL;
+			}
+			hotkey->key = controls->hotkey.key;
+			hotkey->mask = controls->hotkey.mask;
+			hotkey->event = gtk_combo_box_get_active( GTK_COMBO_BOX(controls->combobox) );
+			hotkey->type = controls->hotkey.type;
+		}
+		controls = controls->next;
+	}
+
 	save_config ( );
 	
 	gtk_widget_destroy (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
--- a/src/hotkey/plugin.c	Wed Jan 23 14:12:02 2008 +0100
+++ b/src/hotkey/plugin.c	Wed Jan 23 19:37:05 2008 +0100
@@ -94,7 +94,7 @@
 }
 
 /* handle keys */
-gboolean handle_keyevent (int keycode, int state, int type)
+gboolean handle_keyevent (EVENT event)
 {
 	gint current_volume, old_volume;
 	static gint volume_static = 0;
@@ -116,7 +116,7 @@
 	}
 
 	/* mute the playback */
-	if ((keycode == plugin_cfg.mute.key) && (state == plugin_cfg.mute.mask) && (type == plugin_cfg.mute.type))
+	if (event == EVENT_MUTE)
 	{
 		if (!mute)
 		{
@@ -131,7 +131,7 @@
 	}
 	
 	/* decreace volume */
-	if ((keycode == plugin_cfg.vol_down.key) && (state == plugin_cfg.vol_down.mask) && (type == plugin_cfg.vol_down.type))
+	if (event == EVENT_VOL_DOWN)
 	{
 		if (mute)
 		{
@@ -155,7 +155,7 @@
 	}
 	
 	/* increase volume */
-	if ((keycode == plugin_cfg.vol_up.key) && (state == plugin_cfg.vol_up.mask) && (type == plugin_cfg.vol_up.type))
+	if (event == EVENT_VOL_UP)
 	{
 		if (mute)
 		{
@@ -179,14 +179,14 @@
 	}
 	
 	/* play */
-	if ((keycode == plugin_cfg.play.key) && (state == plugin_cfg.play.mask) && (type == plugin_cfg.play.type))
+	if (event == EVENT_PLAY)
 	{
 		audacious_drct_play ();
 		return TRUE;
 	}
 
 	/* pause */
-	if ((keycode == plugin_cfg.pause.key) && (state == plugin_cfg.pause.mask) && (type == plugin_cfg.pause.type))
+	if (event == EVENT_PAUSE)
 	{
 		if (!play) audacious_drct_play ();
 		else audacious_drct_pause ();
@@ -195,28 +195,28 @@
 	}
 	
 	/* stop */
-	if ((keycode == plugin_cfg.stop.key) && (state == plugin_cfg.stop.mask) && (type == plugin_cfg.stop.type))
+	if (event == EVENT_STOP)
 	{
 		audacious_drct_stop ();
 		return TRUE;
 	}
 	
 	/* prev track */	
-	if ((keycode == plugin_cfg.prev_track.key) && (state == plugin_cfg.prev_track.mask) && (type == plugin_cfg.prev_track.type))
+	if (event == EVENT_PREV_TRACK)
 	{
 		audacious_drct_playlist_prev ();
 		return TRUE;
 	}
 	
 	/* next track */
-	if ((keycode == plugin_cfg.next_track.key) && (state == plugin_cfg.next_track.mask) && (type == plugin_cfg.next_track.type))
+	if (event == EVENT_NEXT_TRACK)
 	{
 		audacious_drct_playlist_next ();
 		return TRUE;
 	}
 
 	/* forward */
-	if ((keycode == plugin_cfg.forward.key) && (state == plugin_cfg.forward.mask) && (type == plugin_cfg.forward.type))
+	if (event == EVENT_FORWARD)
 	{
 		gint time = audacious_drct_get_output_time();
 		time += 5000; /* Jump 5s into future */
@@ -225,7 +225,7 @@
 	}
 
 	/* backward */
-	if ((keycode == plugin_cfg.backward.key) && (state == plugin_cfg.backward.mask) && (type == plugin_cfg.backward.type))
+	if (event == EVENT_BACKWARD)
 	{
 		gint time = audacious_drct_get_output_time();
 		if (time > 5000) time -= 5000; /* Jump 5s back */
@@ -235,14 +235,14 @@
 	}
 
 	/* Open Jump-To-File dialog */
-	if ((keycode == plugin_cfg.jump_to_file.key) && (state == plugin_cfg.jump_to_file.mask) && (type == plugin_cfg.jump_to_file.type))
+	if (event == EVENT_JUMP_TO_FILE)
 	{
 		audacious_drct_show_jtf_box();
 		return TRUE;
 	}
 
 	/* Toggle Windows */
-	if ((keycode == plugin_cfg.toggle_win.key) && (state == plugin_cfg.toggle_win.mask) && (type == plugin_cfg.toggle_win.type))
+	if (event == EVENT_TOGGLE_WIN)
 	{
 		static gboolean is_main, is_eq, is_pl;
 		is_main = audacious_drct_main_win_is_visible();
@@ -262,7 +262,7 @@
 	}
 
 	/* Show OSD through AOSD plugin*/
-	if ((keycode == plugin_cfg.show_aosd.key) && (state == plugin_cfg.show_aosd.mask) && (type == plugin_cfg.show_aosd.type))
+	if (event == EVENT_SHOW_AOSD)
 	{
 		aud_hook_call("aosd toggle", NULL);
 		return TRUE;
@@ -271,40 +271,106 @@
 	return FALSE;
 }
 
+void add_hotkey(HotkeyConfiguration** pphotkey, KeySym keysym, gint mask, gint type, EVENT event)
+{
+	KeyCode keycode;
+	HotkeyConfiguration *photkey;
+	if (keysym == 0) return;
+	if (pphotkey == NULL) return;
+	photkey = *pphotkey;
+	if (photkey == NULL) return;
+	keycode = XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), keysym);
+	if (keycode == 0) return;
+	if (photkey->key) {
+		photkey->next = (HotkeyConfiguration*)
+			malloc(sizeof (HotkeyConfiguration));
+		photkey = photkey->next;
+		*pphotkey = photkey;
+		photkey->next = NULL;
+	}
+	photkey->key = (gint)keycode;
+	photkey->mask = mask;
+	photkey->event = event;
+	photkey->type = type;
+}
+
+void load_defaults (void)
+{
+	HotkeyConfiguration* hotkey;
+
+	hotkey = &(plugin_cfg.first);
+
+	add_hotkey(&hotkey, XF86XK_AudioPrev, 0, TYPE_KEY, EVENT_PREV_TRACK);
+	add_hotkey(&hotkey, XF86XK_AudioPlay, 0, TYPE_KEY, EVENT_PLAY);
+	add_hotkey(&hotkey, XF86XK_AudioPause, 0, TYPE_KEY, EVENT_PAUSE);
+	add_hotkey(&hotkey, XF86XK_AudioStop, 0, TYPE_KEY, EVENT_STOP);
+	add_hotkey(&hotkey, XF86XK_AudioNext, 0, TYPE_KEY, EVENT_NEXT_TRACK);
+
+/*	add_hotkey(&hotkey, XF86XK_AudioRewind, 0, TYPE_KEY, EVENT_BACKWARD); */
+
+	add_hotkey(&hotkey, XF86XK_AudioMute, 0, TYPE_KEY, EVENT_MUTE);
+	add_hotkey(&hotkey, XF86XK_AudioRaiseVolume, 0, TYPE_KEY, EVENT_VOL_UP);
+	add_hotkey(&hotkey, XF86XK_AudioLowerVolume, 0, TYPE_KEY, EVENT_VOL_DOWN);
+
+/*	add_hotkey(&hotkey, XF86XK_AudioMedia, 0, TYPE_KEY, EVENT_JUMP_TO_FILE);
+	add_hotkey(&hotkey, XF86XK_Music, 0, TYPE_KEY, EVENT_TOGGLE_WIN); */
+}
+
 /* load plugin configuration */
 void load_config (void)
 {
 	ConfigDb *cfdb;
+	HotkeyConfiguration *hotkey;
+	int i,max;
 	
 	/* default volume level */
 	plugin_cfg.vol_increment = 4;
 	plugin_cfg.vol_decrement = 4;
 
-#define load_key(hotkey,default) \
-	plugin_cfg.hotkey.key = (default)?(XKeysymToKeycode(GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), (default))):0; \
-	plugin_cfg.hotkey.mask = 0; \
-	plugin_cfg.hotkey.type = TYPE_KEY; \
-	aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey, &plugin_cfg.hotkey.key); \
-	aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_mask", &plugin_cfg.hotkey.mask); \
-	aud_cfg_db_get_int (cfdb, "globalHotkey", #hotkey "_type", &plugin_cfg.hotkey.type);
-
-
 	/* open configuration database */
 	cfdb = aud_cfg_db_open ( );
+	hotkey = &(plugin_cfg.first);
+	hotkey->next = NULL;
+	hotkey->key = 0;
+	hotkey->mask = 0;
+	hotkey->event = 0;
+	hotkey->type = TYPE_KEY;
+	max = 0;
+	aud_cfg_db_get_int (cfdb, "globalHotkey", "NumHotkeys", &max);
+	if (max == 0)
+		load_defaults();
+	else for (i=0; i<max; i++)
+	{
+		gchar *text = NULL;
+		gint value;
+		if (hotkey->key) {
+			hotkey->next = (HotkeyConfiguration*)
+				malloc(sizeof (HotkeyConfiguration));
+			hotkey = hotkey->next;
+			hotkey->next = NULL;
+			hotkey->key = 0;
+			hotkey->mask = 0;
+			hotkey->event = 0;
+			hotkey->type = TYPE_KEY;
+		}
+		text = g_strdup_printf("Hotkey_%d_key", i);
+		aud_cfg_db_get_int (cfdb, "globalHotkey", text, &(hotkey->key));
+		g_free(text);
 
-	load_key(mute, XF86XK_AudioMute);
-	load_key(vol_down, XF86XK_AudioLowerVolume);
-	load_key(vol_up, XF86XK_AudioRaiseVolume);
-	load_key(play, XF86XK_AudioPlay);
-	load_key(pause, XF86XK_AudioPause);
-	load_key(stop, XF86XK_AudioStop);
-	load_key(prev_track, XF86XK_AudioPrev);
-	load_key(next_track, XF86XK_AudioNext);
-	load_key(jump_to_file, XF86XK_AudioMedia);
-	load_key(toggle_win, 0);
-	load_key(forward, 0);
-	load_key(backward, XF86XK_AudioRewind);
-	load_key(show_aosd, 0);
+		text = g_strdup_printf("Hotkey_%d_mask", i);
+		aud_cfg_db_get_int (cfdb, "globalHotkey", text, &(hotkey->mask));
+		g_free(text);
+
+		text = g_strdup_printf("Hotkey_%d_type", i);
+		aud_cfg_db_get_int (cfdb, "globalHotkey", text, &(hotkey->type));
+		g_free(text);
+
+		text = g_strdup_printf("Hotkey_%d_event", i);
+		value = (gint)hotkey->event;
+		aud_cfg_db_get_int (cfdb, "globalHotkey", text, &value);
+		hotkey->event = (EVENT) value;
+		g_free(text);
+	}
 
 	aud_cfg_db_close (cfdb);
 }
@@ -313,37 +379,60 @@
 void save_config (void)
 {
 	ConfigDb *cfdb;
+	int max;
+	HotkeyConfiguration *hotkey;
 
-#define save_key(hotkey) \
-	aud_cfg_db_set_int (cfdb, "globalHotkey", #hotkey, plugin_cfg.hotkey.key); \
-	aud_cfg_db_set_int (cfdb, "globalHotkey", #hotkey "_mask", plugin_cfg.hotkey.mask); \
-	aud_cfg_db_set_int (cfdb, "globalHotkey", #hotkey "_type", plugin_cfg.hotkey.type);
-	
 	/* open configuration database */
 	cfdb = aud_cfg_db_open ( );
-	
-	save_key(mute);
-	save_key(vol_up);
-	save_key(vol_down);
-	save_key(play);
-	save_key(pause);
-	save_key(stop);
-	save_key(prev_track);
-	save_key(next_track);
-	save_key(jump_to_file);
-	save_key(forward);
-	save_key(backward);
-	save_key(toggle_win);
-	save_key(show_aosd);
+	hotkey = &(plugin_cfg.first);
+	max = 0;
+	while (hotkey) {
+		gchar *text = NULL;
+		if (hotkey->key) {
+			text = g_strdup_printf("Hotkey_%d_key", max);
+			aud_cfg_db_set_int (cfdb, "globalHotkey", text, hotkey->key);
+			g_free(text);
+
+			text = g_strdup_printf("Hotkey_%d_mask", max);
+			aud_cfg_db_set_int (cfdb, "globalHotkey", text, hotkey->mask);
+			g_free(text);
+
+			text = g_strdup_printf("Hotkey_%d_type", max);
+			aud_cfg_db_set_int (cfdb, "globalHotkey", text, hotkey->type);
+			g_free(text);
+
+			text = g_strdup_printf("Hotkey_%d_event", max);
+			aud_cfg_db_set_int (cfdb, "globalHotkey", text, hotkey->event);
+			g_free(text);
+			max++;
+		}
+
+		hotkey = hotkey->next;
+	}
+	aud_cfg_db_set_int (cfdb, "globalHotkey", "NumHotkeys", max);
 
 	aud_cfg_db_close (cfdb);
 }
 
 static void cleanup (void)
 {
+	HotkeyConfiguration* hotkey;
 	if (!loaded) return;
 	ungrab_keys ();
 	release_filter();
+	hotkey = &(plugin_cfg.first);
+	hotkey = hotkey->next;
+	while (hotkey)
+	{
+		HotkeyConfiguration * old;
+		old = hotkey;
+		hotkey = hotkey->next;
+		free(old);
+	}
+	plugin_cfg.first.next = NULL;
+	plugin_cfg.first.key = 0;
+	plugin_cfg.first.event = 0;
+	plugin_cfg.first.mask = 0;
 	loaded = FALSE;
 }
 
--- a/src/hotkey/plugin.h	Wed Jan 23 14:12:02 2008 +0100
+++ b/src/hotkey/plugin.h	Wed Jan 23 19:37:05 2008 +0100
@@ -6,9 +6,31 @@
 #define TYPE_KEY 0
 #define TYPE_MOUSE 1
 
-typedef struct {
+typedef enum {
+	EVENT_PREV_TRACK = 0,
+	EVENT_PLAY,
+	EVENT_PAUSE,
+	EVENT_STOP,
+	EVENT_NEXT_TRACK,
+
+	EVENT_FORWARD,
+	EVENT_BACKWARD,
+	EVENT_MUTE,
+	EVENT_VOL_UP,
+	EVENT_VOL_DOWN,
+	EVENT_JUMP_TO_FILE,
+	EVENT_TOGGLE_WIN,
+	EVENT_SHOW_AOSD,
+
+	EVENT_MAX 
+} EVENT;
+
+
+typedef struct _HotkeyConfiguration {
 	gint key, mask;
 	gint type;
+	EVENT event;
+	struct _HotkeyConfiguration *next;
 } HotkeyConfiguration;
 
 typedef struct {
@@ -16,25 +38,15 @@
 	gint vol_decrement;
 	
 	/* keyboard */
-	HotkeyConfiguration mute;
-	HotkeyConfiguration vol_down;
-	HotkeyConfiguration vol_up;
-	HotkeyConfiguration play;
-	HotkeyConfiguration stop;
-	HotkeyConfiguration pause;
-	HotkeyConfiguration prev_track;
-	HotkeyConfiguration next_track;
-	HotkeyConfiguration jump_to_file;
-	HotkeyConfiguration toggle_win;
-	HotkeyConfiguration forward;
-	HotkeyConfiguration backward;
-	HotkeyConfiguration show_aosd;
+	HotkeyConfiguration first;
 } PluginConfig;
 
+
+
 void load_config (void);
 void save_config (void);
 PluginConfig* get_config(void);
 gboolean is_loaded (void);
-gboolean handle_keyevent(int keycode, int state, int type);
+gboolean handle_keyevent(EVENT event);
 
 #endif