// for finding memory leaks in debug mode with Visual Studio
#if defined _DEBUG && defined _MSC_VER
#include <crtdbg.h>
#endif

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "ft2_header.h"
#include "ft2_keyboard.h"
#include "ft2_gui.h"
#include "ft2_about.h"
#include "ft2_video.h"
#include "ft2_edit.h"
#include "ft2_config.h"
#include "ft2_help.h"
#include "ft2_mouse.h"
#include "ft2_nibbles.h"
#include "ft2_inst_ed.h"
#include "ft2_pattern_ed.h"
#include "ft2_diskop.h"
#include "ft2_wav_renderer.h"
#include "ft2_sample_ed.h"
#include "ft2_audio.h"
#include "ft2_trim.h"
#include "ft2_sample_ed_features.h"
#include "ft2_midi.h"
#include "ft2_structs.h"

keyb_t keyb; // globalized

static const uint8_t scancodeKey2Note[52] = // keys (USB usage page standard) to FT2 notes look-up table
{
	0x08, 0x05, 0x04, 0x11, 0x00, 0x07, 0x09, 0x19,
	0x0B, 0x00, 0x0E, 0x0C, 0x0A, 0x1B, 0x1D, 0x0D,
	0x12, 0x02, 0x14, 0x18, 0x06, 0x0F, 0x03, 0x16,
	0x01, 0x00, 0x0E, 0x10, 0x00, 0x13, 0x15, 0x17,
	0x00, 0x1A, 0x1C, 0x22, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x1F, 0x1E, 0x20, 0x13, 0x00, 0x10, 0x00,
	0x00, 0x0D, 0x0F, 0x11
};

static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey);
static bool checkModifiedKeys(SDL_Keycode keycode);

int8_t scancodeKeyToNote(SDL_Scancode scancode)
{
	if (scancode == SDL_SCANCODE_CAPSLOCK || scancode == SDL_SCANCODE_NONUSBACKSLASH)
		return NOTE_OFF;

	// translate key to note
	int8_t note = 0;
	if (scancode >= SDL_SCANCODE_B && scancode <= SDL_SCANCODE_SLASH)
		note = scancodeKey2Note[(int32_t)scancode - SDL_SCANCODE_B];

	if (note == 0)
		return -1; // not a note key, do further key handling

	return note + (editor.curOctave * 12);
}

void readKeyModifiers(void)
{
	const SDL_Keymod modState = SDL_GetModState();

	keyb.leftCtrlPressed = (modState & KMOD_LCTRL) ? true : false;
	keyb.leftAltPressed = (modState & KMOD_LALT) ? true : false;
	keyb.leftShiftPressed = (modState & KMOD_LSHIFT) ? true : false;
#ifdef __APPLE__
	keyb.leftCommandPressed = (modState & KMOD_LGUI) ? true : false;
#endif
	keyb.keyModifierDown = (modState & (KMOD_LSHIFT | KMOD_LCTRL | KMOD_LALT | KMOD_LGUI)) ? true : false;


#ifdef _WIN32
	keyb.leftWinKeyDown = (modState & KMOD_LGUI) ? true : false;
#endif
}

void keyUpHandler(SDL_Scancode scancode, SDL_Keycode keycode)
{
	if (editor.editTextFlag || ui.sysReqShown)
		return; // kludge: don't handle key up! (XXX: Is this hack really needed anymore?)

	/* Yet another kludge for not leaving a ghost key-up event after an inputBox/okBox
	** was exited with a key press. They could be picked up as note release events.
	*/
	if (keyb.ignoreCurrKeyUp)
	{
		keyb.ignoreCurrKeyUp = false;
		return;
	}

	if (cursor.object == CURSOR_NOTE && !keyb.keyModifierDown)
		testNoteKeysRelease(scancode);

	if (scancode == SDL_SCANCODE_KP_PLUS)
		keyb.numPadPlusPressed = false;

	keyb.keyRepeat = false;

	(void)keycode;
}

void keyDownHandler(SDL_Scancode scancode, SDL_Keycode keycode, bool keyWasRepeated)
{
	if (keycode == SDLK_UNKNOWN)
		return;

	// revert "delete/rename" mouse modes (disk op.)
	if (mouse.mode != MOUSE_MODE_NORMAL)
		setMouseMode(MOUSE_MODE_NORMAL);

	if (ui.sysReqShown)
	{
		if (keycode == SDLK_RETURN)
			ui.sysReqEnterPressed = true;
		else if (keycode == SDLK_ESCAPE)
			ui.sysReqShown = false;

		return;
	}

	if (testNibblesCheatCodes(keycode))
		return; // current key (+ modifiers) matches nibbles cheat code sequence, ignore current key here

	if (keyWasRepeated)
	{
		if (editor.NI_Play || !keyb.keyRepeat)
			return; // do NOT repeat keys in Nibbles or if keyRepeat is disabled
	}

	if (scancode != SDL_SCANCODE_ESCAPE)
		keyb.keyRepeat = true;

	// handle certain keys (home/end/left/right etc) when editing text
	if (editor.editTextFlag)
	{
		handleTextEditControl(keycode);
		return;
	}

	if (editor.NI_Play)
	{
		nibblesKeyAdministrator(scancode);
		return;
	}

	if (keycode == SDLK_ESCAPE)
	{
		if (quitBox(false) == 1)
			editor.throwExit = true;

		return;
	}

	if (scancode == SDL_SCANCODE_KP_PLUS)
		keyb.numPadPlusPressed = true;

	if (handleEditKeys(keycode, scancode))
		return;

	if (keyb.keyModifierDown && checkModifiedKeys(keycode))
		return;

	handleKeys(keycode, scancode); // no pattern editing, do general key handling
}

static void handleKeys(SDL_Keycode keycode, SDL_Scancode scanKey)
{
	// if we're holding numpad plus but not pressing bank keys, don't check any other key
	if (keyb.numPadPlusPressed && !keyb.leftCtrlPressed)
	{
		if (scanKey != SDL_SCANCODE_NUMLOCKCLEAR && scanKey != SDL_SCANCODE_KP_DIVIDE &&
			scanKey != SDL_SCANCODE_KP_MULTIPLY  && scanKey != SDL_SCANCODE_KP_MINUS)
		{
			return;
		}
	}

	// handle scankeys (actual key on keyboard, using US keyb. layout)
	switch (scanKey)
	{
		case SDL_SCANCODE_KP_ENTER: pbSwapInstrBank(); return;

		case SDL_SCANCODE_NUMLOCKCLEAR:
		{
			if (keyb.numPadPlusPressed)
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank13();
				else
					pbSetInstrBank5();
			}
			else
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank9();
				else
					pbSetInstrBank1();
			}
		}
		return;

		case SDL_SCANCODE_KP_DIVIDE:
		{
			if (keyb.numPadPlusPressed)
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank14();
				else
					pbSetInstrBank6();
			}
			else
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank10();
				else
					pbSetInstrBank2();
			}
		}
		return;

		case SDL_SCANCODE_KP_MULTIPLY:
		{
			if (keyb.numPadPlusPressed)
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank15();
				else
					pbSetInstrBank7();
			}
			else
			{
				if (editor.instrBankSwapped)
					pbSetInstrBank11();
				else
					pbSetInstrBank3();
			}
		}
		return;

		case SDL_SCANCODE_KP_MINUS:
		{
			if (keyb.leftCtrlPressed) // non-FT2 feature: Decrease master volume by 16
			{
				if (config.masterVol >= 16)
					config.masterVol -= 16;
				else
					config.masterVol = 0;

				setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
				if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
					showConfigScreen();
			}
			else
			{
				if (keyb.numPadPlusPressed)
				{
					if (editor.instrBankSwapped)
						pbSetInstrBank16();
					else
						pbSetInstrBank8();
				}
				else
				{
					if (editor.instrBankSwapped)
						pbSetInstrBank12();
					else
						pbSetInstrBank4();
				}
			}
		}
		return;

		case SDL_SCANCODE_KP_PLUS:
		{
			if (keyb.leftCtrlPressed) // non-FT2 feature: Increase master volume by 16
			{
				if (config.masterVol <= 256-16)
					config.masterVol += 16;
				else
					config.masterVol = 256;

				setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
				if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
					showConfigScreen();
			}
		}
		return;

		case SDL_SCANCODE_KP_PERIOD:
		{
			if (editor.curInstr > 0)
			{
				if (keyb.leftShiftPressed) // this only triggers if num lock is off. Probably an SDL bug...
				{
					clearSample();
				}
				else
				{
					if (editor.curInstr == 0 || instr[editor.curInstr] == NULL)
						return;

					if (okBox(1, "System request", "Clear instrument?", NULL) == 1)
					{
						freeInstr(editor.curInstr);
						memset(song.instrName[editor.curInstr], 0, sizeof(song.instrName[editor.curInstr]));
						updateNewInstrument();
						setSongModifiedFlag();
					}
				}
			}
		}
		return;

		case SDL_SCANCODE_KP_0: setNewInstr(0); return;
		case SDL_SCANCODE_KP_1: setNewInstr(editor.instrBankOffset+1); return;
		case SDL_SCANCODE_KP_2: setNewInstr(editor.instrBankOffset+2); return;
		case SDL_SCANCODE_KP_3: setNewInstr(editor.instrBankOffset+3); return;
		case SDL_SCANCODE_KP_4: setNewInstr(editor.instrBankOffset+4); return;
		case SDL_SCANCODE_KP_5: setNewInstr(editor.instrBankOffset+5); return;
		case SDL_SCANCODE_KP_6: setNewInstr(editor.instrBankOffset+6); return;
		case SDL_SCANCODE_KP_7: setNewInstr(editor.instrBankOffset+7); return;
		case SDL_SCANCODE_KP_8: setNewInstr(editor.instrBankOffset+8); return;

		case SDL_SCANCODE_GRAVE: // "key below esc"
		{
			if (keyb.leftShiftPressed)
			{
				// decrease edit skip
				if (editor.editRowSkip == 0)
					editor.editRowSkip = 16;
				else
					editor.editRowSkip--;
			}
			else
			{
				// increase edit skip
				if (editor.editRowSkip == 16)
					editor.editRowSkip = 0;
				else
					editor.editRowSkip++;
			}

			if (!ui.nibblesShown     && !ui.configScreenShown &&
				!ui.aboutScreenShown && !ui.diskOpShown       &&
				!ui.helpScreenShown  && !ui.extendedPatternEditor)
			{
				drawIDAdd();
			}
		}
		return;

		default: break;
	}

	// no normal key (keycode) pressed (XXX: shouldn't happen..?)
	if (keycode == SDLK_UNKNOWN)
		return;

	// handle normal keys (keycodes - affected by keyb. layout in OS)
	switch (keycode)
	{
		default: return;

		case SDLK_DELETE: // non-FT2 addition
		{
			if (ui.sampleEditorShown)
				sampCut();
		}
		break;

		// This is maybe not an ideal key for this anymore...
		//case SDLK_PRINTSCREEN: togglePatternEditorExtended(); break;

		// EDIT/PLAY KEYS

		// record pattern
		case SDLK_RSHIFT: startPlaying(PLAYMODE_RECPATT, 0); break;

		// play song
#ifdef __APPLE__
		case SDLK_RGUI: // fall-through for Apple keyboards
#endif
		case SDLK_RCTRL:
			startPlaying(PLAYMODE_SONG, 0);
		break;

		// play pattern
		case SDLK_RALT:
		{
			if (!keyb.leftCtrlPressed) // kludge for Mac (toggle fullscreen)
				startPlaying(PLAYMODE_PATT, 0);
		}
		break;

		case SDLK_SPACE:
		{
			if (playMode == PLAYMODE_IDLE)
			{
				lockMixerCallback();
				memset(editor.keyOnTab, 0, sizeof (editor.keyOnTab));
				playMode = PLAYMODE_EDIT;
				ui.updatePosSections = true; // for updating mode text
				unlockMixerCallback();
			}
			else
			{
				stopPlaying();
			}
		}
		break;

		case SDLK_TAB:
		{
			if (keyb.leftShiftPressed)
				cursorTabLeft();
			else
				cursorTabRight();
		}
		break;

		case SDLK_LEFT:  cursorLeft();  break;
		case SDLK_RIGHT: cursorRight(); break;

		// function Keys (F1..F12)

		case SDLK_F1:
		{
			     if (keyb.leftShiftPressed) trackTranspAllInsDn();
			else if (keyb.leftCtrlPressed)  pattTranspAllInsDn();
			else if (keyb.leftAltPressed)   blockTranspAllInsDn();
			else                            editor.curOctave = 0;
		}
		break;

		case SDLK_F2:
		{
			     if (keyb.leftShiftPressed) trackTranspAllInsUp();
			else if (keyb.leftCtrlPressed)  pattTranspAllInsUp();
			else if (keyb.leftAltPressed)   blockTranspAllInsUp();
			else                            editor.curOctave = 1;
		}
		break;

		case SDLK_F3:
		{
			     if (keyb.leftShiftPressed) cutTrack();
			else if (keyb.leftCtrlPressed)  cutPattern();
			else if (keyb.leftAltPressed)   cutBlock();
			else                            editor.curOctave = 2;
		}
		break;

		case SDLK_F4:
		{
			     if (keyb.leftShiftPressed) copyTrack();
			else if (keyb.leftCtrlPressed)  copyPattern();
			else if (keyb.leftAltPressed)   copyBlock();
			else                            editor.curOctave = 3;
		}
		break;

		case SDLK_F5:
		{
			     if (keyb.leftShiftPressed) pasteTrack();
			else if (keyb.leftCtrlPressed)  pastePattern();
			else if (keyb.leftAltPressed)   pasteBlock();
			else                            editor.curOctave = 4;
		}
		break;

		case SDLK_F6: editor.curOctave = 5; break;

		case SDLK_F7:
		{
			     if (keyb.leftShiftPressed) trackTranspCurInsDn();
			else if (keyb.leftCtrlPressed)  pattTranspCurInsDn();
			else if (keyb.leftAltPressed)   blockTranspCurInsDn();
			else                            editor.curOctave = 6;
		}
		break;

		case SDLK_F8:
		{
			     if (keyb.leftShiftPressed) trackTranspCurInsUp();
			else if (keyb.leftCtrlPressed)  pattTranspCurInsUp();
			else if (keyb.leftAltPressed)   blockTranspCurInsUp();
			else                            editor.curOctave = 6;
		}
		break;

		case SDLK_F9:
		{
			lockAudio();

			song.row = editor.ptnJumpPos[0];
			if (song.row >= song.currNumRows)
				song.row = song.currNumRows - 1;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			unlockAudio();
		}
		break;

		case SDLK_F10:
		{
			lockAudio();

			song.row = editor.ptnJumpPos[1];
			if (song.row >= song.currNumRows)
				song.row = song.currNumRows - 1;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			unlockAudio();
		}
		break;

		case SDLK_F11:
		{
			lockAudio();

			song.row = editor.ptnJumpPos[2];
			if (song.row >= song.currNumRows)
				song.row  = song.currNumRows - 1;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			unlockAudio();
		}
		break;

		case SDLK_F12:
		{
			lockAudio();

			song.row = editor.ptnJumpPos[3];
			if (song.row >= song.currNumRows)
				song.row = song.currNumRows - 1;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			unlockAudio();
		}
		break;

		// PATTERN EDITOR POSITION KEYS
		
		case SDLK_INSERT:
		{
			if (keyb.leftShiftPressed)
				insertPatternLine();
			else
				insertPatternNote();
		}
		break;

		case SDLK_BACKSPACE:
		{
			     if (ui.diskOpShown) diskOpGoParent();
			else if (keyb.leftShiftPressed) deletePatternLine();
			else                            deletePatternNote();
		}
		break;

		case SDLK_UP:
		{
			if (keyb.leftShiftPressed)
			{
				decCurIns();
			}
			else
			{
				if (keyb.leftAltPressed)
					keybPattMarkUp();
				else
					rowOneUpWrap();
			}
			break;
		}
		break;

		case SDLK_DOWN:
		{
			if (keyb.leftShiftPressed)
			{
				incCurIns();
			}
			else
			{
				if (keyb.leftAltPressed)
					keybPattMarkDown();
				else
					rowOneDownWrap();
			}
		}
		break;

		case SDLK_PAGEUP:
		{
			const bool audioWasntLocked = !audio.locked;
			if (audioWasntLocked)
				lockAudio();

			song.row -= 16;
			if (song.row < 0)
				song.row = 0;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			if (audioWasntLocked)
				unlockAudio();
		}
		break;

		case SDLK_PAGEDOWN:
		{
			const bool audioWasntLocked = !audio.locked;
			if (audioWasntLocked)
				lockAudio();

			song.row += 16;
			if (song.row >= song.currNumRows)
				song.row = song.currNumRows-1;

			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			if (audioWasntLocked)
				unlockAudio();
		}
		break;

		case SDLK_HOME:
		{
			const bool audioWasntLocked = !audio.locked;
			if (audioWasntLocked)
				lockAudio();

			song.row = 0;
			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			if (audioWasntLocked)
				unlockAudio();
		}
		break;

		case SDLK_END:
		{
			const bool audioWasntLocked = !audio.locked;
			if (audioWasntLocked)
				lockAudio();

			song.row = song.currNumRows - 1;
			if (!songPlaying)
			{
				editor.row = (uint8_t)song.row;
				ui.updatePatternEditor = true;
			}

			if (audioWasntLocked)
				unlockAudio();
		}
		break;
	}
}

static bool checkModifiedKeys(SDL_Keycode keycode)
{
	// normal keys
	switch (keycode)
	{
		default: break;

		case SDLK_KP_ENTER:
		case SDLK_RETURN:
		{
			if (keyb.leftAltPressed && !keyb.leftCtrlPressed)
			{
				toggleFullscreen();
				return true;
			}
			else if (keyb.leftCommandPressed || keyb.leftCtrlPressed)
			{
				if (keyb.leftShiftPressed)
					insertPatternLine();
				else
					insertPatternNote();
			}
		}
		break;

		case SDLK_F9:
		{
			if (keyb.leftCtrlPressed)
			{
				startPlaying(PLAYMODE_PATT, editor.ptnJumpPos[0]);
				return true;
			}
			else if (keyb.leftShiftPressed)
			{
				editor.ptnJumpPos[0] = (uint8_t)editor.row;
				return true;
			}
		}
		break;

		case SDLK_F10:
		{
			if (keyb.leftCtrlPressed)
			{
				startPlaying(PLAYMODE_PATT, editor.ptnJumpPos[1]);
				return true;
			}
			else if (keyb.leftShiftPressed)
			{
				editor.ptnJumpPos[1] = (uint8_t)editor.row;
				return true;
			}
		}
		break;

		case SDLK_F11:
		{
			if (keyb.leftCtrlPressed)
			{
				startPlaying(PLAYMODE_PATT, editor.ptnJumpPos[2]);
				return true;
			}
			else if (keyb.leftShiftPressed)
			{
				editor.ptnJumpPos[2] = (uint8_t)editor.row;
				return true;
			}
		}
		break;

		case SDLK_F12:
		{
			if (keyb.leftCtrlPressed)
			{
				startPlaying(PLAYMODE_PATT, editor.ptnJumpPos[3]);
				return true;
			}
			else if (keyb.leftShiftPressed)
			{
				editor.ptnJumpPos[3] = (uint8_t)editor.row;
				return true;
			}
		}
		break;

		case SDLK_a:
		{
			if (ui.sampleEditorShown)
			{
#ifdef __APPLE__
				if (keyb.leftCtrlPressed || keyb.leftAltPressed || keyb.leftCommandPressed)
#else
				if (keyb.leftCtrlPressed || keyb.leftAltPressed)
#endif
				{
					rangeAll();
					return true;
				}
			}
			else if (keyb.leftCtrlPressed)
			{
				showAdvEdit();
				return true;
			}
			else if (keyb.leftAltPressed)
			{
				jumpToChannel(8);
				return true;
			}
		}
		break;

		case SDLK_b:
		{
			if (keyb.leftCtrlPressed)
			{
				if (!ui.aboutScreenShown)
					showAboutScreen();

				return true;
			}
		}
		break;

		case SDLK_c:
		{
#ifdef __APPLE__
			if (keyb.leftAltPressed || keyb.leftCommandPressed)
#else
			if (keyb.leftAltPressed)
#endif
			{
				if (ui.sampleEditorShown)
				{
					sampCopy();
				}
				else
				{
					// mark current track (non-FT2 feature)
					pattMark.markX1 = cursor.ch;
					pattMark.markX2 = pattMark.markX1;
					pattMark.markY1 = 0;
					pattMark.markY2 = patternNumRows[editor.editPattern];

					ui.updatePatternEditor = true;
				}

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				if (ui.sampleEditorShown)
					sampCopy();
				else
					showConfigScreen();

				return true;
			}
		}
		break;

		case SDLK_d:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(10);
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				if (!ui.diskOpShown)
					showDiskOpScreen();

				return true;
			}
		}
		break;

		case SDLK_e:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(2);
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				if (ui.aboutScreenShown)  hideAboutScreen();
				if (ui.configScreenShown) hideConfigScreen();
				if (ui.helpScreenShown)   hideHelpScreen();
				if (ui.nibblesShown)      hideNibblesScreen();

				showSampleEditorExt();
				return true;
			}
		}
		break;

		case SDLK_f:
		{
#ifdef __APPLE__
			if (keyb.leftCommandPressed && keyb.leftCtrlPressed)
			{
				toggleFullscreen();
				return true;
			}
			else
#endif
			if (keyb.leftShiftPressed && keyb.leftCtrlPressed)
			{
				resetFPSCounter();
				video.showFPSCounter ^= 1;
				if (!video.showFPSCounter)
				{
					if (ui.extendedPatternEditor) // yet another kludge...
						exitPatternEditorExtended();

					showTopScreen(false);
				}
			}
			else if (keyb.leftAltPressed)
			{
				jumpToChannel(11);
				return true;
			}
		}
		break;

		case SDLK_g:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(12);
				return true;
			}
		}
		break;

		case SDLK_h:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(13);
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				showHelpScreen();
				return true;
			}
		}
		break;

		case SDLK_i:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(7);
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				showInstEditor();
				return true;
			}
		}
		break;

		case SDLK_j:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(14);
				return true;
			}
		}
		break;

		case SDLK_k:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(15);
				return true;
			}
		}
		break;

		case SDLK_m:
		{
			if (keyb.leftCtrlPressed)
			{
				if (ui.aboutScreenShown)  hideAboutScreen();
				if (ui.configScreenShown) hideConfigScreen();
				if (ui.helpScreenShown)   hideHelpScreen();
				if (ui.nibblesShown)      hideNibblesScreen();

				showInstEditorExt();

				return true;
			}
		}
		break;

		case SDLK_n:
		{
			if (keyb.leftCtrlPressed)
			{
				showNibblesScreen();
				return true;
			}
		}
		break;

		case SDLK_p:
		{
			if (keyb.leftCtrlPressed)
			{
				if (!ui.patternEditorShown)
				{
					if (ui.sampleEditorShown)    hideSampleEditor();
					if (ui.sampleEditorExtShown) hideSampleEditorExt();
					if (ui.instEditorShown)      hideInstEditor();

					showPatternEditor();
				}

				return true;
			}
		}
		break;

		case SDLK_q:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(0);
				return true;
			}
		}
		break;

		case SDLK_r:
		{
			if (keyb.leftAltPressed)
			{
				if (ui.sampleEditorShown)
					sampCrop();
				else
					jumpToChannel(3);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				showTrimScreen();
				return true;
			}
		}
		break;

		case SDLK_s:
		{
			if (keyb.leftAltPressed)
			{
				if (ui.sampleEditorShown)
					showRange();
				else
					jumpToChannel(9);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				showSampleEditor();
				return true;
			}
		}
		break;

		case SDLK_t:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(4);
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				showTranspose();
				return true;
			}
		}
		break;

		case SDLK_u:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(6);
				return true;
			}
		}
		break;

		case SDLK_v:
		{
#ifdef __APPLE__
			if (keyb.leftAltPressed || keyb.leftCommandPressed)
#else
			if (keyb.leftAltPressed)
#endif
			{
				if (ui.sampleEditorShown)
					sampPaste();
				else if (!ui.instEditorShown)
					scaleFadeVolumeBlock();

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				if (ui.sampleEditorShown)
					sampPaste();
				else if (!ui.instEditorShown)
					scaleFadeVolumePattern();

				return true;
			}
			else if (keyb.leftShiftPressed)
			{
				if (!ui.sampleEditorShown && !ui.instEditorShown)
				{
					keyb.ignoreTextEditKey = true; // ignore key from first frame
					scaleFadeVolumeTrack();
				}

				return true;
			}
		}
		break;

		case SDLK_w:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(1);
				return true;
			}
		}
		break;

		case SDLK_x:
		{
#ifdef __APPLE__
			if (keyb.leftAltPressed || keyb.leftCommandPressed)
#else
			if (keyb.leftAltPressed)
#endif
			{
				if (ui.sampleEditorShown)
					sampCut();

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				if (ui.extendedPatternEditor)
					exitPatternEditorExtended();

				if (ui.sampleEditorShown)    hideSampleEditor();
				if (ui.sampleEditorExtShown) hideSampleEditorExt();
				if (ui.instEditorShown)      hideInstEditor();
				if (ui.instEditorExtShown)   hideInstEditorExt();
				if (ui.transposeShown)       hideTranspose();
				if (ui.aboutScreenShown)     hideAboutScreen();
				if (ui.configScreenShown)    hideConfigScreen();
				if (ui.helpScreenShown)      hideHelpScreen();
				if (ui.nibblesShown)         hideNibblesScreen();
				if (ui.diskOpShown)          hideDiskOpScreen();
				if (ui.advEditShown)         hideAdvEdit();
				if (ui.wavRendererShown)     hideWavRenderer();
				if (ui.trimScreenShown)      hideTrimScreen();

				showTopScreen(false);
				showBottomScreen();

				showPatternEditor();

				return true;
			}
		}
		break;

		case SDLK_y:
		{
			if (keyb.leftAltPressed)
			{
				jumpToChannel(5);
				return true;
			}
		}
		break;

		case SDLK_z:
		{
			if (keyb.leftAltPressed)
			{
				if (ui.sampleEditorShown)
					zoomOut();

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				togglePatternEditorExtended();
				return true;
			}
		}
		break;

		case SDLK_1:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(1-1);
				else
					writeFromMacroSlot(1-1);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				editor.currConfigScreen = 0;
				showConfigScreen();
				checkRadioButton(RB_CONFIG_AUDIO);

				return true;
			}
		}
		break;

		case SDLK_2:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(2-1);
				else
					writeFromMacroSlot(2-1);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				editor.currConfigScreen = 1;
				showConfigScreen();
				checkRadioButton(RB_CONFIG_LAYOUT);

				return true;
			}
		}
		break;

		case SDLK_3:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(3-1);
				else
					writeFromMacroSlot(3-1);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				editor.currConfigScreen = 2;
				showConfigScreen();
				checkRadioButton(RB_CONFIG_MISCELLANEOUS);

				return true;
			}
		}
		break;

		case SDLK_4:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(4-1);
				else
					writeFromMacroSlot(4-1);

				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
#ifdef HAS_MIDI
				editor.currConfigScreen = 3;
				showConfigScreen();
				checkRadioButton(RB_CONFIG_MIDI_INPUT);
#endif
				return true;
			}
		}
		break;

		case SDLK_5:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(5-1);
				else
					writeFromMacroSlot(5-1);

				return true;
			}
		}
		break;

		case SDLK_6:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(6-1);
				else
					writeFromMacroSlot(6-1);

				return true;
			}
		}
		break;

		case SDLK_7:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(7-1);
				else
					writeFromMacroSlot(7-1);

				return true;
			}
		}
		break;

		case SDLK_8:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(8-1);
				else
					writeFromMacroSlot(8-1);

				return true;
			}
		}
		break;

		case SDLK_9:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(9-1);
				else
					writeFromMacroSlot(9-1);

				return true;
			}
		}
		break;

		case SDLK_0:
		{
			if (keyb.leftAltPressed)
			{
				if (keyb.leftShiftPressed)
					writeToMacroSlot(10-1);
				else
					writeFromMacroSlot(10-1);

				return true;
			}
		}
		break;

		case SDLK_LEFT:
		{
			if (keyb.leftShiftPressed)
			{
				decSongPos();
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				pbEditPattDown();
				return true;
			}
			else if (keyb.leftAltPressed)
			{
				keybPattMarkLeft();
				return true;
			}
		}
		break;

		case SDLK_RIGHT:
		{
			if (keyb.leftShiftPressed)
			{
				incSongPos();
				return true;
			}
			else if (keyb.leftCtrlPressed)
			{
				pbEditPattUp();
				return true;
			}
			else if (keyb.leftAltPressed)
			{
				keybPattMarkRight();
				return true;
			}
		}
		break;
	}

	return false;
}
