/***************************************************************************
                          configdialog.c  -  configuration dialog
                             -------------------
    begin                : Fri Oct 18 2002
    copyright            : (C) 2002 by blight
    email                : blight@Ashitaka
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#ifdef GUI_GTK

#include "winlnxdefs.h"
#include "Controller_1.1.h"
#include "configdialog_gtk.h"
#include "pad.xpm"
#include "plugin.h"
#include <stdlib.h>
#include <glib.h>
#include <gtk/gtk.h>
#include "SDL.h"
#include "SDL_thread.h"
#include "SDL_ttf.h"

#include "arial.ttf.h" // arial font
#define FONT_SIZEPT 18

// functions
extern void read_configuration( void ); // from plugin.c
extern int write_configuration( void ); // from plugin.c

static int open_joystick( int device );
static void close_joystick( void );
static void fill_text_entry( int button );

static GtkWidget *window = NULL;
static GtkWidget *pluggedCheckbox = NULL;
static GtkWidget *mempakCheckbox = NULL;
static GtkWidget *deviceCombo = NULL;
static GtkWidget *mapEntries[NUM_BUTTONS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };

static int window_shown = 0;
static int controller = 0;	// which controller to configure
static SController config[4];
static SController *g_Controller = NULL;
static SDL_Joystick *joystick = NULL;	// opened joystick
static TTF_Font *font = NULL;

static const char *button_names[] = {
	"DPad R",	// R_DPAD
	"DPad L",	// L_DPAD
	"DPad D",	// D_DPAD
	"DPad U",	// U_DPAD
	"Start",	// START_BUTTON
	"Z Trig",	// Z_TRIG
	"B Button",	// B_BUTTON
	"A Button",	// A_BUTTON
	"C Button R",	// R_CBUTTON
	"C Button L",	// L_CBUTTON
	"C Button D",	// D_CBUTTON
	"C Button U",	// U_CBUTTON
	"R Trig",	// R_TRIG
	"L Trig",	// L_TRIG
	"Y Axis",	// Y_AXIS
	"X Axis"	// X_AXIS
};

// function to create pixmaps from buffers
static GtkWidget *
create_pixmap_d                        (GtkWidget       *widget,
                                        gchar          **data)
{
	GdkColormap *colormap;
	GdkPixmap *gdkpixmap;
	GdkBitmap *mask;
	GtkWidget *pixmap;

	colormap = gtk_widget_get_colormap (widget);
	gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL, colormap, &mask,
                                                     NULL, data);
	pixmap = gtk_pixmap_new (gdkpixmap, mask);
	gdk_pixmap_unref (gdkpixmap);
	gdk_bitmap_unref (mask);

	return pixmap;
}

static void
ok_clicked(
	GtkWidget *widget,
	gpointer   data )
{
	int i;

	SDL_QuitSubSystem( SDL_INIT_JOYSTICK );

	// save configuration
	for( i = 0; i < 4; i++ )
	{
		g_Controller[i].device = config[i].device;
		memcpy( g_Controller[i].axis, config[i].axis, sizeof( SAxisMap ) * 2 );
		memcpy( g_Controller[i].button, config[i].button, sizeof( SButtonMap ) * 14 );
		memcpy( &g_Controller[i].control, &config[i].control, sizeof( CONTROL ) );
	}
	write_configuration();

	// hide dialog
	gtk_widget_hide_all( window );
	window_shown = 0;
}

static void
cancel_clicked(
	GtkWidget *widget,
	gpointer   data )
{
	SDL_QuitSubSystem( SDL_INIT_JOYSTICK );

	// hide dialog
	gtk_widget_hide_all( window );
	window_shown = 0;
}

static void
update_window( void )
{
	int i;
	const char *text;

	if( !window )
		return;

	// update window
	for( i = 0; i < NUM_BUTTONS; i++ )
		fill_text_entry( i );

	gdk_threads_enter();

	// update device combo, plugged and mempak checkboxes
	if( config[controller].device <= DEVICE_NONE )
		text = "None";
	else if( config[controller].device == DEVICE_KEYBOARD )
		text = "Keyboard";
	else
		text = SDL_JoystickName( config[controller].device );
	gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(deviceCombo)->entry), text );

	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(pluggedCheckbox), config[controller].control.Present );
	gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mempakCheckbox), (config[controller].control.Plugin == PLUGIN_MEMPAK) );

	gdk_threads_leave();
}

static int HACK = 0; // i'm pretty sure there's a gtk function to avoid the device_changed callback

static void
device_changed(
	GtkWidget *widget,
	gpointer   data )
{
	const char *name;
	int i, device = DEVICE_NONE;

	if( HACK )
		return;

	if( !window_shown )
		return;

	name = gtk_entry_get_text( GTK_ENTRY(widget) );
	if( !strcmp( name, "Keyboard" ) )
		device = DEVICE_KEYBOARD;
	else
		for( i = 0; i < SDL_NumJoysticks(); i++ )
			if( !strcmp( name, SDL_JoystickName( i ) ) )
			{
				device = i;
				break;
			}

	if( device >= 0 )
	{
		close_joystick();
		if( open_joystick( device ) == -1 )
		{
			device = DEVICE_NONE;
			gtk_entry_set_text( GTK_ENTRY(widget), "None" );
		}
	}

	config[controller].device = device;
}

static void
controller_changed(
	GtkWidget *widget,
	gpointer   data )
{
	int i = -1;
	const char *text = gtk_entry_get_text( GTK_ENTRY(widget) );

	if( !window_shown )
		return;

	if( !text )
		return;

	if( text[0] )
		i = text[1] - '1';

	if( i < 0 || i > 3 )
		return;

	controller = i;

	// block the device_changed signal
	HACK = 1;
	update_window();
	if( config[controller].device >= 0 )
	{
		close_joystick();
		if( open_joystick( config[controller].device ) == -1 )
		{
			config[controller].device = DEVICE_NONE;
			gtk_entry_set_text( GTK_ENTRY(GTK_COMBO(deviceCombo)->entry), "None" );
		}
	}
	HACK = 0;
}

static void
plugged_toggled(
	GtkWidget *toggle,
	gpointer   data )
{
	config[controller].control.Present = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(toggle) );
}

static void
mempak_toggled(
	GtkWidget *toggle,
	gpointer   data )
{
	config[controller].control.Plugin = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(toggle) ) ? PLUGIN_MEMPAK : PLUGIN_NONE;
}

static char axis_names[] = {
	'X',
	'Y',
	'Z',
	'U',
	'V',
	'W'
};

static void
fill_text_entry(
	int button )
{
	char desc[1024];
	int axis;
	const char *cKey_a, *cKey_b;
	char cButton_a[100], cButton_b[100], cAxis[100];

	strcpy( desc, "Unassigned" );
	if( button == X_AXIS || button == Y_AXIS )
	{
		axis = button - Y_AXIS;

		cKey_a = (config[controller].axis[axis].key_a == SDLK_UNKNOWN) ?
			"None" : SDL_GetKeyName( config[controller].axis[axis].key_a );
		cKey_b = (config[controller].axis[axis].key_b == SDLK_UNKNOWN) ?
			"None" : SDL_GetKeyName( config[controller].axis[axis].key_b );
		strcpy( cButton_a, "None" );
		strcpy( cButton_b, "None" );
		strcpy( cAxis, "None" );
		if( config[controller].axis[axis].button_a >= 0 )
			sprintf( cButton_a, "%d", config[controller].axis[axis].button_a );
		if( config[controller].axis[axis].button_b >= 0 )
			sprintf( cButton_b, "%d", config[controller].axis[axis].button_b );
		if( config[controller].axis[axis].axis >= 0 )
			sprintf( cAxis, "%c", axis_names[config[controller].axis[axis].axis] );

		sprintf( desc, "key(%s,%s); button(%s,%s); axis(%s)",
				cKey_a, cKey_b, cButton_a, cButton_b, cAxis );
	}
	else
	{
		cKey_a = (config[controller].button[button].key == SDLK_UNKNOWN) ?
			"None" : SDL_GetKeyName( config[controller].button[button].key );
		strcpy( cButton_a, "None" );
		strcpy( cAxis, "None" );
		if( config[controller].button[button].button >= 0 )
			sprintf( cButton_a, "%d", config[controller].button[button].button );
		if( config[controller].button[button].axis >= 0 )
			sprintf( cAxis, "%c%c", axis_names[config[controller].button[button].axis],
					(config[controller].button[button].axis_dir > 0) ? '+' : '-' );

		sprintf( desc, "key(%s); button(%s); axis(%s)", cKey_a, cButton_a, cAxis );
	}
	gdk_threads_enter();
	gtk_entry_set_text( GTK_ENTRY(mapEntries[button]), desc );
	gdk_threads_leave();
}

struct arg
{
	GtkWidget *widget;
	int        button;
};

/**
 * display a dialog telling the user to press a button
 */
static int
display_dialog( const char *text, const char *caption )
{
	SDL_Surface *screen, *label;
	SDL_Rect dstrect;
	SDL_RWops *rw;
	SDL_Color black = { 0x00, 0x00, 0x00, 0 };
	SDL_Color gray  = { 0xAA, 0xAA, 0xAA, 0 };

	// init sdl
	if( !SDL_WasInit( SDL_INIT_VIDEO ) )
		if( SDL_InitSubSystem( SDL_INIT_VIDEO ) < 0 )
		{
			fprintf( stderr, "["PLUGIN_NAME"]: Couldn't init SDL video subsystem: %s\n", SDL_GetError() );
			return;
		}
	SDL_JoystickEventState( SDL_ENABLE );

	// init sdl_ttf2
	if( !TTF_WasInit() )
		if( TTF_Init() < 0 )
		{
			fprintf( stderr, "["PLUGIN_NAME"]: Couldn't init TTF library: %s\n", SDL_GetError() );
			SDL_JoystickEventState( SDL_DISABLE );
			SDL_QuitSubSystem( SDL_INIT_VIDEO );
			return;
		}

	// open font
	rw = SDL_RWFromMem( arial.data, arial.size );
	font = TTF_OpenFontRW( rw, 0, FONT_SIZEPT );
	if( font == NULL )
	{
		fprintf( stderr, "["PLUGIN_NAME"]: Couldn't load %d pt font: %s\n", FONT_SIZEPT, SDL_GetError() );
		TTF_Quit();
		SDL_JoystickEventState( SDL_DISABLE );
		SDL_QuitSubSystem( SDL_INIT_VIDEO );
		return;
	}
	TTF_SetFontStyle( font, TTF_STYLE_NORMAL );

	// draw text
	label = TTF_RenderText_Shaded( font, text, gray, black );
	if( label == NULL )
	{
		fprintf( stderr, "["PLUGIN_NAME"]: Couldn't render text: %s\n", SDL_GetError() );
		TTF_Quit();
		SDL_JoystickEventState( SDL_DISABLE );
		SDL_QuitSubSystem( SDL_INIT_VIDEO );
		return;
	}

	// display text
	screen = SDL_SetVideoMode( label->w + 80, label->h + 80, 0, SDL_SWSURFACE );
	SDL_FillRect( screen, NULL, SDL_MapRGB( screen->format, 0, 0, 0 ) );
	SDL_WM_SetCaption( caption, NULL );

	dstrect.x = dstrect.y = 40;
	dstrect.w = label->w;
	dstrect.h = label->h;

	SDL_BlitSurface( label, NULL, screen, &dstrect );
	SDL_FreeSurface( label );
	SDL_Flip( screen );
}

static void
hide_dialog( void )
{
	TTF_CloseFont( font );
	TTF_Quit();
	SDL_JoystickEventState( SDL_DISABLE );
	SDL_QuitSubSystem( SDL_INIT_VIDEO );
}

static int
open_joystick( int device )
{
	close_joystick();

	joystick = SDL_JoystickOpen( device );
	if( joystick == NULL )
	{
		fprintf( stderr, "["PLUGIN_NAME"]: Couldn't open joystick #%d (%s): %s\n",
				device, SDL_JoystickName( device ), SDL_GetError() );
		return -1;
	}
	return 0;
}

static void
close_joystick( void )
{
	if( joystick )
		SDL_JoystickClose( joystick );
}

static int
read_assignment_thread(
	void *_arg )
{
	GtkWidget *widget = ((struct arg *)_arg)->widget;
	int button = ((struct arg *)_arg)->button;
	int axis;

	SDL_Event event;
	char text[1000];
	int waitevent = 1;

	free( _arg );

	if( button == X_AXIS || button == Y_AXIS )
	{
		// read key/button a
		sprintf( text, "Move any axis or press a key/button for '%s'",
				(button == X_AXIS) ? "left" : "up" );
		axis = button - Y_AXIS;
		display_dialog( text, button_names[button] );
		while( waitevent )
		{
			if( SDL_WaitEvent( &event ) == 0 )
			{
				fprintf( stderr, "["PLUGIN_NAME"]: SDL_WaitEvent(): %s\n", SDL_GetError() );
				hide_dialog();
				return;
			}

			switch( event.type )
			{
			case SDL_KEYDOWN:
				config[controller].axis[axis].key_a = event.key.keysym.sym;
				waitevent = 0;
				break;
			case SDL_JOYAXISMOTION:
				if( event.jaxis.which == config[controller].device )
				{
					if( event.jaxis.value >= 15000 || event.jaxis.value <= -15000 )
					{
	               		 		config[controller].axis[axis].axis = event.jaxis.axis;
						hide_dialog();

						// update text entry
						fill_text_entry( button );

						return;
					}
				}
				break;
			case SDL_JOYBUTTONDOWN:
				if( event.jbutton.which == config[controller].device )
				{
	                		config[controller].axis[axis].button_a = event.jbutton.button;
					waitevent = 0;
				}
				break;
			}
		}
		hide_dialog();

		// read key/button b
		sprintf( text, "Press a key/button for '%s'",
				(button == X_AXIS) ? "right" : "down" );
		display_dialog( text, button_names[button] );

		waitevent = 1;
		while( waitevent )
		{
			if( SDL_WaitEvent( &event ) == 0 )
			{
				fprintf( stderr, "["PLUGIN_NAME"]: SDL_WaitEvent(): %s\n", SDL_GetError() );
				hide_dialog();

				// update text entry
				fill_text_entry( button );

				return;
			}

			switch( event.type )
			{
			case SDL_KEYDOWN:
				config[controller].axis[axis].key_b = event.key.keysym.sym;
				waitevent = 0;
				break;
			case SDL_JOYBUTTONDOWN:
				if( event.jbutton.which == config[controller].device )
				{
	                		config[controller].axis[axis].button_b = event.jbutton.button;
					waitevent = 0;
				}
				break;
			}
		}
		hide_dialog();

		// update text entry
		fill_text_entry( button );
	}
	else
	{
		strcpy( text, "Press a key/button or move any axis..." );
		display_dialog( text, button_names[button] );

		while( waitevent )
		{
			if( SDL_WaitEvent( &event ) == 0 )
			{
				fprintf( stderr, "["PLUGIN_NAME"]: SDL_WaitEvent(): %s\n", SDL_GetError() );
				hide_dialog();
				return;
			}
			switch( event.type )
			{
			case SDL_KEYDOWN:
				config[controller].button[button].key = event.key.keysym.sym;
				waitevent = 0;
				break;
			case SDL_JOYBUTTONDOWN:
				if( event.jbutton.which == config[controller].device )
				{
	                		config[controller].button[button].button = event.jbutton.button;
					waitevent = 0;
				}
				break;
			case SDL_JOYAXISMOTION:
				if( event.jaxis.which == config[controller].device )
				{
					if( event.jaxis.value >= 15000 )
					{
		                		config[controller].button[button].axis = event.jaxis.axis;
		                		config[controller].button[button].axis_dir = 1;
						waitevent = 0;
					}
					else if( event.jaxis.value <= -15000 )
					{
		                		config[controller].button[button].axis = event.jaxis.axis;
		                		config[controller].button[button].axis_dir = -1;
						waitevent = 0;
					}
				}
				break;
			}
		}
		hide_dialog();

		// update text entry
		fill_text_entry( button );
	}
}

#define READ_ASSIGNMENT(blah)						\
static void								\
read_assignment_##blah##(						\
	GtkWidget *widget,						\
	gpointer   data )						\
{									\
	struct arg *_arg = malloc( sizeof( struct arg ) );		\
									\
	_arg->widget = widget;						\
	_arg->button = ##blah##;					\
									\
	if( _arg->button < 0 || _arg->button >= NUM_BUTTONS )		\
	{								\
		printf( "WARNING: button = %d\n", _arg->button );	\
		free( _arg );						\
		return;							\
	}								\
									\
	SDL_CreateThread( read_assignment_thread, (void *)_arg );	\
}

READ_ASSIGNMENT(R_DPAD)
READ_ASSIGNMENT(L_DPAD)
READ_ASSIGNMENT(D_DPAD)
READ_ASSIGNMENT(U_DPAD)
READ_ASSIGNMENT(START_BUTTON)
READ_ASSIGNMENT(Z_TRIG)
READ_ASSIGNMENT(B_BUTTON)
READ_ASSIGNMENT(A_BUTTON)
READ_ASSIGNMENT(R_CBUTTON)
READ_ASSIGNMENT(L_CBUTTON)
READ_ASSIGNMENT(D_CBUTTON)
READ_ASSIGNMENT(U_CBUTTON)
READ_ASSIGNMENT(R_TRIG)
READ_ASSIGNMENT(L_TRIG)
READ_ASSIGNMENT(Y_AXIS)
READ_ASSIGNMENT(X_AXIS)

void
configure_gtk( SController *controller )
{
	int i;

	g_Controller = controller;
	for( i = 0; i < 4; i++ )
	{
		config[i].device = g_Controller[i].device;
		memcpy( config[i].axis, g_Controller[i].axis, sizeof( SAxisMap ) * 2 );
		memcpy( config[i].button, g_Controller[i].button, sizeof( SButtonMap ) * 14 );
		memcpy( &config[i].control, &g_Controller[i].control, sizeof( CONTROL ) );
	}


	// create config dialog
	if( !window )
	{
		GtkWidget *padimage;			// pad image
		GtkWidget *toplevel_vbox;
		GtkWidget *padfixed;			// pad fixed (for the image and the assign pairs)

		GtkWidget *controller;			// controller hbox
		GtkWidget *controller_label;		// controller label
		GtkWidget *controller_combo;		// controller combo box (1-4)
		GtkWidget *controller_plugged;		// controller plugged checkbox
		GtkWidget *controller_mempak;		// mempak checkbox
		GList *controller_list = NULL;

		GtkWidget *device;			// device hbox
		GtkWidget *device_label;		// device label
		GtkWidget *device_combo;		// device combo box
		GList *device_list = NULL;

		GtkWidget *assigns[NUM_BUTTONS];	// those are hboxes to put a label
							// together with it's entry
		GtkWidget *labels[NUM_BUTTONS];		// labels for button/axis names
		GtkWidget *entries[NUM_BUTTONS];	// text entries for button/axis names
		int i;

		GtkWidget *buttons;			// button hbox
		GtkWidget *ok_button, *cancel_button;	// buttons

		// init sdl joystick subsystem
		if( SDL_InitSubSystem( SDL_INIT_JOYSTICK ) == -1 )
		{
			fprintf( stderr, "["PLUGIN_NAME"]: Couldn't init SDL joystick subsystem: %s\n", SDL_GetError() );
			return;
		}

		// create the window
		window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
		gtk_container_set_border_width( GTK_CONTAINER(window), 10 );
		gtk_window_set_title( GTK_WINDOW(window), PLUGIN_NAME );
		gtk_signal_connect( GTK_OBJECT(window), "delete-event",
				GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete), (gpointer) window );

		// toplevel vbox
		toplevel_vbox = gtk_vbox_new( FALSE, 10 );
		gtk_container_add( GTK_CONTAINER(window), GTK_WIDGET(toplevel_vbox) );

		// create/fill controller hbox/label/combo
		controller = gtk_hbox_new( FALSE, 5 );

		controller_label = gtk_label_new( "Configure controller" );
		gtk_box_pack_start( GTK_BOX(controller), GTK_WIDGET(controller_label), TRUE, FALSE, 0 );

		controller_combo = gtk_combo_new();
		gtk_entry_set_editable( GTK_ENTRY(GTK_COMBO(controller_combo)->entry), FALSE );
		gtk_signal_connect( GTK_OBJECT(GTK_COMBO(controller_combo)->entry), "changed",
				GTK_SIGNAL_FUNC(controller_changed), (gpointer) NULL);
		controller_list = g_list_append( controller_list, "#1" );
		controller_list = g_list_append( controller_list, "#2" );
		controller_list = g_list_append( controller_list, "#3" );
		controller_list = g_list_append( controller_list, "#4" );
		gtk_combo_set_popdown_strings( GTK_COMBO(controller_combo), controller_list );
		gtk_box_pack_start( GTK_BOX(controller), GTK_WIDGET(controller_combo), TRUE, FALSE, 0 );

		gtk_box_pack_start( GTK_BOX(toplevel_vbox), GTK_WIDGET(controller), TRUE, FALSE, 0 );

		// plugged/mempack checkboxes
		controller_plugged = gtk_check_button_new_with_label( "Plugged" );
		controller_mempak = gtk_check_button_new_with_label( "Mem Pak" );
		gtk_box_pack_start( GTK_BOX(controller), GTK_WIDGET(controller_plugged), TRUE, FALSE, 0 );
		gtk_box_pack_start( GTK_BOX(controller), GTK_WIDGET(controller_mempak), TRUE, FALSE, 0 );
		gtk_signal_connect( GTK_OBJECT(controller_plugged), "toggled",
					GTK_SIGNAL_FUNC (plugged_toggled), NULL);
		gtk_signal_connect( GTK_OBJECT(controller_mempak), "toggled",
					GTK_SIGNAL_FUNC (mempak_toggled), NULL);

		// create/fill device hbox/label/combo
		device = gtk_hbox_new( FALSE, 5 );

		device_label = gtk_label_new( "Joystick device:" );
		gtk_box_pack_start( GTK_BOX(device), GTK_WIDGET(device_label), TRUE, FALSE, 0 );

		device_combo = gtk_combo_new();
		gtk_entry_set_editable( GTK_ENTRY(GTK_COMBO(device_combo)->entry), FALSE );
		gtk_widget_set_usize( GTK_WIDGET(GTK_COMBO(device_combo)->entry), 300, 25 );
		gtk_signal_connect( GTK_OBJECT(GTK_COMBO(device_combo)->entry), "changed",
				GTK_SIGNAL_FUNC(device_changed), (gpointer) NULL);
		device_list = g_list_append( device_list, "None" );
		device_list = g_list_append( device_list, "Keyboard" );
		for( i = 0; i < SDL_NumJoysticks(); i++ )
			device_list = g_list_append( device_list, SDL_JoystickName( i ) );
		gtk_combo_set_popdown_strings( GTK_COMBO(device_combo), device_list );
		gtk_box_pack_start( GTK_BOX(device), GTK_WIDGET(device_combo), TRUE, FALSE, 0 );

		gtk_box_pack_start( GTK_BOX(toplevel_vbox), GTK_WIDGET(device), TRUE, FALSE, 0 );

		// pad fixed
		padfixed = gtk_fixed_new();
		gtk_box_pack_start( GTK_BOX(toplevel_vbox), GTK_WIDGET(padfixed), TRUE, FALSE, 0 );

		// create the pad image
		padimage = create_pixmap_d( window, pad_xpm );

		// create button/axis labels
		for( i = 0; i < NUM_BUTTONS; i++ )
		{
			assigns[i] = gtk_hbox_new( FALSE, 5 );
			labels[i] = gtk_label_new( button_names[i] );
			entries[i] = gtk_entry_new();
			gtk_entry_set_editable( GTK_ENTRY(entries[i]), FALSE );
			if( i == X_AXIS || i == Y_AXIS )
				gtk_widget_set_usize( GTK_WIDGET(entries[i]), 300, 25 );
			else
				gtk_widget_set_usize( GTK_WIDGET(entries[i]), 220, 25 );
			gtk_box_pack_start( GTK_BOX(assigns[i]), GTK_WIDGET(labels[i]), TRUE, FALSE, 0 );
			gtk_box_pack_start( GTK_BOX(assigns[i]), GTK_WIDGET(entries[i]), TRUE, FALSE, 0 );
		}
		gtk_signal_connect( GTK_OBJECT(entries[R_DPAD]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_R_DPAD), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[L_DPAD]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_L_DPAD), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[D_DPAD]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_D_DPAD), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[U_DPAD]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_U_DPAD), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[START_BUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_START_BUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[Z_TRIG]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_Z_TRIG), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[B_BUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_B_BUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[A_BUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_A_BUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[R_CBUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_R_CBUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[L_CBUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_L_CBUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[D_CBUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_D_CBUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[U_CBUTTON]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_U_CBUTTON), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[R_TRIG]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_R_TRIG), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[L_TRIG]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_L_TRIG), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[Y_AXIS]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_Y_AXIS), (gpointer)NULL );
		gtk_signal_connect( GTK_OBJECT(entries[X_AXIS]), "button_press_event",
			GTK_SIGNAL_FUNC(read_assignment_X_AXIS), (gpointer)NULL );

		// place assignment pairs and pad image
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[L_TRIG]), 40,  35 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[U_DPAD]),  0, 100 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[L_DPAD]),  0, 130 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[D_DPAD]),  0, 160 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[R_DPAD]),  0, 190 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[Z_TRIG]), 10, 225 );

		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[START_BUTTON]), 270,   0 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(padimage),              265,  25 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[X_AXIS]),       270, 335 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[Y_AXIS]),       270, 360 );

		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[R_TRIG]),    470,  35 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[L_CBUTTON]), 575,  65 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[U_CBUTTON]), 575,  90 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[R_CBUTTON]), 575, 115 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[D_CBUTTON]), 575, 140 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[B_BUTTON]),  575, 170 );
		gtk_fixed_put( GTK_FIXED(padfixed), GTK_WIDGET(assigns[A_BUTTON]),  575, 270 );

		// ok/cancel button
		buttons = gtk_hbox_new( FALSE, 50 );

		ok_button = gtk_button_new_with_label( "Ok" );
		gtk_box_pack_start( GTK_BOX(buttons), GTK_WIDGET(ok_button), TRUE, FALSE, 0 );

		cancel_button = gtk_button_new_with_label( "Cancel" );
		gtk_box_pack_start( GTK_BOX(buttons), GTK_WIDGET(cancel_button), TRUE, FALSE, 0 );

		gtk_box_pack_start( GTK_BOX(toplevel_vbox), GTK_WIDGET(buttons), TRUE, FALSE, 0 );

		// connect signals
		gtk_signal_connect( GTK_OBJECT(ok_button), "clicked",
				GTK_SIGNAL_FUNC(ok_clicked), (gpointer) controller);
		gtk_signal_connect( GTK_OBJECT(cancel_button), "clicked",
				GTK_SIGNAL_FUNC(cancel_clicked), (gpointer) NULL);

		// globals
		pluggedCheckbox = controller_plugged;
		mempakCheckbox = controller_mempak;
		deviceCombo = device_combo;
		memcpy( mapEntries, entries, sizeof( GtkWidget * ) * NUM_BUTTONS );
	}

	// read configuration
	read_configuration();

	// fill widgets with configuration
	update_window();

	// show dialog
	window_shown = 1;
	gtk_widget_show_all( window );
}

#endif // GUI_GTK
