// Project description
// -------------------
// Name: nJoy
// Description: A Dolphin Compatible Input Plugin
//
// Author: Falcon4ever (nJoy@falcon4ever.com)
// Site: www.multigesture.net
// Copyright (C) 2003 Dolphin Project.
//
// Licensetype: GNU General Public License (GPL)
//
// 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, version 2.0.
//
// 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 2.0 for more details.
//
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
//
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
//

#include "math.h" // System

#include "ConfigBox.h" // Local
#include "../nJoy.h"
#include "Images/controller.xpm"

extern bool g_EmulatorRunning;

// D-Pad type
static const char* DPadType[] =
{
	"Hat",
	"Custom",
};

// Trigger type
static const char* TriggerType[] =
{
	"SDL", // -0x8000 to 0x7fff
	"XInput", // 0x00 to 0xff
};


// The wxWidgets class
BEGIN_EVENT_TABLE(PADConfigDialognJoy,wxDialog)
	EVT_CLOSE(PADConfigDialognJoy::OnClose)
	EVT_BUTTON(ID_ABOUT, PADConfigDialognJoy::AboutClick)
	EVT_BUTTON(ID_OK, PADConfigDialognJoy::OKClick)
	EVT_BUTTON(ID_CANCEL, PADConfigDialognJoy::CancelClick)
	EVT_NOTEBOOK_PAGE_CHANGED(ID_NOTEBOOK, PADConfigDialognJoy::NotebookPageChanged)

	// Change gamepad
	EVT_COMBOBOX(IDC_JOYNAME, PADConfigDialognJoy::ChangeSettings)

	 // Other settings
	EVT_CHECKBOX(IDC_SAVEBYID, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDC_SHOWADVANCED, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_CHECKFOCUS, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDCB_MAINSTICK_DIAGONAL, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDC_CONTROLTYPE, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDC_TRIGGERTYPE, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDC_DEADZONE, PADConfigDialognJoy::ChangeSettings)	

	// Rumble settings
	EVT_CHECKBOX(IDC_ENABLERUMBLE, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDC_RUMBLESTRENGTH, PADConfigDialognJoy::ChangeSettings)

	// Advanced settings
	EVT_COMBOBOX(IDCB_MAINSTICK_RADIUS, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_MAINSTICK_CB_RADIUS, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDCB_MAINSTICK_DIAGONAL, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_MAINSTICK_S_TO_C, PADConfigDialognJoy::ChangeSettings)
	// C-stick
	EVT_COMBOBOX(IDCB_CSTICK_RADIUS, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_CSTICK_CB_RADIUS, PADConfigDialognJoy::ChangeSettings)
	EVT_COMBOBOX(IDCB_CSTICK_DIAGONAL, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_CSTICK_S_TO_C, PADConfigDialognJoy::ChangeSettings)
	EVT_CHECKBOX(IDCB_FILTER_SETTINGS, PADConfigDialognJoy::ChangeSettings)

	EVT_BUTTON(IDB_SHOULDER_L, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_SHOULDER_R, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTON_A, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTON_B, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTON_X, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTON_Y, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTON_Z, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTONSTART, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_BUTTONHALFPRESS, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_DPAD_UP, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_DPAD_DOWN, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_DPAD_LEFT, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_DPAD_RIGHT, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_ANALOG_MAIN_X, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_ANALOG_MAIN_Y, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_ANALOG_SUB_X, PADConfigDialognJoy::GetButtons)
	EVT_BUTTON(IDB_ANALOG_SUB_Y, PADConfigDialognJoy::GetButtons)

	#if wxUSE_TIMER
		EVT_TIMER(IDTM_SLOW, PADConfigDialognJoy::OnSlowTimer)
		EVT_TIMER(IDTM_CONSTANT, PADConfigDialognJoy::OnTimer)
		EVT_TIMER(IDTM_BUTTON, PADConfigDialognJoy::OnButtonTimer)
	#endif
END_EVENT_TABLE()

PADConfigDialognJoy::PADConfigDialognJoy(wxWindow *parent, wxWindowID id, const wxString &title,
					 const wxPoint &position, const wxSize& size, long style)
	: wxDialog(parent, id, title, position, size, style)
{
	// Initialize values
	notebookpage = 0;
	g_Pressed = 0;	
	m_TCDebugging = NULL;
	ControlsCreated = false;
	
	// Settings
	Debugging = false;
	// Only tested in Windows
	#ifdef _WIN32
	LiveUpdates = true;
	#else
	LiveUpdates = false;
	#endif

	// Create controls
	CreateGUIControls();
	
	// Update GUI
	UpdateGUIAll();
	
	// Update device list
	UpdateDeviceList();

	#if wxUSE_TIMER
		m_SlowTimer = new wxTimer(this, IDTM_SLOW);
		m_ConstantTimer = new wxTimer(this, IDTM_CONSTANT);
		m_ButtonMappingTimer = new wxTimer(this, IDTM_BUTTON);

		// Reset values
		GetButtonWaitingID = 0; GetButtonWaitingTimer = 0;

		// Start timers
		StartTimer();
	#endif

	// wxEVT_KEY_DOWN is blocked for enter, tab and the directional keys
	wxTheApp->Connect(wxID_ANY, wxEVT_KEY_UP,
			wxKeyEventHandler(PADConfigDialognJoy::OnKeyDown),
			(wxObject*)0, this);
}

PADConfigDialognJoy::~PADConfigDialognJoy()
{
// The statbar sample has this so I add this to
#if wxUSE_TIMER
    if (m_ConstantTimer->IsRunning()) m_ConstantTimer->Stop();
#endif
}

void PADConfigDialognJoy::DoShow()
{
	// Start timers
	StartTimer();
	// Show
	ShowModal();
}

void PADConfigDialognJoy::StartTimer()
{	
	// Start the slow timer
	double TimesPerSecond = 2;
	m_SlowTimer->Start( floor((double)(1000.0 / TimesPerSecond)) );
	// Start the constant timer
	TimesPerSecond = 30;
	m_ConstantTimer->Start( floor((double)(1000 / TimesPerSecond)) );
}

void PADConfigDialognJoy::OnKeyDown(wxKeyEvent& event)
{
	g_Pressed = event.GetKeyCode();
}

// Close window
void PADConfigDialognJoy::OnClose(wxCloseEvent& event)
{
	// Allow wxWidgets to close the window
	event.Skip();

	// Stop the timer
	m_SlowTimer->Stop();
	m_ConstantTimer->Stop();
	m_ButtonMappingTimer->Stop();

	// Close pads, unless we are running a game
	if (!g_EmulatorRunning) Shutdown();
}

// Call about dialog
void PADConfigDialognJoy::AboutClick(wxCommandEvent& event)
{
	#ifdef _WIN32
		wxWindow win;
		win.SetHWND((WXHWND)this->GetHWND());
		win.Enable(false);  

		AboutBox frame(&win);
		frame.ShowModal();

		win.Enable(true);
		win.SetHWND(0);
	#else
		AboutBox frame(NULL);
		frame.ShowModal();
	#endif
}

// Click OK
void PADConfigDialognJoy::OKClick(wxCommandEvent& event)
{
	if (event.GetId() == ID_OK)
	{
		DoSave();	// Save settings
		if (Debugging) PanicAlert("Done");
		Close(); // Call OnClose()
	}
}

// Click Cancel
void PADConfigDialognJoy::CancelClick(wxCommandEvent& event)
{
	if (event.GetId() == ID_CANCEL)
	{
		// Forget all potential changes to PadMapping by loading the last saved settings
		g_Config.Load();
		Close(); // Call OnClose()
	}
}

// Debugging
void PADConfigDialognJoy::LogMsg(const char* format, ...)
{
	#ifdef _WIN32
	if (Debugging)
	{
		const int MaxMsgSize = 1024*2;
		char buffer[MaxMsgSize];
		va_list args;
		va_start(args, format);
		CharArrayFromFormatV(buffer, MaxMsgSize, format, args);
		va_end(args);

		// Add timestamp
		std::string StrTmp = buffer;
		//StrTmp += Common::Timer::GetTimeFormatted();

		if(m_TCDebugging) m_TCDebugging->AppendText(wxString::FromAscii(StrTmp.c_str()));
	}	
	#endif
}



// Save Settings
/*

   Saving is currently done when:

   1. Closing the configuration window
   2. Changing the gamepad
   3. When the gamepad is enabled or disbled
   4. When we change saving mode (by Id or by slot)

   Input: ChangePad needs to be used when we change the pad for a slot. Slot needs to be used when
   we only want to save changes to one slot.
*/
void PADConfigDialognJoy::DoSave(bool ChangePad, int Slot)
{
	// Replace "" with "-1" before we are saving
	ToBlank(false);

	if (ChangePad)
	{
		// Since we are selecting the pad to save to by the Id we can't update it when we change the pad
		for(int i = 0; i < 4; i++) SaveButtonMapping(i, true);
		
		g_Config.Save(Slot);
		// Now we can update the ID
		UpdateID();
	}
	else
	{
		// Update PadMapping[] from the GUI controls
		for(int i = 0; i < 4; i++) SaveButtonMapping(i);
		g_Config.Save(Slot);
	}		

	// Then change it back to ""
	ToBlank();
}
void PADConfigDialognJoy::UpdateID()
{
	INFO_LOG(PAD, "Slot %i: Changed PadMapping ID from %i to %i", notebookpage, PadMapping[notebookpage].ID, m_Joyname[notebookpage]->GetSelection());
	PadMapping[notebookpage].ID = joyinfo.at(m_Joyname[notebookpage]->GetSelection()).ID;
	PadMapping[notebookpage].Name = joyinfo.at(m_Joyname[notebookpage]->GetSelection()).Name;
	// Update all handles
	for(int i = 0; i < 4; i++)
	{
		if (PadMapping[i].Name == PadMapping[notebookpage].Name)
		{
			if (m_Joyname[i]->GetSelection() >= 0 && joyinfo.size() > m_Joyname[i]->GetSelection())
			PadState[i].joy = joyinfo.at(m_Joyname[i]->GetSelection()).joy;
		}
	}
}

// On changing the SaveById option we update all pages
void PADConfigDialognJoy::OnSaveById()
{
	// Save current settings
	DoSave(false, notebookpage);

	// Update the SaveByID setting and load the settings
	g_Config.bSaveByID = m_CBSaveByID[notebookpage]->IsChecked();
	g_Config.Load(false, true);

	// Update the GUI from the now updated PadMapping[]
	UpdateGUIAll(-1);
}

// Change Joystick
/* Function: When changing the joystick we save and load the settings and update the PadMapping
   and PadState array. PadState[].joy is the gamepad handle that is used to access the pad throughout
   the plugin. Joyinfo[].joy is only used the first time the pads are checked. */
void PADConfigDialognJoy::DoChangeJoystick()
{
	// Before changing the pad we save potential changes to the current pad (to support SaveByID)
	WARN_LOG(PAD, "\n--- DoChangeJoystick ----");
	WARN_LOG(PAD, "DoSave");
	DoSave(true);
	
	// Load the settings for the new ID
	WARN_LOG(PAD, "Load");
	g_Config.Load(true);
	// Update the GUI
	WARN_LOG(PAD, "UpdateGUI");
	UpdateGUI(notebookpage);
	WARN_LOG(PAD, "");
}
// Pads have been connected/disconnected
void PADConfigDialognJoy::UpdateDeviceList()
{
	if (!ControlsCreated) return;

	DEBUG_LOG(PAD, "UpdateDeviceList");

	for (int i = 0; i < 4; i++)
	{	
		// Save current selection
		//std::string CurrentSel = m_Joyname[i]->GetValue().mb_str();
		m_Joyname[i]->Clear();

		// Search for devices and add them to the device list	
		if (NumPads > 0)
		{			
			for (int j = 0; j < NumPads; j++)	
				m_Joyname[i]->Append(wxString::FromAscii(joyinfo.at(j).Name.c_str()));
			// Set selection
							//PanicAlert("%s", PadMapping[i].Name.c_str());
			for (int j = 0; j < NumPads; j++)
			{	
				if (joyinfo.at(j).Name == PadMapping[i].Name) m_Joyname[i]->SetSelection(j);
			}
			if (m_Joyname[i]->GetSelection() == -1) m_Joyname[i]->SetSelection(0);
			// Load settings
			DoChangeJoystick();
		}
		else
		{
			m_Joyname[i]->Append(wxString::FromAscii("<No Gamepad Detected>"));	
			m_Joyname[i]->SetSelection(0);
		}
	}	
}

// Notebook page changed
void PADConfigDialognJoy::NotebookPageChanged(wxNotebookEvent& event)
{	
	// Save current settings now, don't wait for OK
	if (ControlsCreated && !g_Config.bSaveByID) DoSave(false, notebookpage);

	// Update the global variable	
	notebookpage = event.GetSelection();

	// Update GUI
	if (ControlsCreated) UpdateGUI(notebookpage);
}

// Replace the harder to understand -1 with "" for the sake of user friendliness
void PADConfigDialognJoy::ToBlank(bool ToBlank)
{
	if (!ControlsCreated) return;

	for (int j = 0; j < 4; j++)
	{
		if(ToBlank)
		{
			for(int i = IDB_ANALOG_MAIN_X; i <= IDB_BUTTONHALFPRESS; i++)
				#ifndef _WIN32
					if(!strcmp(GetButtonText(i, j).mb_str(), "-1")) SetButtonText(i, "", j);
				#else
					if(GetButtonText(i, j) == wxT("-1")) SetButtonText(i, "", j);
				#endif
		}
		else
		{
			for(int i = IDB_ANALOG_MAIN_X; i <= IDB_BUTTONHALFPRESS; i++)
				if(GetButtonText(i, j).IsEmpty()) SetButtonText(i, "-1", j);
		}
	}	
}


// Change settings for all slots that have this pad selected
// -----------------------
void PADConfigDialognJoy::SetButtonTextAll(int id, const char *text)
{
	for (int i = 0; i < 4; i++)
	{
		if (IDToName(PadMapping[i].ID) == IDToName(PadMapping[notebookpage].ID))
		{
			SetButtonText(id, text, i);
			DEBUG_LOG(PAD, "Updated button text for slot %i", i);
		}
	};
}
void PADConfigDialognJoy::SaveButtonMappingAll(int Slot)
{
	for (int i = 0; i < 4; i++)
	{
		if (IDToName(PadMapping[i].ID) == IDToName(PadMapping[Slot].ID))
			SaveButtonMapping(i, false, Slot);
	}
}
// -----------------------

void PADConfigDialognJoy::UpdateGUIAll(int Slot)
{
	if (Slot == -1)
	{
		for (int i = 0; i < 4; i++) UpdateGUI(i);
	}
	else
	{
		for (int i = 0; i < 4; i++)
		{
			if (IDToName(PadMapping[i].ID) == IDToName(PadMapping[Slot].ID))
				UpdateGUI(i);
		}
	}
}

void PADConfigDialognJoy::ChangeSettings( wxCommandEvent& event )
{
	switch(event.GetId())
	{
		case IDC_SAVEBYID:
			OnSaveById();
			break;		

		case IDC_SHOWADVANCED:
			g_Config.bShowAdvanced = m_CBShowAdvanced[notebookpage]->IsChecked();			
			for(int i = 0; i < 4; i++)
			{
				UpdateGUI(i);
				m_CBShowAdvanced[i]->SetValue(g_Config.bShowAdvanced);
				m_sMainRight[i]->Show(g_Config.bShowAdvanced);
			}
			// Resize the window without the need of any weird hack 
			SetSizerAndFit(m_MainSizer);
			break;

		// Advanced settings
		case IDCB_CHECKFOCUS:
			g_Config.bCheckFocus = m_CBCheckFocus[notebookpage]->IsChecked();
			for(int i = 0; i < 4; i++)
			{
				m_CBCheckFocus[i]->SetValue(g_Config.bCheckFocus);
			}
			break;
		case IDCB_FILTER_SETTINGS:
			g_Config.bNoTriggerFilter = m_AdvancedMapFilter[notebookpage]->IsChecked();
			for(int i = 0; i < 4; i++)
			{
				m_AdvancedMapFilter[i]->SetValue(g_Config.bNoTriggerFilter);
			}
			break;

		case IDC_CONTROLTYPE:
			if(!g_Config.bSaveByID)
			{
				PadMapping[notebookpage].controllertype = m_ControlType[notebookpage]->GetSelection();
				UpdateGUI(notebookpage);
			}
		case IDC_TRIGGERTYPE:
			if(!g_Config.bSaveByID)
			{
				PadMapping[notebookpage].triggertype = m_TriggerType[notebookpage]->GetSelection();
				UpdateGUI(notebookpage);
			}
			break;
		case IDC_ENABLERUMBLE:
			PadMapping[notebookpage].rumble = m_Rumble[notebookpage]->IsChecked();
			UpdateGUI(notebookpage);
			break;
		case IDC_RUMBLESTRENGTH:
			g_Config.RumbleStrength = m_RStrength[notebookpage]->GetSelection();
			break;
		case IDC_JOYNAME: 
			DoChangeJoystick();
			break;
	}

	// Update all slots that use this device
	//if(g_Config.bSaveByID)
		SaveButtonMappingAll(notebookpage);
	//if(g_Config.bSaveByID)
		UpdateGUIAll(notebookpage);
}


// Update GUI
// Called from: CreateGUIControls(), ChangeControllertype()
void PADConfigDialognJoy::UpdateGUI(int _notebookpage)
{
	DEBUG_LOG(PAD, "UpdateGUI for slot %i, %i pads connected", _notebookpage, NumPads);

	// If there are no good pads disable the entire notebook
	if (NumPads == 0)
	{
		m_Notebook->Enable(false);
		return;
	}
	else
	{
		m_Notebook->Enable(true);
	}

	// Update the GUI from PadMapping[]
	UpdateGUIButtonMapping(_notebookpage);

	// Collect status
	bool Hat = (PadMapping[_notebookpage].controllertype == InputCommon::CTL_DPAD_HAT);
	long Left, Right;
	m_JoyShoulderL[_notebookpage]->GetValue().ToLong(&Left);
	m_JoyShoulderR[_notebookpage]->GetValue().ToLong(&Right);
	bool AnalogTrigger = (Left >= 1000 || Right >= 1000);
	#ifdef _WIN32
		bool XInput = XInput::IsConnected(0);	
	#endif

	// Hat type selection
	m_JoyDpadUp[_notebookpage]->Show(!Hat);
	m_JoyDpadLeft[_notebookpage]->Show(!Hat);
	m_JoyDpadRight[_notebookpage]->Show(!Hat);

	m_bJoyDpadUp[_notebookpage]->Show(!Hat);
	m_bJoyDpadLeft[_notebookpage]->Show(!Hat);
	m_bJoyDpadRight[_notebookpage]->Show(!Hat);
	
	m_textDpadUp[_notebookpage]->Show(!Hat);
	m_textDpadLeft[_notebookpage]->Show(!Hat);
	m_textDpadRight[_notebookpage]->Show(!Hat);	

	m_textDpadDown[_notebookpage]->SetLabel(Hat ? wxT("Select hat") : wxT("Down"));
	m_bJoyDpadDown[_notebookpage]->SetToolTip(Hat ?
		wxT("Select a hat by pressing the hat in any direction") : wxT(""));

	// General settings
	m_CBSaveByID[_notebookpage]->SetValue(g_Config.bSaveByID);
	m_CBShowAdvanced[_notebookpage]->SetValue(g_Config.bShowAdvanced);
	m_CBCheckFocus[_notebookpage]->SetValue(g_Config.bCheckFocus);
	m_AdvancedMapFilter[_notebookpage]->SetValue(g_Config.bNoTriggerFilter);
	m_RStrength[_notebookpage]->SetSelection(g_Config.RumbleStrength);

	// Replace the harder to understand -1 with "" for the sake of user friendliness
	ToBlank();

	// Advanced settings
	if (g_Config.bShowAdvanced)
	{
		if (PadMapping[_notebookpage].bRadiusOnOff) m_CoBRadius[_notebookpage]->Enable(true);
			else m_CoBRadius[_notebookpage]->Enable(false);
		if (PadMapping[_notebookpage].bSquareToCircle) m_CoBDiagonal[_notebookpage]->Enable(true);
			else m_CoBDiagonal[_notebookpage]->Enable(false);
		if (PadMapping[_notebookpage].bRadiusOnOffC) m_CoBRadiusC[_notebookpage]->Enable(true);
			else m_CoBRadiusC[_notebookpage]->Enable(false);
		if (PadMapping[_notebookpage].bSquareToCircleC) m_CoBDiagonalC[_notebookpage]->Enable(true);
			else m_CoBDiagonalC[_notebookpage]->Enable(false);

		DEBUG_LOG(PAD, "Main radius %s", PadMapping[_notebookpage].bRadiusOnOff ? "enabled" : "disabled");
	}

	 // Repaint the background
	m_Controller[_notebookpage]->Refresh();
}


// Paint the background
void PADConfigDialognJoy::OnPaint(wxPaintEvent &event)
{
	event.Skip();

	wxPaintDC dcWin(m_pKeys[notebookpage]);
	PrepareDC( dcWin );
	dcWin.DrawBitmap( WxStaticBitmap1_BITMAP, 94, 0, true );
}

// Populate the config window
void PADConfigDialognJoy::CreateGUIControls()
{
	INFO_LOG(PAD, "CreateGUIControls()\n");

	#ifndef _DEBUG		
		SetTitle(wxT("Configure: nJoy Input Plugin"));
	#else			
		SetTitle(wxT("Configure: nJoy (Debug) Input Plugin"));
	#endif

	SetIcon(wxNullIcon);

#ifndef _WIN32
	// Force a 8pt font so that it looks more or less "correct" regardless of the default font setting
	wxFont f(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
	SetFont(f);
#endif

	// Buttons
	m_About = new wxButton(this, ID_ABOUT, wxT("About"), wxDefaultPosition, wxSize(75, 25), 0, wxDefaultValidator, wxT("About"));
	m_OK = new wxButton(this, ID_OK, wxT("OK"), wxDefaultPosition, wxSize(75, 25), 0, wxDefaultValidator, wxT("OK"));
	m_Cancel = new wxButton(this, ID_CANCEL, wxT("Cancel"), wxDefaultPosition, wxSize(75, 25), 0, wxDefaultValidator, wxT("Cancel"));
	m_OK->SetToolTip(
		wxT("Save your settings and close this window.")
		);
	m_Cancel->SetToolTip(
		wxT("Close this window without saving your changes.")
		);

	// Notebook
	m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize);

	// Controller pages
	m_Controller[0] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE1, wxDefaultPosition, wxDefaultSize);
	m_Notebook->AddPage(m_Controller[0], wxT("Controller 1"));
	m_Controller[1] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE2, wxDefaultPosition, wxDefaultSize);
	m_Notebook->AddPage(m_Controller[1], wxT("Controller 2"));
	m_Controller[2] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE3, wxDefaultPosition, wxDefaultSize);
	m_Notebook->AddPage(m_Controller[2], wxT("Controller 3"));
	m_Controller[3] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE4, wxDefaultPosition, wxDefaultSize);
	m_Notebook->AddPage(m_Controller[3], wxT("Controller 4"));

	// Define bitmap for EVT_PAINT
	WxStaticBitmap1_BITMAP = wxBitmap(ConfigBox_WxStaticBitmap1_XPM);

	// Populate the DPad type and Trigger type list
	wxArrayString wxAS_DPadType;
	wxAS_DPadType.Add(wxString::FromAscii(DPadType[InputCommon::CTL_DPAD_HAT]));
	wxAS_DPadType.Add(wxString::FromAscii(DPadType[InputCommon::CTL_DPAD_CUSTOM]));

	wxArrayString wxAS_TriggerType;
	wxAS_TriggerType.Add(wxString::FromAscii(TriggerType[InputCommon::CTL_TRIGGER_SDL]));
	wxAS_TriggerType.Add(wxString::FromAscii(TriggerType[InputCommon::CTL_TRIGGER_XINPUT]));
	
	// Populate the deadzone list and the Rumble Strength

	char buffer[8];

	wxArrayString wxAS_RumbleStrength;
	for (int i = 1; i < 11; i++) 
	{
		sprintf (buffer, "%d %%", i*10);
		wxAS_RumbleStrength.Add(wxString::FromAscii(buffer));
	}

	wxArrayString arrayStringFor_Deadzone;
	for(int x = 1; x <= 100; x++)
	{		
		sprintf (buffer, "%d %%", x);
		arrayStringFor_Deadzone.Add(wxString::FromAscii(buffer));
	}

	// Populate all four pages
	for(int i = 0; i < 4; i++)
	{
		// Populate keys sizer
		// Set relative values for the keys
		int t = -75; // Top
		int l = -4; // Left
		m_sKeys[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Keys"));
		m_pKeys[i] = new wxPanel(m_Controller[i], ID_KEYSPANEL1 + i, wxDefaultPosition, wxSize(600, 400), 0);
		//m_sKeys[i] = new wxStaticBox (m_Controller[i], IDG_JOYSTICK, wxT("Keys"), wxDefaultPosition, wxSize(608, 500));
		m_sKeys[i]->Add(m_pKeys[i], 0, (wxALL), 0); // margin = 0

		// GameCube controller picture
		// TODO: Controller image
		// Placeholder instead of bitmap
		// m_PlaceholderBMP[i] = new wxTextCtrl(m_Controller[i], ID_CONTROLLERPICTURE, wxT("BITMAP HERE PLZ KTHX!"), wxPoint(98, 75), wxSize(423, 306), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("BITMAP HERE PLZ KTHX!"));
		// m_PlaceholderBMP[i]->Enable(false);
		
		/* You can enable the bitmap here. But it loads �berslow on init... (only in windows, linux
		seems to load it fast!) AAaaand the XPM file (256 colours) looks crappier than the real bitmap...
		so maybe we can find a way to use a bitmap?	*/
		//m_controllerimage[i] = new wxStaticBitmap(m_pKeys[i], ID_CONTROLLERPICTURE, WxStaticBitmap1_BITMAP, wxPoint(l + 98, t + 75), wxSize(421,304));		
		//m_controllerimage[i] = new wxBitmap( WxStaticBitmap1_BITMAP );		

		 // Paint background. This allows objects to be visible on top of the picture
		m_pKeys[i]->Connect(wxID_ANY, wxEVT_PAINT,
			wxPaintEventHandler(PADConfigDialognJoy::OnPaint),
			(wxObject*)0, this);


		// Keys objects
		// Left and right shoulder buttons
		m_JoyShoulderL[i] = new wxTextCtrl(m_pKeys[i], ID_SHOULDER_L, wxT("0"), wxPoint(l + 6, t + 80), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyShoulderL[i]->Enable(false);
		m_bJoyShoulderL[i] = new wxButton(m_pKeys[i], IDB_SHOULDER_L, wxEmptyString, wxPoint(l + 70, t + 82), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_JoyShoulderR[i] = new wxTextCtrl(m_pKeys[i], ID_SHOULDER_R, wxT("0"), wxPoint(l + 552, t + 106), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyShoulderR[i]->Enable(false);
		m_bJoyShoulderR[i] = new wxButton(m_pKeys[i], IDB_SHOULDER_R, wxEmptyString, wxPoint(l + 526, t + 108), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		// Left analog
		int ALt = 169; int ALw = ALt + 14; int ALb = ALw + 2; // Set offset
		m_JoyAnalogMainX[i] = new wxTextCtrl(m_pKeys[i], ID_ANALOG_MAIN_X, wxT("0"), wxPoint(l + 6, t + ALw), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyAnalogMainX[i]->Enable(false);
		m_JoyAnalogMainY[i] = new wxTextCtrl(m_pKeys[i], ID_ANALOG_MAIN_Y, wxT("0"), wxPoint(l + 6, t + ALw + 36), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyAnalogMainY[i]->Enable(false);		
		m_bJoyAnalogMainX[i] = new wxButton(m_pKeys[i], IDB_ANALOG_MAIN_X, wxEmptyString, wxPoint(l + 70, t + ALb), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyAnalogMainY[i] = new wxButton(m_pKeys[i], IDB_ANALOG_MAIN_Y, wxEmptyString, wxPoint(l + 70, t + ALb + 36), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_textMainX[i] = new wxStaticText(m_pKeys[i], IDT_ANALOG_MAIN_X, wxT("X-axis"), wxPoint(l + 6, t + ALt), wxDefaultSize, 0, wxT("X-axis"));
		m_textMainY[i] = new wxStaticText(m_pKeys[i], IDT_ANALOG_MAIN_Y, wxT("Y-axis"), wxPoint(l + 6, t + ALt + 36), wxDefaultSize, 0, wxT("Y-axis"));

		// D-Pad
		int DPt = 250; int DPw = DPt + 14; int DPb = DPw + 2; // Set offset
		m_JoyDpadUp[i] = new wxTextCtrl(m_pKeys[i], ID_DPAD_UP, wxT("0"), wxPoint(l + 6, t + DPw), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyDpadDown[i] = new wxTextCtrl(m_pKeys[i], ID_DPAD_DOWN, wxT("0"), wxPoint(l + 6, t + DPw + 36*1), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyDpadLeft[i] = new wxTextCtrl(m_pKeys[i], ID_DPAD_LEFT, wxT("0"), wxPoint(l + 6, t + DPw + 36*2), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyDpadRight[i] = new wxTextCtrl(m_pKeys[i], ID_DPAD_RIGHT, wxT("0"), wxPoint(l + 6, t + DPw + 36*3), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyDpadUp[i]->Enable(false);
		m_JoyDpadDown[i]->Enable(false);
		m_JoyDpadLeft[i]->Enable(false);
		m_JoyDpadRight[i]->Enable(false);
		m_bJoyDpadUp[i] = new wxButton(m_pKeys[i], IDB_DPAD_UP, wxEmptyString, wxPoint(l + 70, t + DPb + 36*0), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyDpadDown[i] = new wxButton(m_pKeys[i], IDB_DPAD_DOWN, wxEmptyString, wxPoint(l + 70, t + DPb + 36*1), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyDpadLeft[i] = new wxButton(m_pKeys[i], IDB_DPAD_LEFT, wxEmptyString, wxPoint(l + 70, t + DPb + 36*2), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyDpadRight[i] = new wxButton(m_pKeys[i], IDB_DPAD_RIGHT, wxEmptyString, wxPoint(l + 70, t + DPb + 36*3), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_textDpadUp[i] = new wxStaticText(m_pKeys[i], IDT_DPAD_UP, wxT("Up"), wxPoint(l + 6, t + DPt + 36*0), wxDefaultSize, 0, wxT("Up"));
		m_textDpadDown[i] = new wxStaticText(m_pKeys[i], IDT_DPAD_DOWN, wxT("Down"), wxPoint(l + 6, t + DPt + 36*1), wxDefaultSize, 0, wxT("Down"));
		m_textDpadLeft[i] = new wxStaticText(m_pKeys[i], IDT_DPAD_LEFT, wxT("Left"), wxPoint(l + 6, t + DPt + 36*2), wxDefaultSize, 0, wxT("Left"));
		m_textDpadRight[i] = new wxStaticText(m_pKeys[i], IDT_DPAD_RIGHT, wxT("Right"), wxPoint(l + 6, t + DPt + 36*3), wxDefaultSize, 0, wxT("Right"));

		// Buttons
		m_JoyButtonA[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTON_A, wxT("0"), wxPoint(l + 552, t + 280), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonA[i]->Enable(false);
		m_JoyButtonB[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTON_B, wxT("0"), wxPoint(l + 552, t + 80), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonB[i]->Enable(false);
		m_JoyButtonX[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTON_X, wxT("0"), wxPoint(l + 552, t + 242), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonX[i]->Enable(false);
		m_JoyButtonY[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTON_Y, wxT("0"), wxPoint(l + 552, t + 171), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonY[i]->Enable(false);
		m_JoyButtonZ[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTON_Z, wxT("0"), wxPoint(l + 552, t + 145), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonZ[i]->Enable(false);
		m_bJoyButtonA[i] = new wxButton(m_pKeys[i], IDB_BUTTON_A, wxEmptyString, wxPoint(l + 526, t + 282), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyButtonB[i] = new wxButton(m_pKeys[i], IDB_BUTTON_B, wxEmptyString, wxPoint(l + 526, t + 82), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyButtonX[i] = new wxButton(m_pKeys[i], IDB_BUTTON_X, wxEmptyString, wxPoint(l + 526, t + 244), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyButtonY[i] = new wxButton(m_pKeys[i], IDB_BUTTON_Y, wxEmptyString, wxPoint(l + 526, t + 173), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyButtonZ[i] = new wxButton(m_pKeys[i], IDB_BUTTON_Z, wxEmptyString, wxPoint(l + 526, t + 147), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);

		// C-buttons
		m_JoyAnalogSubX[i] = new wxTextCtrl(m_pKeys[i], ID_ANALOG_SUB_X, wxT("0"), wxPoint(l + 552, t + 336), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyAnalogSubX[i]->Enable(false);
		m_JoyAnalogSubY[i] = new wxTextCtrl(m_pKeys[i], ID_ANALOG_SUB_Y, wxT("0"), wxPoint(l + 552, t + 373), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyAnalogSubY[i]->Enable(false);
		m_bJoyAnalogSubX[i] = new wxButton(m_pKeys[i], IDB_ANALOG_SUB_X, wxEmptyString, wxPoint(l + 526, t + 338), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_bJoyAnalogSubY[i] = new wxButton(m_pKeys[i], IDB_ANALOG_SUB_Y, wxEmptyString, wxPoint(l + 526, t + 375), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_textSubX[i] = new wxStaticText(m_pKeys[i], IDT_ANALOG_SUB_X, wxT("X-axis"), wxPoint(l + 552, t + 321), wxDefaultSize, 0, wxT("X-axis"));
		m_textSubY[i] = new wxStaticText(m_pKeys[i], IDT_ANALOG_SUB_Y, wxT("Y-axis"), wxPoint(l + 552, t + 358), wxDefaultSize, 0, wxT("Y-axis"));
		
		// Start button
		m_bJoyButtonStart[i] = new wxButton(m_pKeys[i], IDB_BUTTONSTART, wxEmptyString, wxPoint(l + 284, t + 365), wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		m_JoyButtonStart[i] = new wxTextCtrl(m_pKeys[i], ID_BUTTONSTART, wxT("0"), wxPoint(l + 220, t + 363), wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonStart[i]->Enable(false);
		
		// Website text
		#ifdef _WIN32
		m_textWebsite[i] = new wxStaticText(m_pKeys[i], IDT_WEBSITE, wxT("www.multigesture.net"), wxPoint(l + 400, t + 380), wxDefaultSize, 0, wxT("www.multigesture.net"));
		#else
		m_textWebsite[i] = new wxStaticText(m_Controller[i], IDT_WEBSITE, wxT("www.multigesture.net"), wxPoint(l + 480, t + 418), wxDefaultSize, 0, wxT("www.multigesture.net"));
		#endif


		// Populate Controller sizer
		// Groups
		#ifdef _WIN32
		m_Joyname[i] = new wxComboBox(m_Controller[i], IDC_JOYNAME, wxEmptyString, wxDefaultPosition, wxSize(476, 21), 0, NULL, wxCB_READONLY);
		#else
		m_Joyname[i] = new wxComboBox(m_Controller[i], IDC_JOYNAME, arrayStringFor_Joyname[0], wxDefaultPosition, wxSize(450, 25), arrayStringFor_Joyname, 0, wxDefaultValidator, wxT("m_Joyname"));
		#endif

		m_gJoyname[i] = new wxStaticBoxSizer (wxHORIZONTAL, m_Controller[i], wxT("Controller"));
		m_gJoyname[i]->Add(m_Joyname[i], 0, (wxLEFT | wxRIGHT), 5);

		m_Joyname[i]->SetToolTip(wxT("Save your settings and configure another joypad"));



		// General settings

		// General settings 1
		m_JoyButtonHalfpress[i] = new wxTextCtrl(m_Controller[i], ID_BUTTONHALFPRESS, wxT("0"), wxDefaultPosition, wxSize(59, 19), wxTE_READONLY | wxTE_CENTRE, wxDefaultValidator, wxT("0"));
		m_JoyButtonHalfpress[i]->Enable(false);
		m_bJoyButtonHalfpress[i] = new wxButton(m_Controller[i], IDB_BUTTONHALFPRESS, wxEmptyString, wxDefaultPosition, wxSize(21, 14), 0, wxDefaultValidator, wxEmptyString);
		#ifdef _WIN32
		m_Deadzone[i] = new wxComboBox(m_Controller[i], IDC_DEADZONE, wxEmptyString, wxDefaultPosition, wxSize(59, 21), arrayStringFor_Deadzone, wxCB_READONLY, wxDefaultValidator, wxT("m_Deadzone"));
		m_textDeadzone[i] = new wxStaticText(m_Controller[i], IDT_DEADZONE, wxT("Deadzone"));		
		m_textHalfpress[i] = new wxStaticText(m_Controller[i], IDT_BUTTONHALFPRESS, wxT("Half press"));
		#else
		m_Deadzone[i] = new wxComboBox(m_Controller[i], IDC_DEADZONE, wxEmptyString, wxPoint(167, 398), wxSize(80, 25), arrayStringFor_Deadzone, wxCB_READONLY, wxDefaultValidator, wxT("m_Deadzone"));
		m_textDeadzone[i] = new wxStaticText(m_Controller[i], IDT_DEADZONE, wxT("Deadzone"), wxPoint(105, 404), wxDefaultSize, 0, wxT("Deadzone"));		
		m_textHalfpress[i] = new wxStaticText(m_Controller[i], IDT_BUTTONHALFPRESS, wxT("Half press"), wxPoint(105, 428), wxDefaultSize, 0, wxT("Half press"));
		#endif

		// Populate general settings 1		
		m_gExtrasettings[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Extra settings"));
		m_gGBExtrasettings[i] = new wxGridBagSizer(0, 0);
		m_gGBExtrasettings[i]->Add(m_textDeadzone[i], wxGBPosition(0, 0), wxGBSpan(1, 1), (wxRIGHT | wxTOP), 3);
		m_gGBExtrasettings[i]->Add(m_Deadzone[i], wxGBPosition(0, 1), wxGBSpan(1, 1), (wxBOTTOM), 2);
		m_gGBExtrasettings[i]->Add(m_textHalfpress[i], wxGBPosition(1, 0), wxGBSpan(1, 1), (wxRIGHT | wxTOP), 3);
		m_gGBExtrasettings[i]->Add(m_JoyButtonHalfpress[i], wxGBPosition(1, 1), wxGBSpan(1, 1), (wxALL), 0);
		m_gGBExtrasettings[i]->Add(m_bJoyButtonHalfpress[i], wxGBPosition(1, 2), wxGBSpan(1, 1), (wxLEFT | wxTOP), 2);
		m_gExtrasettings[i]->Add(m_gGBExtrasettings[i], 0, wxEXPAND | wxALL, 3);

		// Create general settings 2 (controller typ)
		m_TSControltype[i] = new wxStaticText(m_Controller[i], IDT_DPADTYPE, wxT("D-Pad"));		
		m_TSTriggerType[i] = new wxStaticText(m_Controller[i], IDT_TRIGGERTYPE, wxT("Trigger"));
		m_ControlType[i] = new wxComboBox(m_Controller[i], IDC_CONTROLTYPE, wxAS_DPadType[0], wxDefaultPosition, wxDefaultSize, wxAS_DPadType, wxCB_READONLY);
		m_TriggerType[i] = new wxComboBox(m_Controller[i], IDC_TRIGGERTYPE, wxAS_TriggerType[0], wxDefaultPosition, wxDefaultSize, wxAS_TriggerType, wxCB_READONLY);

		// Populate general settings 2 (controller typ)
		m_gGenSettings[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("D-pad and trigger"));
		m_gGBGenSettings[i] = new wxGridBagSizer(0, 0);
		m_gGBGenSettings[i]->Add(m_TSControltype[i], wxGBPosition(0, 0), wxGBSpan(1, 1), (wxTOP), 4);
		m_gGBGenSettings[i]->Add(m_ControlType[i], wxGBPosition(0, 1), wxGBSpan(1, 1), (wxBOTTOM | wxLEFT), 2);
		m_gGBGenSettings[i]->Add(m_TSTriggerType[i], wxGBPosition(1, 0), wxGBSpan(1, 1), (wxTOP), 4);
		m_gGBGenSettings[i]->Add(m_TriggerType[i], wxGBPosition(1, 1), wxGBSpan(1, 1), (wxLEFT), 2);
		m_gGenSettings[i]->Add(m_gGBGenSettings[i], 0, wxEXPAND | wxALL, 3);		

		// Create objects for general settings 3
		m_gGenSettingsID[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Settings") );
		m_CBSaveByID[i] = new wxCheckBox(m_Controller[i], IDC_SAVEBYID, wxT("Save by ID"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
		m_CBShowAdvanced[i] = new wxCheckBox(m_Controller[i], IDC_SHOWADVANCED, wxT("Show advanced settings"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
		
		// Populate general settings 3
		m_gGenSettingsID[i]->Add(m_CBSaveByID[i], 0, wxEXPAND | wxALL, 3);
		m_gGenSettingsID[i]->Add(m_CBShowAdvanced[i], 0, wxEXPAND | wxALL, 3);
		
		// Create objects for Rumble settings (general 4)	
		m_RStrength[i] = new wxComboBox(m_Controller[i], IDC_RUMBLESTRENGTH, wxAS_RumbleStrength[0], wxDefaultPosition, wxSize(85, 20), wxAS_RumbleStrength, wxCB_READONLY);
		m_Rumble[i] = new wxCheckBox(m_Controller[i], IDC_ENABLERUMBLE, wxT("Enable Rumble"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
#if !SDL_VERSION_ATLEAST(1, 3, 0) && !defined(_WIN32)
		m_Rumble[i]->Disable();
#endif

		// Populate general settings 4
		m_gRumble[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Rumble settings"));
		m_gGBRumble[i] = new wxGridBagSizer(0, 0);
		m_gGBRumble[i]->Add(m_Rumble[i], wxGBPosition(0, 0), wxGBSpan(1, 1), (wxTOP), 1);
		m_gGBRumble[i]->Add(m_RStrength[i], wxGBPosition(1, 0), wxGBSpan(1, 1), (wxTOP), 6);
		m_gRumble[i]->Add(m_gGBRumble[i], 0, wxEXPAND | wxALL, 3);

		// Create tooltips	
		m_ControlType[i]->SetToolTip(wxT(
			"Use a 'hat' on your gamepad or configure a custom button for each direction."
			));
		m_TriggerType[i]->SetToolTip(wxT(
			"Select XInput if you want the triggers to work with the XBox 360 pad."
			));
		m_CBSaveByID[i]->SetToolTip(wxString::Format(
			wxT("Map these settings to the selected controller device instead of to the")
			wxT("\nselected slot (1, 2, 3 or 4). This may be a more convenient way")
			wxT("\nto save your settings if you have multiple controllers.")
			, i+1
			));	

		// Populate settings
		m_sSettings[i] = new wxBoxSizer ( wxHORIZONTAL );
		m_sSettings[i]->Add(m_gExtrasettings[i], 0, wxEXPAND | wxALL, 0);
		m_sSettings[i]->Add(m_gGenSettings[i], 0, wxEXPAND | wxLEFT, 5);
		m_sSettings[i]->Add(m_gGenSettingsID[i], 0, wxEXPAND | wxLEFT, 5);
		m_sSettings[i]->Add(m_gRumble[i], 0, wxEXPAND | wxLEFT, 5);

		// Advanced settings

		// Input status controls
		
		// Input status text
		CreateAdvancedControls(i);

		// Main-stick sizers
		m_GBAdvancedMainStick[i] = new wxGridBagSizer(0, 0);
		m_GBAdvancedMainStick[i]->Add(m_pInStatus[i], wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL, 0);
		m_GBAdvancedMainStick[i]->Add(m_pOutStatus[i], wxGBPosition(0, 1), wxGBSpan(1, 1), wxLEFT, 5);
		m_GBAdvancedMainStick[i]->Add(m_TStatusIn[i], wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL, 0);
		m_GBAdvancedMainStick[i]->Add(m_TStatusOut[i], wxGBPosition(1, 1), wxGBSpan(1, 1), wxLEFT, 5);
		// Cstick sizers
		m_GBAdvancedCStick[i] = new wxGridBagSizer(0, 0);
		m_GBAdvancedCStick[i]->Add(m_pInStatusC[i], wxGBPosition(0, 0), wxGBSpan(1, 1), wxALL, 0);
		m_GBAdvancedCStick[i]->Add(m_pOutStatusC[i], wxGBPosition(0, 1), wxGBSpan(1, 1), wxLEFT, 5);
		m_GBAdvancedCStick[i]->Add(m_TStatusInC[i], wxGBPosition(1, 0), wxGBSpan(1, 1), wxALL, 0);
		m_GBAdvancedCStick[i]->Add(m_TStatusOutC[i], wxGBPosition(1, 1), wxGBSpan(1, 1), wxLEFT, 5);
		// Add sizers
		m_gStatusIn[i]->Add(m_GBAdvancedMainStick[i], 0, wxLEFT, 5);
		m_gStatusInC[i]->Add(m_GBAdvancedCStick[i], 0, wxLEFT, 5);

		// Populate input status settings

		// The drop down menu
		m_gStatusInSettings[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Main-stick settings"));
		m_gStatusInSettingsRadiusH[i] = new wxBoxSizer(wxHORIZONTAL);
		m_gStatusInSettingsC[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("C-stick settings"));
		m_gStatusInSettingsRadiusHC[i] = new wxBoxSizer(wxHORIZONTAL);
		wxArrayString asRadius;
			asRadius.Add(wxT("100%"));
			asRadius.Add(wxT("90%"));
			asRadius.Add(wxT("80%"));
			asRadius.Add(wxT("70%"));
			asRadius.Add(wxT("60%"));
			asRadius.Add(wxT("50%"));
			asRadius.Add(wxT("40%"));
		m_CoBRadius[i] = new wxComboBox(m_Controller[i], IDCB_MAINSTICK_RADIUS, asRadius[0], wxDefaultPosition, wxDefaultSize, asRadius, wxCB_READONLY);
		m_CoBRadiusC[i] = new wxComboBox(m_Controller[i], IDCB_CSTICK_RADIUS, asRadius[0], wxDefaultPosition, wxDefaultSize, asRadius, wxCB_READONLY);

		// The checkbox
		m_CBRadius[i] = new wxCheckBox(m_Controller[i], IDCB_MAINSTICK_CB_RADIUS, wxT("Radius"));
		m_CBRadiusC[i] = new wxCheckBox(m_Controller[i], IDCB_CSTICK_CB_RADIUS, wxT("Radius"));
		wxString CBRadiusToolTip = wxT("This will reduce the stick radius.");
		m_CBRadius[i]->SetToolTip(CBRadiusToolTip);
		m_CBRadiusC[i]->SetToolTip(CBRadiusToolTip);

		// The drop down menu);
		m_gStatusInSettingsH[i] = new wxBoxSizer(wxHORIZONTAL);
		m_gStatusInSettingsHC[i] = new wxBoxSizer(wxHORIZONTAL);
		wxArrayString asStatusInSet;
			asStatusInSet.Add(wxT("100%"));
			asStatusInSet.Add(wxT("95%"));
			asStatusInSet.Add(wxT("90%"));
			asStatusInSet.Add(wxT("85%"));
			asStatusInSet.Add(wxT("80%"));
			asStatusInSet.Add(wxT("75%"));
		m_CoBDiagonal[i] = new wxComboBox(m_Controller[i], IDCB_MAINSTICK_DIAGONAL, asStatusInSet[0], wxDefaultPosition, wxDefaultSize, asStatusInSet, wxCB_READONLY);
		m_CoBDiagonalC[i] = new wxComboBox(m_Controller[i], IDCB_CSTICK_DIAGONAL, asStatusInSet[0], wxDefaultPosition, wxDefaultSize, asStatusInSet, wxCB_READONLY);

		// The checkbox
		m_CBS_to_C[i] = new wxCheckBox(m_Controller[i], IDCB_MAINSTICK_S_TO_C, wxT("Diagonal"));
		m_CBS_to_CC[i] = new wxCheckBox(m_Controller[i], IDCB_CSTICK_S_TO_C, wxT("Diagonal"));
		wxString CBS_to_CToolTip = 
			wxT("This will convert a square stick radius to a circle stick radius similar to the octagonal area that the original GameCube pad produce.")
			wxT(" To produce a smooth circle in the 'Out' window you have to manually set")
			wxT(" your diagonal values from the 'In' window in the drop down menu.");
		m_CBS_to_C[i]->SetToolTip(CBS_to_CToolTip);
		m_CBS_to_CC[i]->SetToolTip(CBS_to_CToolTip);

		// Populate sizers
		m_gStatusInSettings[i]->Add(m_gStatusInSettingsRadiusH[i], 0, (wxLEFT | wxRIGHT | wxBOTTOM), 4);
		m_gStatusInSettings[i]->Add(m_gStatusInSettingsH[i], 0, (wxLEFT | wxRIGHT | wxBOTTOM), 4);	
		// C-stick
		m_gStatusInSettingsC[i]->Add(m_gStatusInSettingsRadiusHC[i], 0, (wxLEFT | wxRIGHT | wxBOTTOM), 4);
		m_gStatusInSettingsC[i]->Add(m_gStatusInSettingsHC[i], 0, (wxLEFT | wxRIGHT | wxBOTTOM), 4);		

		m_gStatusInSettingsRadiusH[i]->Add(m_CBRadius[i], 0, wxLEFT | wxTOP, 3);
		m_gStatusInSettingsRadiusH[i]->Add(m_CoBRadius[i], 0, wxLEFT, 3);
		m_gStatusInSettingsH[i]->Add(m_CBS_to_C[i], 0, wxLEFT | wxTOP, 3);
		m_gStatusInSettingsH[i]->Add(m_CoBDiagonal[i], 0, wxLEFT, 3);
		// C-stick
		m_gStatusInSettingsRadiusHC[i]->Add(m_CBRadiusC[i], 0, wxLEFT | wxTOP, 3);
		m_gStatusInSettingsRadiusHC[i]->Add(m_CoBRadiusC[i], 0, wxLEFT, 3);
		m_gStatusInSettingsHC[i]->Add(m_CBS_to_CC[i], 0, wxLEFT | wxTOP, 3);
		m_gStatusInSettingsHC[i]->Add(m_CoBDiagonalC[i], 0, wxLEFT, 3);

		// The trigger values
		m_gStatusTriggers[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Trigger values"));
		m_TStatusTriggers[i] = new wxStaticText(m_Controller[i], IDT_TRIGGERS, wxT("Left:  Right:"));
		m_gStatusTriggers[i]->Add(m_TStatusTriggers[i], 0, (wxALL), 4);

		m_gStatusAdvancedSettings[i] = new wxStaticBoxSizer( wxVERTICAL, m_Controller[i], wxT("Advanced settings"));
		m_CBCheckFocus[i] = new wxCheckBox(m_Controller[i], IDCB_CHECKFOCUS, wxT("Allow out of focus input"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator);
		m_AdvancedMapFilter[i] = new wxCheckBox(m_Controller[i], IDCB_FILTER_SETTINGS , wxT("No trigger filter"));
		m_gStatusAdvancedSettings[i]->Add(m_CBCheckFocus[i], 0, (wxALL), 4);
		m_gStatusAdvancedSettings[i]->Add(m_AdvancedMapFilter[i], 0, (wxALL), 4);

		// Tool tips
		m_CBCheckFocus[i]->SetToolTip(wxT(
			"Allow gamepad input even when Dolphin is not in focus. Out of focus keyboard input is never allowed."));
		m_AdvancedMapFilter[i]->SetToolTip(
			wxT("This will allow you to map a digital axis to the main stick or the C-stick. If you don't have")
			wxT(" any analog triggers that will be automatically set when the trigger filter is off.")
			);
		

		// Populate sizers

		// Populate main left sizer
		m_sMainLeft[i] = new wxBoxSizer(wxVERTICAL);
		m_sMainLeft[i]->Add(m_gJoyname[i], 0, wxEXPAND | (wxALL), 5);
		m_sMainLeft[i]->Add(m_sKeys[i], 1, wxEXPAND | (wxLEFT | wxRIGHT), 5);
		m_sMainLeft[i]->Add(m_sSettings[i], 0, wxEXPAND | (wxALL), 5);

		// Populate main right sizer
		m_sMainRight[i] = new wxBoxSizer(wxVERTICAL);
		m_sMainRight[i]->Add(m_gStatusIn[i], 0, wxEXPAND | (wxLEFT), 2);
		m_sMainRight[i]->Add(m_gStatusInSettings[i], 0, wxEXPAND | (wxLEFT | wxTOP), 2);	
		m_sMainRight[i]->Add(m_gStatusInC[i], 0, wxEXPAND | (wxLEFT), 2);
		m_sMainRight[i]->Add(m_gStatusInSettingsC[i], 0, wxEXPAND | (wxLEFT | wxTOP), 2);
		m_sMainRight[i]->Add(m_gStatusTriggers[i], 0, wxEXPAND | (wxLEFT | wxTOP), 2);
		m_sMainRight[i]->Add(m_gStatusAdvancedSettings[i], 0, wxEXPAND | (wxLEFT | wxTOP), 2);
#ifdef RERECORDING
		m_sMainRight[i]->Add(m_SizeRecording[i], 0, wxEXPAND | (wxLEFT | wxTOP), 2);
#endif

		// Populate main sizer
		m_sMain[i] = new wxBoxSizer(wxHORIZONTAL);
		m_sMain[i]->Add(m_sMainLeft[i], 0, wxEXPAND | (wxALL), 0);
		m_sMain[i]->Add(m_sMainRight[i], 0, wxEXPAND | (wxRIGHT | wxTOP), 5);
		m_Controller[i]->SetSizerAndFit(m_sMain[i]); // Set the main sizer

		// Show or hide it. We have to do this after we add it to its sizer
		m_sMainRight[i]->Show(g_Config.bShowAdvanced);
	} // end of loop

	// Populate buttons sizer.
	wxBoxSizer * m_sButtons = new wxBoxSizer(wxHORIZONTAL);
	m_sButtons->Add(m_About, 0, (wxBOTTOM), 0);
	m_sButtons->AddStretchSpacer(1);
	m_sButtons->Add(m_OK, 0, wxALIGN_RIGHT | (wxBOTTOM), 0);
	m_sButtons->Add(m_Cancel, 0, wxALIGN_RIGHT | (wxLEFT), 5);


	// Populate master sizer.
	m_MainSizer = new wxBoxSizer(wxVERTICAL);
	m_MainSizer->Add(m_Notebook, 0, wxEXPAND | wxALL, 5);
	m_MainSizer->Add(m_sButtons, 1, wxEXPAND | ( wxLEFT | wxRIGHT | wxBOTTOM), 5);
	SetSizerAndFit(m_MainSizer);

	// Debugging
	#ifdef SHOW_PAD_STATUS
		m_pStatusBar = new wxStaticText(this, IDT_DEBUGGING, wxT("Debugging"), wxPoint(135, 100), wxSize(200, -1));
	#endif

	// Set window size
	Center();

	// All done
	ControlsCreated = true;

	// Replace the harder to understand -1 with "" for the sake of user friendliness
	ToBlank();
}