/*****************************************************************************

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

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.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<assert.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include	"Drumpad.h"
#include	"file.h"
#include	"fnames.h"
#include	"gt_limit.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"log.h"
#include	"mixer.h"
#include	"mods_ct.h"
#include	"modstruc.h"
#include	"mpannel.h"
#include	"os.h"
#include	"patt.h"
#include	"Pattern.h"
#include	"PatternTools.h"
#include	"player.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"SoundDriver.h"
#include	"ssm_tool.h"
#include	"String.h"
#include	"tracks.h"
#include	"UndoCellList.h"
#include	"UndoCellPatNote.h"



/*\\\ CONSTANTES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

enum
{
	KEYB_KEY_TYPE_SCAN = 0,
	KEYB_KEY_TYPE_ASCII,
	KEYB_KEY_TYPE_MODIFIER,
	KEYB_KEY_TYPE_UNKNOWN
};

enum
{
	KEYB_PAD_INSTRUMENT = 0,
	KEYB_PAD_DRUM,

	KEYB_PAD_NBR_ELT
};



/*\\\ TYPES & STRUCTURES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

typedef struct
{
	const char	*name_0;
	LWORD		id;
	WORD		type;	// 0 = scan, 1 = ascii, 2 = modifieur
} KEYB_PREF_KEY_NAME;

typedef struct
{
	const char	*name_0;
	void		(*fnc_ptr) (int group_nbr, LWORD key_code, LWORD param);
} KEYB_PREF_FNC_NAME;



/*\\\ PROTOTYPES PRIVES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

signed int	KEYB_read_config_key (int index, const std::list <ConfigKey> &key_list);
signed int	KEYB_read_config_key_part (int index, int part, const std::list <ConfigKey> &key_list);
signed int	KEYB_read_group (const std::list <ConfigKey> &key_list);
signed int	KEYB_read_group_key (const std::list <ConfigKey> &key_list, KEYB_KEY_GROUP &group);
signed int	KEYB_read_group_section (const std::list <ConfigKey> &key_list, KEYB_KEY_GROUP &group);
signed int	KEYB_read_modifier_key_list (const std::list <ConfigKey> &key_list, LWORD &modif);
bool	KEYB_decode_key (const char *key_0, int &type, LWORD &code);

/*--------------------------------------------------------------------------*/
/*      Panneau principal                                                   */
/*--------------------------------------------------------------------------*/

void	KEYB_mp_cursor_left (void);
void	KEYB_mp_cursor_right (void);
void	KEYB_mp_prev_line (void);
void	KEYB_mp_next_line (void);
void	KEYB_mp_prev_instr (void);
void	KEYB_mp_next_instr (void);
void	KEYB_mp_cur_instr (void);
void	KEYB_mp_prev_sample (void);
void	KEYB_mp_next_sample (void);
void	KEYB_mp_prev_octave (void);
void	KEYB_mp_next_octave (void);
void	KEYB_mp_prev_column (void);
void	KEYB_mp_next_column (void);
void	KEYB_mp_prev_bar (void);
void	KEYB_mp_next_bar (void);
void	KEYB_mp_prev_songpos (void);
void	KEYB_mp_next_songpos (void);
void	KEYB_mp_prev_preset (void);
void	KEYB_mp_next_preset (void);
void	KEYB_mp_dec_step (void);
void	KEYB_mp_inc_step (void);
void	KEYB_mp_dec_height (void);
void	KEYB_mp_inc_height (void);
void	KEYB_mp_stop_edit (void);
void	KEYB_mp_play_song (void);
void	KEYB_mp_cont_song (void);
void	KEYB_mp_play_pattern (void);
void	KEYB_mp_cont_pattern (void);
void	KEYB_mp_del_note (void);
void	KEYB_mp_del_fx (void);
void	KEYB_mp_del_volume (void);
void	KEYB_mp_del_whole_note (void);
void	KEYB_mp_chg_pad_mode (void);
void	KEYB_mp_chg_display_type (void);
void	KEYB_mp_interpol_one_track (void);
void	KEYB_mp_interpol_all_tracks (void);
void	KEYB_mp_block_start (void);
void	KEYB_mp_block_end (void);
void	KEYB_mp_insert_line (int scope, bool rot_flag);
void	KEYB_mp_delete_line (int scope, bool rot_flag);
void	KEYB_mp_cut_block (void);
void	KEYB_mp_copy_block (void);
void	KEYB_mp_paste_block (void);
void	KEYB_mp_first_line (void);
void	KEYB_mp_last_line (void);
void	KEYB_mp_select_track_pattern (void);
void	KEYB_mp_select_top_left (void);
void	KEYB_mp_select_bottom_right (void);
void	KEYB_mp_quick_save (bool song_flag);
void	KEYB_mp_quick_load (bool song_flag);
void	KEYB_mp_new_module (void);
void	KEYB_mp_undo (void);
void	KEYB_mp_redo (void);
void	KEYB_mp_play_line (void);
void	KEYB_mp_play_context (void);
void	KEYB_mp_change_replay_freq (void);
void	KEYB_mp_del_field (void);

/*--------------------------------------------------------------------------*/
/*      Panneau principal, fonctions des groupes de touches                 */
/*--------------------------------------------------------------------------*/

void	KEYB_mp_select_instrument (int group_nbr, LWORD key_code, LWORD param);
void	KEYB_mp_jump_to_bar (int group_nbr, LWORD key_code, LWORD param);
void	KEYB_mp_jump_to_track (int group_nbr, LWORD key_code, LWORD param);
void	KEYB_mp_select_menu (int group_nbr, LWORD key_code, LWORD param);

void	KEYB_press_drumpad (int index);



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Touches correspondant a chaque fonction */
KEYB_KEY_FUNC	KEYB_mp_key_functions [] =
{
	{
		KEYB_FNC_PREV_LINE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_UP<<16,
		0, 0,
		0, "prev_line"
	},
	{
		KEYB_FNC_NEXT_LINE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_DOWN<<16,
		0, 0,
		0, "next_line"
	},
	{
		KEYB_FNC_CURS_LEFT,
		KEYB_SCAN_MASK, KEYB_SCANCODE_LEFT<<16,
		0, 0,
		0, "curs_left"
	},
	{
		KEYB_FNC_CURS_RIGHT,
		KEYB_SCAN_MASK, KEYB_SCANCODE_RIGHT<<16,
		0, 0,
		0, "curs_right"
	},
	{
		KEYB_FNC_PREV_INSTR,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_LEFT<<16 | KEYB_ALT,
		0, 0,
		0, "prev_instr"
	},
	{
		KEYB_FNC_NEXT_INSTR,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_RIGHT<<16 | KEYB_ALT,
		0, 0,
		0, "next_instr"
	},
	{
		KEYB_FNC_CUR_INSTR,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_PADPOINT<<16,
		0, 0,
		0, "cur_instr"
	},
	{
		KEYB_FNC_PREV_SAMPLE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_LEFT<<16 | KEYB_ALT | KEYB_SHIFT,
		0, 0,
		0, "prev_sample"
	},
	{
		KEYB_FNC_NEXT_SAMPLE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_RIGHT<<16 | KEYB_ALT | KEYB_SHIFT,
		0, 0,
		0, "next_sample"
	},
	{
		KEYB_FNC_PREV_OCTAVE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_ESC<<16 | KEYB_SHIFT,
		0, 0,
		0, "prev_octave"
	},
	{
		KEYB_FNC_NEXT_OCTAVE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_ESC<<16,
		0, 0,
		0, "next_octave"
	},
	{
		KEYB_FNC_PREV_COLUMN,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_TAB<<16 | KEYB_SHIFT,
		0, 0,
		0, "prev_column"
	},
	{
		KEYB_FNC_NEXT_COLUMN,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_TAB<<16,
		0, 0,
		0, "next_column"
	},
	{
		KEYB_FNC_PREV_BAR,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_UP<<16 | KEYB_SHIFT,
		KEYB_SCAN_MASK, KEYB_SCANCODE_PAGEUP<<16,
		0, "prev_bar"
	},
	{
		KEYB_FNC_NEXT_BAR,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_DOWN<<16 | KEYB_SHIFT,
		KEYB_SCAN_MASK, KEYB_SCANCODE_PAGEDOWN<<16,
		0, "next_bar"
	},
	{
		KEYB_FNC_PREV_SONGPOS,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_LEFT<<16 | KEYB_SHIFT,
		0, 0,
		0, "prev_songpos"
	},
	{
		KEYB_FNC_NEXT_SONGPOS,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_RIGHT<<16 | KEYB_SHIFT,
		0, 0,
		0, "next_songpos"
	},
	{
		KEYB_FNC_PREV_PRESET,
		KEYB_SCAN_MASK, KEYB_SCANCODE_LEFT<<16 | KEYB_CTRL,
		0, 0,
		0, "prev_preset"
	},
	{
		KEYB_FNC_NEXT_PRESET,
		KEYB_SCAN_MASK, KEYB_SCANCODE_RIGHT<<16 | KEYB_CTRL,
		0, 0,
		0, "next_preset"
	},
	{
		KEYB_FNC_DEC_STEP,
		KEYB_ASCII_MASK_2SH, 'G' | KEYB_CTRL | KEYB_SHIFT,
		0, 0,
		0, "dec_step"
	},
	{
		KEYB_FNC_INC_STEP,
		KEYB_ASCII_MASK_2SH, 'G' | KEYB_CTRL,
		0, 0,
		0, "inc_step"
	},
	{
		KEYB_FNC_STOP_EDIT,
		KEYB_SCAN_MASK, KEYB_SCANCODE_SPACE<<16,
		0, 0,
		0, "stop_edit"
	},
	{
		KEYB_FNC_PLAY_SONG,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_PAD0<<16,
		0, 0,
		0, "play_song"
	},
	{
		KEYB_FNC_CONT_SONG,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_PAD0<<16 | KEYB_CTRL,
		0, 0,
		0, "cont_song"
	},
	{
		KEYB_FNC_PLAY_PATTERN,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_PAD0<<16 | KEYB_SHIFT,
		0, 0,
		0, "play_pattern"
	},
	{
		KEYB_FNC_CONT_PATTERN,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_PAD0<<16 | KEYB_CTRL | KEYB_SHIFT,
		0, 0,
		0, "cont_pattern"
	},
	{
		KEYB_FNC_DEL_FIELD,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_BACKSPACE<<16,
		0, 0,
		0, "del_field"
	},
	{
		KEYB_FNC_DEL_NOTE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_BACKSPACE<<16 | KEYB_CTRL | KEYB_ALT,
		0, 0,
		0, "del_note"
	},
	{
		KEYB_FNC_DEL_FX,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_BACKSPACE<<16 | KEYB_SHIFT,
		0, 0,
		0, "del_command"
	},
	{
		KEYB_FNC_DEL_VOL,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_BACKSPACE<<16 | KEYB_ALT,
		0, 0,
		0, "del_vol"
	},
	{
		KEYB_FNC_DEL_W_NOTE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_BACKSPACE<<16 | KEYB_CTRL,
		0, 0,
		0, "del_whole_note"
	},
	{
		KEYB_FNC_CHG_PAD_MODE,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_FUNC + 4)<<16,
		0, 0,
		0, "change_pad_mode"
	},
	{
		KEYB_FNC_CHG_DISP_TYPE,
		KEYB_ASCII_MASK_2SH, 'D' | KEYB_CTRL,
		0, 0,
		0, "change_disp"
	},
	{
		KEYB_FNC_INTERPOL_ONE,
		KEYB_ASCII_MASK_2SH, 'I' | KEYB_CTRL,
		0, 0,
		0, "interpol_one"
	},
	{
		KEYB_FNC_INTERPOL_ALL,
		KEYB_ASCII_MASK_2SH, 'I' | KEYB_CTRL | KEYB_SHIFT,
		0, 0,
		0, "interpol_all"
	},
	{
		KEYB_FNC_BLOCK_START,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_FUNC+0)<<16,
		0, 0,
		0, "block_start"
	},
	{
		KEYB_FNC_BLOCK_END,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_FUNC+1)<<16,
		0, 0,
		0, "block_end"
	},
	{
		KEYB_FNC_INSERT_LINE_TRACK,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_INSERT)<<16 | KEYB_SHIFT,
		0, 0,
		0, "insert_line_track"
	},
	{
		KEYB_FNC_INSERT_LINE_PATTERN,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_INSERT)<<16 | KEYB_CTRL,
		0, 0,
		0, "insert_line_pattern"
	},
	{
		KEYB_FNC_INSERT_LINE_PRESET,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_INSERT)<<16 | KEYB_ALT,
		0, 0,
		0, "insert_line_preset"
	},
	{
		KEYB_FNC_DELETE_LINE_TRACK,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_DELETE)<<16 | KEYB_SHIFT,
		0, 0,
		0, "delete_line_track"
	},
	{
		KEYB_FNC_DELETE_LINE_PATTERN,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_DELETE)<<16 | KEYB_CTRL,
		0, 0,
		0, "delete_line_pattern"
	},
	{
		KEYB_FNC_DELETE_LINE_PRESET,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_DELETE)<<16 | KEYB_ALT,
		0, 0,
		0, "delete_line_preset"
	},
	{
		KEYB_FNC_CUT_BLOCK,
		KEYB_ASCII_MASK_2SH, 'X' | KEYB_CTRL,
		0, 0,
		0, "cut_block"
	},
	{
		KEYB_FNC_COPY_BLOCK,
		KEYB_ASCII_MASK_2SH, 'C' | KEYB_CTRL,
		0, 0,
		0, "copy_block"
	},
	{
		KEYB_FNC_PASTE_BLOCK,
		KEYB_ASCII_MASK_2SH, 'V' | KEYB_CTRL,
		0, 0,
		0, "paste_block"
	},
	{
		KEYB_FNC_FIRST_LINE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_CLRHOME<<16,
		0, 0,
		0, "first_line"
	},
	{
		KEYB_FNC_LAST_LINE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_END<<16,
		0, 0,
		0, "last_line"
	},
	{
		KEYB_FNC_SELECT_TRACK_PAT,
		KEYB_ASCII_MASK_2SH, 'L' | KEYB_CTRL,
		0, 0,
		0, "select_track_pat"
	},
	{
		KEYB_FNC_SELECT_TOP_LEFT,
		KEYB_ASCII_MASK_2SH, 'B' | KEYB_CTRL,
		0, 0,
		0, "select_top_left"
	},
	{
		KEYB_FNC_SELECT_BOT_RIGHT,
		KEYB_ASCII_MASK_2SH, 'E' | KEYB_CTRL,
		0, 0,
		0, "select_bot_right"
	},
	{
		KEYB_FNC_QUICK_SAVE_MOD,
		KEYB_ASCII_MASK_2SH, 'S' | KEYB_CTRL,
		0, 0,
		0, "quick_save_mod"
	},
	{
		KEYB_FNC_QUICK_SAVE_SONG,
		KEYB_ASCII_MASK_2SH, 'S' | KEYB_CTRL | KEYB_SHIFT,
		0, 0,
		0, "quick_save_song"
	},
	{
		KEYB_FNC_QUICK_LOAD_MOD,
		KEYB_ASCII_MASK_2SH, 'O' | KEYB_CTRL,
		0, 0,
		0, "quick_load_mod"
	},
	{
		KEYB_FNC_QUICK_LOAD_SONG,
		KEYB_ASCII_MASK_2SH, 'O' | KEYB_CTRL | KEYB_SHIFT,
		0, 0,
		0, "quick_load_song"
	},
	{
		KEYB_FNC_NEW_MOD,
		KEYB_ASCII_MASK_2SH, 'N' | KEYB_CTRL,
		0, 0,
		0, "new_mod"
	},
	{
		KEYB_FNC_UNDO,
		KEYB_ASCII_MASK_2SH, 'Z' | KEYB_CTRL,
		0, 0,
		0, "undo"
	},
	{
		KEYB_FNC_REDO,
		KEYB_ASCII_MASK_2SH, 'Y' | KEYB_CTRL,
		0, 0,
		0, "redo"
	},
	{
		KEYB_FNC_PLAY_LINE,
		KEYB_SCAN_MASK_2SH, KEYB_SCANCODE_RETURN<<16,
		0, 0,
		0, "play_line"
	},
	{
		KEYB_FNC_PLAY_CONTEXT,
		KEYB_SCAN_MASK_2SH, (KEYB_SCANCODE_RETURN<<16) | KEYB_SHIFT,
		0, 0,
		0, "play_context"
	},
	{
		KEYB_FNC_CHANGE_REPLAY_FREQ,
		KEYB_ASCII_MASK_2SH, 'R' | KEYB_CTRL,
		0, 0,
		0, "change_replay_freq"
	},

	{ KEYB_FNC_END, 0, 0, 0, 0, 0 }		/* Fin des donnees */
};

/* Groupes de touches */
KEYB_KEY_GROUP	KEYB_key_group [KEYB_NBR_GROUPS] =
{
	/* Touches de fonction F9 - F12 */
	{
		4, 1,
		{
			{
				KEYB_mp_jump_to_bar,
				KEYB_PURE_SCAN_MASK,
				0
			},
		},
		{
			{ (KEYB_SCANCODE_FUNC +  8) << 16, 0 },
			{ (KEYB_SCANCODE_FUNC +  9) << 16, 1 },
			{ (KEYB_SCANCODE_FUNC + 10) << 16, 2 },
			{ (KEYB_SCANCODE_FUNC + 11) << 16, 3 }
		}
	},

	/* Pave numerique. Different sur Falcon et PC */
	{
	#if defined (MACHINE_TYPE_ATARI)
		16, 1,
		{
			{
				KEYB_mp_select_instrument,
				KEYB_PURE_SCAN_MASK,
				0
			}
		},
		{
			{ KEYB_SCANCODE_PADOPEN << 16,   0 },
			{ KEYB_SCANCODE_PADCLOSED << 16, 1 },
			{ KEYB_SCANCODE_PADSLASH << 16,  2 },
			{ KEYB_SCANCODE_PADSTAR << 16,   3 },
			{ KEYB_SCANCODE_PAD7 << 16,      4 },
			{ KEYB_SCANCODE_PAD8 << 16,      5 },
			{ KEYB_SCANCODE_PAD9 << 16,      6 },
			{ KEYB_SCANCODE_PADMINUS << 16,  7 },
			{ KEYB_SCANCODE_PAD4 << 16,      8 },
			{ KEYB_SCANCODE_PAD5 << 16,      9 },
			{ KEYB_SCANCODE_PAD6 << 16,     10 },
			{ KEYB_SCANCODE_PADPLUS << 16,  11 },
			{ KEYB_SCANCODE_PAD1 << 16,     12 },
			{ KEYB_SCANCODE_PAD2 << 16,     13 },
			{ KEYB_SCANCODE_PAD3 << 16,     14 },
			{ KEYB_SCANCODE_PADENTER << 16, 15 }
		},
	#elif defined (MACHINE_TYPE_PC)
		15, 1,
		{
			{
				KEYB_mp_select_instrument,
				KEYB_PURE_SCAN_MASK,
				0
			}
		},
		{
			{ KEYB_SCANCODE_NUMLOCK << 16,    0 },
			{ KEYB_SCANCODE_PADSLASH << 16,   1 },
			{ KEYB_SCANCODE_PADSTAR << 16,    2 },
			{ KEYB_SCANCODE_PADMINUS << 16,   3 },
			{ KEYB_SCANCODE_PAD7 << 16,       4 },
			{ KEYB_SCANCODE_PAD8 << 16,       5 },
			{ KEYB_SCANCODE_PAD9 << 16,       6 },
			{ KEYB_SCANCODE_PADPLUS << 16,    7 },
			{ KEYB_SCANCODE_PAD4 << 16,       8 },
			{ KEYB_SCANCODE_PAD5 << 16,       9 },
			{ KEYB_SCANCODE_PAD6 << 16,      10 },
			{ KEYB_SCANCODE_PAD1 << 16,      11 },
			{ KEYB_SCANCODE_PAD2 << 16,      12 },
			{ KEYB_SCANCODE_PAD3 << 16,      13 },
			{ KEYB_SCANCODE_PADENTER << 16,  14 }
		}
	#else
		0, 0
	#endif
	},

	/* Sauts vers les pistes */
	{
		20, 1,
		{
			{
				KEYB_mp_jump_to_track,
				KEYB_PURE_SCAN_MASK | KEYB_ALT,
				KEYB_ALT
			}
		},
		{
			{ (KEYB_SCANCODE_ROW2 + 0) << 16,  0 },
			{ (KEYB_SCANCODE_ROW2 + 1) << 16,  1 },
			{ (KEYB_SCANCODE_ROW2 + 2) << 16,  2 },
			{ (KEYB_SCANCODE_ROW2 + 3) << 16,  3 },
			{ (KEYB_SCANCODE_ROW2 + 4) << 16,  4 },
			{ (KEYB_SCANCODE_ROW2 + 5) << 16,  5 },
			{ (KEYB_SCANCODE_ROW2 + 6) << 16,  6 },
			{ (KEYB_SCANCODE_ROW2 + 7) << 16,  7 },
			{ (KEYB_SCANCODE_ROW2 + 8) << 16,  8 },
			{ (KEYB_SCANCODE_ROW2 + 9) << 16,  9 },
			{ (KEYB_SCANCODE_ROW3 + 0) << 16, 10 },
			{ (KEYB_SCANCODE_ROW3 + 1) << 16, 11 },
			{ (KEYB_SCANCODE_ROW3 + 2) << 16, 12 },
			{ (KEYB_SCANCODE_ROW3 + 3) << 16, 13 },
			{ (KEYB_SCANCODE_ROW3 + 4) << 16, 14 },
			{ (KEYB_SCANCODE_ROW3 + 5) << 16, 15 },
			{ (KEYB_SCANCODE_ROW3 + 6) << 16, 16 },
			{ (KEYB_SCANCODE_ROW3 + 7) << 16, 17 },
			{ (KEYB_SCANCODE_ROW3 + 8) << 16, 18 },
			{ (KEYB_SCANCODE_ROW3 + 9) << 16, 19 },
		}
	},

	/* Selection des sous-menus */
	{
		8, 1,
		{
			{
				KEYB_mp_select_menu,
				KEYB_PURE_SCAN_MASK | KEYB_CTRL | KEYB_SHIFT | KEYB_ALT,
				KEYB_CTRL
			}
		},
		{
			{ (KEYB_SCANCODE_ROW1 + 0) << 16, 0 },
			{ (KEYB_SCANCODE_ROW1 + 1) << 16, 1 },
			{ (KEYB_SCANCODE_ROW1 + 2) << 16, 2 },
			{ (KEYB_SCANCODE_ROW1 + 3) << 16, 3 },
			{ (KEYB_SCANCODE_ROW1 + 4) << 16, 4 },
			{ (KEYB_SCANCODE_ROW1 + 5) << 16, 5 },
			{ (KEYB_SCANCODE_ROW1 + 6) << 16, 6 },
			{ (KEYB_SCANCODE_ROW1 + 7) << 16, 7 },
			{ (KEYB_SCANCODE_ROW1 + 8) << 16, 8 }
		}
	}
};



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

Drumpad	KEYB_drumpad;
int	KEYB_pad_mode = KEYB_PAD_INSTRUMENT;

const char *	KEYB_pad_mode_name [KEYB_PAD_NBR_ELT] =
{
	"Instr", "Drum"
};


/* Touches associees a chaque note des 2 rangees. Ne sert pas directement,
	mais permet de generer la table KEYB_key_2_note.                        */
int	KEYB_note_2_key [2] [22] =
{
	/* Octave -1: rangee du bas */
	{
		KEYB_SCANCODE_ROW4 + 1, KEYB_SCANCODE_ROW3 + 1, KEYB_SCANCODE_ROW4 + 2,
		KEYB_SCANCODE_ROW3 + 2, KEYB_SCANCODE_ROW4 + 3, KEYB_SCANCODE_ROW4 + 4,
		KEYB_SCANCODE_ROW3 + 4, KEYB_SCANCODE_ROW4 + 5, KEYB_SCANCODE_ROW3 + 5,
		KEYB_SCANCODE_ROW4 + 6, KEYB_SCANCODE_ROW3 + 6, KEYB_SCANCODE_ROW4 + 7, 

		KEYB_SCANCODE_ROW4 + 8, KEYB_SCANCODE_ROW3 + 8, KEYB_SCANCODE_ROW4 + 9,
		KEYB_SCANCODE_ROW3 + 9, KEYB_SCANCODE_ROW4 + 10,
		
		0
	},
	/* Octave 0: rangee du haut */
	{
		KEYB_SCANCODE_ROW2 + 0, KEYB_SCANCODE_ROW1 + 1, KEYB_SCANCODE_ROW2 + 1,
		KEYB_SCANCODE_ROW1 + 2, KEYB_SCANCODE_ROW2 + 2, KEYB_SCANCODE_ROW2 + 3,
		KEYB_SCANCODE_ROW1 + 4, KEYB_SCANCODE_ROW2 + 4, KEYB_SCANCODE_ROW1 + 5,
		KEYB_SCANCODE_ROW2 + 5, KEYB_SCANCODE_ROW1 + 6, KEYB_SCANCODE_ROW2 + 6, 

		KEYB_SCANCODE_ROW2 + 7, KEYB_SCANCODE_ROW1 + 8, KEYB_SCANCODE_ROW2 + 8,
		KEYB_SCANCODE_ROW1 + 9, KEYB_SCANCODE_ROW2 + 9, KEYB_SCANCODE_ROW2 + 10,
		KEYB_SCANCODE_ROW1 + 11, KEYB_SCANCODE_ROW2 + 11,
#if defined (MACHINE_TYPE_ATARI)
		KEYB_SCANCODE_ROW1 + 12,	/* Cette touche n'existe que sur les claviers Atari */
#endif
		0
	}
};
signed int	KEYB_key_2_note [KEYB_SCANCODE_NBR_KEYS];		/* Notes associees a chaque touche. -1 si aucune note. A initialiser */

/* Nom de chaque touche speciale */
KEYB_PREF_KEY_NAME	KEYB_pref_key_name [] =
{
	/* Touches scan */
	{ "esc",			KEYB_SCANCODE_ESC << 16,			KEYB_KEY_TYPE_SCAN },
	{ "backspace",	KEYB_SCANCODE_BACKSPACE << 16,	KEYB_KEY_TYPE_SCAN },
	{ "tab",			KEYB_SCANCODE_TAB << 16,			KEYB_KEY_TYPE_SCAN },
	{ "return",		KEYB_SCANCODE_RETURN << 16,		KEYB_KEY_TYPE_SCAN },
	{ "backquote",	KEYB_SCANCODE_BCKQUOTE << 16,		KEYB_KEY_TYPE_SCAN },
	{ "space",		KEYB_SCANCODE_SPACE << 16,			KEYB_KEY_TYPE_SCAN },
	{ "home",		KEYB_SCANCODE_CLRHOME << 16,		KEYB_KEY_TYPE_SCAN },
	{ "up",			KEYB_SCANCODE_UP << 16,				KEYB_KEY_TYPE_SCAN },
	{ "pageup",		KEYB_SCANCODE_PAGEUP << 16,		KEYB_KEY_TYPE_SCAN },
	{ "padminus",	KEYB_SCANCODE_PADMINUS << 16,		KEYB_KEY_TYPE_SCAN },
	{ "left",		KEYB_SCANCODE_LEFT << 16,			KEYB_KEY_TYPE_SCAN },
	{ "right",		KEYB_SCANCODE_RIGHT << 16,			KEYB_KEY_TYPE_SCAN },
	{ "padplus",	KEYB_SCANCODE_PADPLUS << 16,		KEYB_KEY_TYPE_SCAN },
	{ "end",			KEYB_SCANCODE_END << 16,			KEYB_KEY_TYPE_SCAN },
	{ "down",		KEYB_SCANCODE_DOWN << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pagedown",	KEYB_SCANCODE_PAGEDOWN << 16,		KEYB_KEY_TYPE_SCAN },
	{ "insert",		KEYB_SCANCODE_INSERT << 16,		KEYB_KEY_TYPE_SCAN },
	{ "del",			KEYB_SCANCODE_DELETE << 16,		KEYB_KEY_TYPE_SCAN },
	{ "numlock",	KEYB_SCANCODE_NUMLOCK << 16,		KEYB_KEY_TYPE_SCAN },
	{ "padslash",	KEYB_SCANCODE_PADSLASH << 16,		KEYB_KEY_TYPE_SCAN },
	{ "padstar",	KEYB_SCANCODE_PADSTAR << 16,		KEYB_KEY_TYPE_SCAN },
	{ "pad7",		KEYB_SCANCODE_PAD7 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad8",		KEYB_SCANCODE_PAD8 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad9",		KEYB_SCANCODE_PAD9 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad1",		KEYB_SCANCODE_PAD1 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad2",		KEYB_SCANCODE_PAD2 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad3",		KEYB_SCANCODE_PAD3 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad0",		KEYB_SCANCODE_PAD0 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "padpoint",	KEYB_SCANCODE_PADPOINT << 16,		KEYB_KEY_TYPE_SCAN },
	{ "padenter",	KEYB_SCANCODE_PADENTER << 16,		KEYB_KEY_TYPE_SCAN },
	{ "pad4",		KEYB_SCANCODE_PAD4 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad5",		KEYB_SCANCODE_PAD5 << 16,			KEYB_KEY_TYPE_SCAN },
	{ "pad6",		KEYB_SCANCODE_PAD6 << 16,			KEYB_KEY_TYPE_SCAN },

	/* Touches ASCII */
	{ "l_bracket",	'{', KEYB_KEY_TYPE_ASCII },
	{ "r_bracket",	'}', KEYB_KEY_TYPE_ASCII },
	{ "quotes",		'"', KEYB_KEY_TYPE_ASCII },
	{ "comma",		',', KEYB_KEY_TYPE_ASCII },

	/* Modificateurs */
	{ "shift",			KEYB_SHIFT,			KEYB_KEY_TYPE_MODIFIER },
	{ "ctrl",			KEYB_CTRL,			KEYB_KEY_TYPE_MODIFIER },
	{ "alt",				KEYB_ALT,			KEYB_KEY_TYPE_MODIFIER },
	{ "capslock",		KEYB_CAPSLOCK,		KEYB_KEY_TYPE_MODIFIER },
	{ "scrolllock",	KEYB_SCROLLLOCK,	KEYB_KEY_TYPE_MODIFIER },
	
	{ NULL, 0, 0 }
};

/* Nom des fonctions de groupes */
KEYB_PREF_FNC_NAME	KEYB_pref_fnc_name [] =
{
	{ "select_instr",		KEYB_mp_select_instrument },
	{ "jump_to_bar",		KEYB_mp_jump_to_bar },
	{ "jump_to_track",	KEYB_mp_jump_to_track },
	{ "select_menu",		KEYB_mp_select_menu },

	{ NULL, NULL }
};



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: KEYB_flush_keyboard_buffer                                     */
/*      Description: Vide le buffer du clavier.                             */
/*==========================================================================*/

void	KEYB_flush_keyboard_buffer (void)
{
	while (OS_key_pressed ())
	{
		OS_get_key ();
	}
}



/*==========================================================================*/
/*      Nom: KEYB_get_key_group                                             */
/*      Description: Cherche si une touche donnee appartient a un groupe,   */
/*                   complete le code de touche et donne le parametre.      */
/*      Parametres en sortie:                                               */
/*        - param_ptr: pointeur sur le parametre.                           */
/*      Parametres en entree/sortie:                                        */
/*        - key_ptr: pointeur sur le code de la touche.                     */
/*      Retour: true si on a trouve un groupe.                              */
/*==========================================================================*/

bool	KEYB_get_key_group (LWORD *key_ptr, LWORD *param_ptr)
{
	int		scan;
	int		group;
	int		key;
	int		section;
	LWORD		masked;
	LWORD		modifier;

	scan = ((*key_ptr) >> 16) & 0xFF;

	for (group = 0; group < KEYB_NBR_GROUPS; group ++)
	{
		for (section = 0; section < KEYB_key_group [group].nbr_sections; section ++)
		{
			masked = (*key_ptr) & KEYB_key_group [group].section [section].mask;
			modifier = KEYB_key_group [group].section [section].modifier;
			for (key = 0; key < KEYB_key_group [group].nbr_keys; key ++)
			{
				if (masked == (KEYB_key_group [group].key [key].code | modifier))
				{
					*key_ptr |= (0x400L * group) | (0x4000L * section );
					*param_ptr = KEYB_key_group [group].key [key].parameter;
					return (true);
				}
			}
		}
	}

	return (false);
}



void	KEYB_print_pad_mode (char *txt_0)
{
	assert (txt_0 != 0);
	strcpy (txt_0, KEYB_pad_mode_name [KEYB_pad_mode]);
}



/****************************************************************************/
/*                                                                          */
/*      INITIALISATIONS                                                     */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: KEYB_init_keyboard                                             */
/*      Description: Initialise le clavier et ses variables.                */
/*      Retour: 0 si aucun probleme                                         */
/*==========================================================================*/

signed int	KEYB_init_keyboard (void)
{
	int		key;
	int		note;
	int		octave;

	/* Initialise le tableau de conversion touche -> note */
	for (key = 0; key < KEYB_SCANCODE_NBR_KEYS; key ++)
	{
		KEYB_key_2_note [key] = -1;
	}
	for (octave = 0; octave < 2; octave ++)
	{
		note = 0;
		for ( ; ; )
		{
			key = KEYB_note_2_key [octave] [note];
			if (key <= 0)
			{
				break;
			}
			KEYB_key_2_note [key] = octave * 12 + note;
			note ++;
		}
	}

	return (0);
}



void	KEYB_restore_keyboard (void)
{
	/* Rien a faire... */
}



signed int	KEYB_read_config_list (const std::list <ConfigKey> &key_list)
{
	int		index;
	bool		found_flag;

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();

		/* Cle anonyme: interdit */
		if (BASE_compare_string (key_name.c_str (), "") == 0)
		{
			return (-1);
		}

		/* Groupes de touches */
		if (BASE_compare_string (key_name.c_str (), "group") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("KEYB_read_config_list: Warning: (in \"keyboard\") Argument of group must be a list.\n");
			}
			else if (KEYB_read_group (key_val.get_list ()))
			{
				return (-1);
			}
		}

		/* Touches simples */
		else
		{
			index = 0;
			found_flag = false;
			while (KEYB_mp_key_functions [index].function > KEYB_FNC_END)
			{
				if (BASE_compare_string (key_name.c_str (), KEYB_mp_key_functions [index].pref_name_0) == 0)
				{
					if (key_val.get_type () != ConfigKeyValue_Type_LIST)
					{
						LOG_printf ("KEYB_read_config_list: Warning: Argument of function \"%s\" must be a list. Key is ignored.\n",
						            key_name.c_str ());
					}

					else if (KEYB_read_config_key (index, key_val.get_list ()))
					{
						return (-1);
					}

					found_flag = true;
					break;
				}

				index ++;
			}

			/* Cle inconnue */
			if (! found_flag)
			{
				LOG_printf ("KEYB_read_config_list: Warning: unknown key \"%s\".\n",
				            key_name.c_str ());
			}
		}
	}

	return (0);
}



signed int	KEYB_read_config_key (int index, const std::list <ConfigKey> &key_list)
{
	int		part = 0;
	String	key_name;

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		if (part >= 2)
		{
			LOG_printf ("KEYB_read_config_key: Warning: Max two lists of keys (function \"%s\").\n",
			            key_name.c_str ());
			break;
		}

		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();

		/* Cle nommee: interdit */
		if (BASE_compare_string (key_name.c_str (), "") != 0)
		{
			return (-1);
		}

		if (KEYB_read_config_key_part (index, part, key_val.get_list ()))
		{
			return (-1);
		}
	}

	return (0);
}



signed int	KEYB_read_config_key_part (int index, int part, const std::list <ConfigKey> &key_list)
{
	LWORD		mask = 0;
	LWORD		val = 0;
	LWORD		code;
	bool		main_key_flag = false;

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();

		/* Decode le nom de la touche */
		int			type;
		if (KEYB_decode_key (key_val.get_string ().c_str (), type, code))
		{
			if (type == KEYB_KEY_TYPE_SCAN)
			{
				mask = KEYB_SCAN_MASK_2SH;
				main_key_flag = true;
			}
			else if (type == KEYB_KEY_TYPE_ASCII)
			{
				mask = KEYB_ASCII_MASK_2SH;
				main_key_flag = true;
			}
			val |= code;
		}

		else
		{
			LOG_printf ("KEYB_read_config_key_part: Warning: syntax error in part %d of function \"%s\".\n",
			            part + 1, KEYB_mp_key_functions [index].pref_name_0);
		}
	}

	if (! main_key_flag)
	{
		LOG_printf ("KEYB_read_config_key_part: Warning: No main key in part %d of function \"%s\".\n",
		            part + 1, KEYB_mp_key_functions [index].pref_name_0);
		mask = 0;
		val = 0;
	}

	if (part == 1)
	{
		KEYB_mp_key_functions [index].mask2 = mask;
		KEYB_mp_key_functions [index].val2 = val;
	}
	else
	{
		KEYB_mp_key_functions [index].mask1 = mask;
		KEYB_mp_key_functions [index].val1 = val;
	}

	return (0);
}



signed int	KEYB_read_group (const std::list <ConfigKey> &key_list)
{
	int		id = 0;
	bool		id_flag = false;
	KEYB_KEY_GROUP	group = { 0, 0 };

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();

		/* Id */
		if (BASE_compare_string (key_name.c_str (), "id") == 0)
		{
			id = atoi (key_val.get_string ().c_str ());
			if (id >= KEYB_NBR_GROUPS)
			{
				LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group/id\") group id too high (max %d).\n",
				            (int) KEYB_NBR_GROUPS);
			}
			else
			{
				id_flag = true;
			}
		}

		/* Key */
		else if (BASE_compare_string (key_name.c_str (), "key") == 0)
		{
			if (group.nbr_keys >= KEYB_NBR_KEYS_MAX_GROUP)
			{
				LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group\") too much keys in group.\n");
			}
			else if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group\") key must be in a list.\n");
			}
			else
			{
				KEYB_read_group_key (key_val.get_list (), group);
			}
		}

		/* Section */
		else if (BASE_compare_string (key_name.c_str (), "section") == 0)
		{
			if (group.nbr_sections >= KEYB_MAX_SECTIONS)
			{
				LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group\") too much sections in group.\n");
			}
			else if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group\") section argument must be a list.\n");
			}
			else
			{
				KEYB_read_group_section (key_val.get_list (), group);
			}
		}

		else
		{
			LOG_printf ("KEYB_read_group: Warning: syntax error \"%s\".\n",
			            key_name.c_str ());
		}
	}

	if (id_flag)
	{
		KEYB_key_group [id] = group;
	}

	else
	{
		LOG_printf ("KEYB_read_group: Warning: (in \"keyboard/group\") Id needed for group.\n");
	}

	return (0);
}



signed int	KEYB_read_group_key (const std::list <ConfigKey> &key_list, KEYB_KEY_GROUP &group)
{
	if (key_list.size () != 2)
	{
		LOG_printf ("KEYB_read_group_key: Warning: (in \"keyboard/group/key\") List must have only 2 elements.\n");
	}

	else
	{
		std::list <ConfigKey>::const_iterator	it = key_list.begin ();

		String			val = it->get_string ();
		int				type;
		LWORD				code;
		if (! KEYB_decode_key (val.c_str (), type, code))
		{
			LOG_printf ("KEYB_read_group_key: Warning: (in \"keyboard/group/key\") Unknown key \"%s\".\n",
			            val.c_str ());
		}
		else if (type != KEYB_KEY_TYPE_SCAN)
		{
			LOG_printf ("KEYB_read_group_key: Warning: (in \"keyboard/group/key\") Only Scan codes allowed, no Ascii or modifiers.\n");
		}
		else
		{
			++ it;
			group.key [group.nbr_keys].parameter = atoi (it->get_string ().c_str ());
			group.key [group.nbr_keys].code = code;
			group.nbr_keys ++;
		}
	}

	return (0);
}



signed int	KEYB_read_group_section (const std::list <ConfigKey> &key_list, KEYB_KEY_GROUP &group)
{
	bool		found_flag;
	bool		fnc_flag = false;
	int		fnc_cnt;

	group.section [group.nbr_sections].mask = KEYB_PURE_SCAN_MASK;
	group.section [group.nbr_sections].modifier = 0;
	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();
		const String	val = key_val.get_string ();

		/* fnc */
		if (BASE_compare_string (key_name.c_str (), "fnc") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_STRING)
			{
				LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section/fnc\") Argument must be a single value.\n");
			}

			else
			{
				fnc_cnt = 0;
				found_flag = false;
				while (KEYB_pref_fnc_name [fnc_cnt].fnc_ptr != NULL)
				{
					if (BASE_compare_string (val.c_str (), KEYB_pref_fnc_name [fnc_cnt].name_0) == 0)
					{
						group.section [group.nbr_sections].fnc_ptr = KEYB_pref_fnc_name [fnc_cnt].fnc_ptr;
						found_flag = true;
						fnc_flag = true;
					}
					fnc_cnt ++;
				}

				if (! found_flag)
				{
					LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section/fnc\") Syntax error \"%s\".\n",
					            val.c_str ());
				}
			}
		}

		else if (BASE_compare_string (key_name.c_str (), "limitation") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section/limitation\") Argument must be a list.\n");
			}
			else
			{
				KEYB_read_modifier_key_list (key_val.get_list (), group.section [group.nbr_sections].mask);
			}
		}


		else if (BASE_compare_string (key_name.c_str (), "obligatory") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section/obligatory\") Argument must be a list.\n");
			}
			else
			{
				KEYB_read_modifier_key_list (key_val.get_list (), group.section [group.nbr_sections].modifier);
			}
		}

		else
		{
			LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section\") Syntax error \"%s\".\n",
			            key_name.c_str ());
		}
	}

	/* Valide le groupe */
	if (fnc_flag)
	{
		group.nbr_sections ++;
	}
	else
	{
		LOG_printf ("KEYB_read_group_section: Warning: (in \"keyboard/group/section\") fnc needed.\n");
	}

	return (0);
}



signed int	KEYB_read_modifier_key_list (const std::list <ConfigKey> &key_list, LWORD &modif)
{

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();
		const String	val = key_val.get_string ();

		int				type;
		LWORD				code;
		if (! KEYB_decode_key (val.c_str (), type, code))
		{
			LOG_printf ("KEYB_read_modifier_key_list: Warning: Unknown key \"%s\".\n",
			            val.c_str ());
		}

		else if (type != KEYB_KEY_TYPE_MODIFIER)
		{
			LOG_printf ("KEYB_read_modifier_key_list: Warning: \"%s\" isn't a modifier.\n",
			            val.c_str ());
		}

		else
		{
			modif |= code;
		}
	}

	return (0);
}



bool	KEYB_decode_key (const char *key_0, int &type, LWORD &code)
{
	code = 0;
	type = KEYB_KEY_TYPE_UNKNOWN;

	if (strlen (key_0) <= 0)
	{
		return (false);
	}

	/* Touches de fonction */
	if (   toupper (key_0 [0]) == 'F'
	    && atoi (key_0 + 1) > 0)
	{
		code = (LWORD)(KEYB_SCANCODE_FUNC + atoi (key_0 + 1) - 1) << 16;
		type = KEYB_KEY_TYPE_SCAN;
		return (true);
	}

	/* Touches de lignes */
	if (BASE_search_in_string_nocase ("row", key_0) == 0)
	{
		if (strlen (key_0) >= 5)
		{
			int				scan = KEYB_SCANCODE_ROW1;
			switch (key_0 [3])
			{
			case	'2':
				scan = KEYB_SCANCODE_ROW2;
				break;
			case	'3':
				scan = KEYB_SCANCODE_ROW3;
				break;
			case	'4':
				scan = KEYB_SCANCODE_ROW4;
				break;
			}
			scan += atoi (key_0 + 5);
			code = (LWORD)scan << 16;
			type = KEYB_KEY_TYPE_SCAN;
			return (true);
		}

		return (false);
	}

	/* Autres touches */
	int				key_index = 0;
	while (KEYB_pref_key_name [key_index].name_0 != NULL)
	{
		if (BASE_compare_string (key_0, KEYB_pref_key_name [key_index].name_0) == 0)
		{
			type = KEYB_pref_key_name [key_index].type;
			code = KEYB_pref_key_name [key_index].id;
			return (true);
		}

		key_index ++;
	}

	/* Simples touches ascii */
	if (strlen (key_0) != 1)
	{
		return (false);
	}

	code = toupper (key_0 [0]);
	type = KEYB_KEY_TYPE_ASCII;

	return (true);
}



/****************************************************************************/
/*                                                                          */
/*      PANNEAU PRINCIPAL                                                   */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: KEYB_mp_keyboard                                               */
/*      Description: Gere les touches du panneau principal.                 */
/*==========================================================================*/

void	KEYB_mp_keyboard (void)
{
	int		group_cnt;
	LWORD		key_code;
	LWORD		parameter;
	LWORD		mask_val;
	LWORD		test_val;
	int		function;
	int		section;
	int		key_code_cnt;
	bool		found_flag;
	bool		group_flag;

	if (!OS_key_pressed ())
	{
		return;
	}

	key_code = OS_get_key ();
	group_flag = KEYB_get_key_group (&key_code, &parameter);
	found_flag = false;
	key_code_cnt = 0;

	/* Conversion en majuscules */
	if (   (key_code & 0xFF) >= 'a'
		 && (key_code & 0xFF) <= 'z')
	{
		key_code -= 'a' - 'A';
	}

	while (KEYB_mp_key_functions [key_code_cnt].function != KEYB_FNC_END)
	{
		/* Premiere touche */
		mask_val = KEYB_mp_key_functions [key_code_cnt].mask1;
		if (mask_val != 0)
		{
			test_val = KEYB_mp_key_functions [key_code_cnt].val1;
			if ((key_code & mask_val) == test_val)
			{
				function = KEYB_mp_key_functions [key_code_cnt].function;
				found_flag = true;
				break;
			}
		}

		/* Deuxieme touche */
		mask_val = KEYB_mp_key_functions [key_code_cnt].mask2;
		if (mask_val != 0)
		{
			test_val = KEYB_mp_key_functions [key_code_cnt].val2;
			if ((key_code & mask_val) == test_val)
			{
				function = KEYB_mp_key_functions [key_code_cnt].function;
				found_flag = true;
				break;
			}
		}

		key_code_cnt ++;
	}

	/* Valide le groupe s'il y en a un */
	if (group_flag)
	{
		group_flag = false;
		group_cnt = (key_code & KEYB_GROUP_MASK) >> 10;
		section = (key_code & KEYB_SECTION_MASK) >> 14;
		if (KEYB_key_group [group_cnt].section [section].fnc_ptr != NULL)
		{
			group_flag = true;
		}
	}

	/* Est-ce une fonction ? */
	if (found_flag)
	{
		switch (function)
		{
		case	KEYB_FNC_PREV_LINE:				KEYB_mp_prev_line ();				break;
		case	KEYB_FNC_NEXT_LINE:				KEYB_mp_next_line ();				break;
		case	KEYB_FNC_CURS_LEFT:				KEYB_mp_cursor_left ();				break;
		case	KEYB_FNC_CURS_RIGHT:				KEYB_mp_cursor_right ();			break;
		case	KEYB_FNC_PREV_INSTR:				KEYB_mp_prev_instr ();				break;
		case	KEYB_FNC_NEXT_INSTR:				KEYB_mp_next_instr ();				break;
		case	KEYB_FNC_CUR_INSTR:				KEYB_mp_cur_instr ();				break;
		case	KEYB_FNC_PREV_SAMPLE:			KEYB_mp_prev_sample ();				break;
		case	KEYB_FNC_NEXT_SAMPLE:			KEYB_mp_next_sample ();				break;
		case	KEYB_FNC_PREV_COLUMN:			KEYB_mp_prev_column ();				break;
		case	KEYB_FNC_NEXT_COLUMN:			KEYB_mp_next_column ();				break;
		case	KEYB_FNC_PREV_BAR:				KEYB_mp_prev_bar ();					break;
		case	KEYB_FNC_NEXT_BAR:				KEYB_mp_next_bar ();					break;
		case	KEYB_FNC_PREV_OCTAVE:			KEYB_mp_prev_octave ();				break;
		case	KEYB_FNC_NEXT_OCTAVE:			KEYB_mp_next_octave ();				break;
		case	KEYB_FNC_PREV_SONGPOS:			KEYB_mp_prev_songpos ();			break;
		case	KEYB_FNC_NEXT_SONGPOS:			KEYB_mp_next_songpos ();			break;
		case	KEYB_FNC_PREV_PRESET:			KEYB_mp_prev_preset ();				break;
		case	KEYB_FNC_NEXT_PRESET:			KEYB_mp_next_preset ();				break;
		case	KEYB_FNC_DEC_STEP:				KEYB_mp_dec_step ();					break;
		case	KEYB_FNC_INC_STEP:				KEYB_mp_inc_step ();					break;
		case	KEYB_FNC_STOP_EDIT:				KEYB_mp_stop_edit ();				break;
		case	KEYB_FNC_PLAY_SONG:				KEYB_mp_play_song ();				break;
		case	KEYB_FNC_CONT_SONG:				KEYB_mp_cont_song ();				break;
		case	KEYB_FNC_PLAY_PATTERN:			KEYB_mp_play_pattern ();			break;
		case	KEYB_FNC_CONT_PATTERN:			KEYB_mp_cont_pattern ();			break;
		case	KEYB_FNC_DEL_FIELD:				KEYB_mp_del_field ();				break;
		case	KEYB_FNC_DEL_NOTE:				KEYB_mp_del_note ();					break;
		case	KEYB_FNC_DEL_FX:					KEYB_mp_del_fx ();					break;
		case	KEYB_FNC_DEL_VOL:					KEYB_mp_del_volume ();				break;
		case	KEYB_FNC_DEL_W_NOTE:				KEYB_mp_del_whole_note ();			break;
		case	KEYB_FNC_CHG_PAD_MODE:			KEYB_mp_chg_pad_mode ();			break;
		case	KEYB_FNC_CHG_DISP_TYPE:			KEYB_mp_chg_display_type ();		break;
		case	KEYB_FNC_INTERPOL_ONE:			KEYB_mp_interpol_one_track ();	break;
		case	KEYB_FNC_INTERPOL_ALL:			KEYB_mp_interpol_all_tracks ();	break;
		case	KEYB_FNC_BLOCK_START:			KEYB_mp_block_start ();				break;
		case	KEYB_FNC_BLOCK_END:				KEYB_mp_block_end ();				break;
		case	KEYB_FNC_INSERT_LINE_TRACK:	KEYB_mp_insert_line (PatternTools_SCOPE_TRACK,   false);	break;
		case	KEYB_FNC_INSERT_LINE_PATTERN:	KEYB_mp_insert_line (PatternTools_SCOPE_PATTERN, false);	break;
		case	KEYB_FNC_INSERT_LINE_PRESET:	KEYB_mp_insert_line (PatternTools_SCOPE_PRESET,  false);	break;
		case	KEYB_FNC_DELETE_LINE_TRACK:	KEYB_mp_delete_line (PatternTools_SCOPE_TRACK,   false);	break;
		case	KEYB_FNC_DELETE_LINE_PATTERN:	KEYB_mp_delete_line (PatternTools_SCOPE_PATTERN, false);	break;
		case	KEYB_FNC_DELETE_LINE_PRESET:	KEYB_mp_delete_line (PatternTools_SCOPE_PRESET,  false);	break;
		case	KEYB_FNC_CUT_BLOCK:				KEYB_mp_cut_block ();				break;
		case	KEYB_FNC_COPY_BLOCK:				KEYB_mp_copy_block ();				break;
		case	KEYB_FNC_PASTE_BLOCK:			KEYB_mp_paste_block ();				break;
		case	KEYB_FNC_FIRST_LINE:				KEYB_mp_first_line ();				break;
		case	KEYB_FNC_LAST_LINE:				KEYB_mp_last_line ();				break;
		case	KEYB_FNC_SELECT_TRACK_PAT:		KEYB_mp_select_track_pattern ();	break;
		case	KEYB_FNC_SELECT_TOP_LEFT:		KEYB_mp_select_top_left ();		break;
		case	KEYB_FNC_SELECT_BOT_RIGHT:		KEYB_mp_select_bottom_right ();	break;
		case	KEYB_FNC_QUICK_SAVE_MOD:		KEYB_mp_quick_save (false);		break;
		case	KEYB_FNC_QUICK_SAVE_SONG:		KEYB_mp_quick_save (true);			break;
		case	KEYB_FNC_QUICK_LOAD_MOD:		KEYB_mp_quick_load (false);		break;
		case	KEYB_FNC_QUICK_LOAD_SONG:		KEYB_mp_quick_load (true);			break;
		case	KEYB_FNC_NEW_MOD:					KEYB_mp_new_module ();				break;
		case	KEYB_FNC_UNDO:						KEYB_mp_undo ();						break;
		case	KEYB_FNC_REDO:						KEYB_mp_redo ();						break;
		case	KEYB_FNC_PLAY_LINE:				KEYB_mp_play_line ();				break;
		case	KEYB_FNC_PLAY_CONTEXT:			KEYB_mp_play_context ();			break;
		case	KEYB_FNC_CHANGE_REPLAY_FREQ:	KEYB_mp_change_replay_freq ();	break;

		default:
			/* Erreur: code non assigne a une fonction */
			assert (false);
			break;
		}
	}

	/* Ou une touche de groupe ? */
	else if (group_flag)
	{
		KEYB_key_group [group_cnt].section [section].fnc_ptr (group_cnt, key_code, parameter);
	}

	/* Sinon c'est peut-etre une note qui a ete rentree */
	else
	{
		KEYB_mp_edit_keys (key_code);
	}

	KEYB_flush_keyboard_buffer ();

	MPAN_refresh_dynamic_display ();
}



void	KEYB_mp_edit_keys (LWORD key_code)
{
	int		scan_code;
	int		ascii_code;
	int		pattern;
	int		line;
	int		track_type;
	int		ext_track_type;
	int		track;
	int		note;
	unsigned long	value;
	int		offset;			/* Offset de l'octet qui contient le chiffre hexa */
	int		offset_2;		/* 1 si chiffre = 1er chiffre, 0 si chiffre = unites */
	signed int	dec_code;
	signed int	hexa_code;
	signed int	note_code;
	bool		new_flag;
	int		field;
	int		test_pos;
	int		byte_index;
	int		content;
	int		old_test_pos;
	int		field_pos;
	BYTE		*note_ptr;

	/* Variables preliminaires */
	scan_code = (key_code >> 16) & 0xFF;
	ascii_code = toupper (key_code & 0xFF);
	pattern = GTK_get_current_pattern_number ();
	line = GTK_get_line_position ();
	track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
	track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	
	UndoCellPatNote	undo_cell (track_type, pattern, line, track);	// Memorise la note sous le curseur en cas de modif
	
	note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);
	ext_track_type = GTK_get_ext_track_type (track_type, note_ptr);

	dec_code = -1;
	hexa_code = -1;
	if (   scan_code >= KEYB_SCANCODE_ROW1
		 && scan_code <= KEYB_SCANCODE_ROW1 + 9)
	{
		dec_code = scan_code - KEYB_SCANCODE_ROW1 + 1;
		if (dec_code == 10)
		{
			dec_code = 0;
		}
		hexa_code = dec_code;
	}
	if (ascii_code >= 'A' && ascii_code <= 'F')
	{
		hexa_code = ascii_code - 'A' + 10;
	}

	note_code = KEYB_key_2_note [scan_code];
	if (note_code >= 0)
	{
		note = note_code + GTK_octave * 12;
		if (note <= 0 || note >= GTK_NBRNOTES_MAXI)
		{
			note = 0;
		}
	}
	else
	{
		note = 0;
	}

	new_flag = false;

/*______________________________________________
 *
 * Touches en mode Edit
 *______________________________________________
 */

	if (GTK_get_edit_status () != 0)
	{
		/* Recherche du type de champ dans lequel on est */
		field = 0;
		test_pos = 0;
		byte_index = 0;
		for ( ; ; )
		{
			content = TRK_note_content [ext_track_type] [field].content;
			old_test_pos = test_pos;
			test_pos += TRK_note_element [content].nbr_col;
			if (TRK_cursor_pos < test_pos)
			{
				break;
			}
			byte_index += TRK_note_element [content].nbr_bytes;
			field ++;
		}

		/* Enregistre la donnee suivant le type du champ */
		field_pos = TRK_cursor_pos - old_test_pos;
		note_ptr += byte_index;
		switch (content)
		{

		/* Note */
		case	TRK_CONTENT_NOTE:
			if (note > 0)
			{
				*note_ptr = note;
				note_ptr [1] = GTK_instr_nbr;
				new_flag = true;
			}
			break;

		/* Colonne de volume */
		case	TRK_CONTENT_VOLUME:
			if (field_pos == 0)
			{
				if (hexa_code >= 0 && hexa_code <= 4)
				{
					value = hexa_code + 1;
				}
				else
				{
					for (value = 0; value < 0x10; value ++)
					{
						if (ascii_code == TRK_vol_col_fx [value])
						{
							break;
						}
					}
				}
				if (value < 0x10)
				{
					if (value > 0)
					{
						value = ((*note_ptr) & 0x0F) + (value << 4);
					}
					if (   (   value <= 0x50
					        && value >= 0x10)
					    || value >= 0x60
					    || value == 0)
					{
						*note_ptr = (BYTE) value;
						new_flag = true;
					}
				}
			}

			else if (hexa_code >= 0)
			{
				value = (*note_ptr) & 0xF0;
				if (hexa_code > 0)
				{
					value = MAX (value, 0x10);
				}
				value += hexa_code;
				if (   (   value <= 0x50
				        && value >= 0x10)
				    || value >= 0x60
				    || value == 0)
				{
					*note_ptr = (BYTE) value;
					new_flag = true;
				}
			}
			break;

		/* Hexadecimal */
		case	TRK_CONTENT_EFFECT_6H:
		case	TRK_CONTENT_COMMAND:
		case	TRK_CONTENT_EFFECT:
		case	TRK_CONTENT_4H:
		case	TRK_CONTENT_2H:
		case	TRK_CONTENT_INSTR:
			if (hexa_code >= 0)
			{
				offset = field_pos / 2;
				offset_2 = (field_pos + 1) & 1;
				value = note_ptr [offset];
				value &= 0x00F0 >> (offset_2 << 2);
				value |= hexa_code << (offset_2 << 2);
				note_ptr [offset] = (BYTE) value;
				new_flag = true;
			}
			break;

		/* Decimal 5 chiffres */
		case	TRK_CONTENT_2D:
		case	TRK_CONTENT_3D:
		case	TRK_CONTENT_INT:
			if (dec_code >= 0)
			{
				offset_2 = 4 - field_pos;
				value = (unsigned long) (UWORD) BASE_dpeek_moto (note_ptr);
				value = (unsigned long)
						  (  (value % (BASE_power (10, offset_2)))
							+   (value / BASE_power (10, offset_2 + 1))
							  * BASE_power (10, offset_2 + 1));
				value += dec_code * BASE_power (10, offset_2);
				if (value <= 65535L)
				{
					BASE_dpoke_moto (note_ptr, (WORD) value);
					new_flag = true;
				}
			}
			break;
		}

		/* Si on a modifier la partition, il faut le noter dans la
			liste des undo/redo. */
		if (new_flag)
		{
			GTK_undo_list_ptr->add_action (undo_cell, "Note input");
		}
	
	}

	/* Touche hors mode Edit: simple note */
	else
	{
		new_flag = true;
	}

/*______________________________________________
 *
 * Joue la ligne et saute a la ligne ou
 * deplace le curseur a droite si on est en
 * mode Edit.
 *______________________________________________
 */

	if (new_flag)
	{
		Player &			player = Player::use_instance ();

		if (   player.get_play_mode () == Player::MODE_STOP
		    && GTK_get_edit_status () != 0)
		{
			GTK_play_one_line (pattern, line);

			if ((key_code & KEYB_SHIFT) != 0)
			{
				KEYB_mp_cursor_right ();
			}
			else
			{
				GTK_next_line ();
			}
		}

		else
		{
			switch (track_type)
			{
			case	Pattern_TYPE_SPL:
				/* Jouer la note sur la voie du curseur */
				if (note > 0)
				{
					player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN] = note;
					player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN + 1] = GTK_instr_nbr;
					player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN + 2] = 0;
					player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN + 3] = 0;
					player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN + 4] = 0;
					player._new_note_flag [Pattern_TYPE_SPL] [track] = true;
					player._new_line_flag [Pattern_TYPE_SPL] = true;
				}
				break;

			case	Pattern_TYPE_FX:
				/* Jouer la note sur la premiere piste SPL trouvee dans les sources
					de la piste d'effets (en remontant les effets si necessaire) */

				/*** A faire ***/

				break;
			}
		}
	}
}



void	KEYB_mp_cursor_left (void)
{
	INTR_pattern_editor_track_ptr->cursor_left ();
}



void	KEYB_mp_cursor_right (void)
{
	INTR_pattern_editor_track_ptr->cursor_right ();
}



void	KEYB_mp_prev_instr (void)
{
	GTK_set_cur_instr (GTK_instr_nbr - 1);
}



void	KEYB_mp_next_instr (void)
{
	GTK_set_cur_instr (GTK_instr_nbr + 1);
}



void	KEYB_mp_prev_sample (void)
{
	GTK_set_cur_sample (GTK_sample_nbr - 1);
}



void	KEYB_mp_next_sample (void)
{
	GTK_set_cur_sample (GTK_sample_nbr + 1);
}



void	KEYB_mp_cur_instr (void)
{
	const int		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	if (track_type == Pattern_TYPE_SPL)
	{
		const int		pattern = GTK_get_current_pattern_number ();
		const int		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		const int		curs_line = GTK_get_line_position ();
		const int		nbr_lines = PAT_get_pattern_height (pattern);
		bool				cont_flag = true;
		for (int cnt = 0; cnt < nbr_lines && cont_flag; ++cnt)
		{
			for (int dir = -1; dir <= 1 && cont_flag; dir += 2)
			{
				const int		line = curs_line + cnt * dir;
				if (line >= 0 && line < nbr_lines)
				{
					const MODS_GT2_SPL_NOTE *	data_ptr = PAT_get_spl_note_adr_pat (pattern, line, track);
					const int		instr = data_ptr->instr;
					if (instr > 0)
					{
						GTK_set_cur_instr (instr);
						cont_flag = false;
					}
				}
			}
		}
	}
}



void	KEYB_mp_prev_line (void)
{
	INTR_pattern_editor_track_ptr->prev_line ();
}



void	KEYB_mp_next_line (void)
{
	INTR_pattern_editor_track_ptr->next_line ();
}



void	KEYB_mp_prev_octave (void)
{
	GTK_octave --;
	while (GTK_octave < 0)
	{
		GTK_octave += 9;
	}
	MPAN_display_status (true);
}



void	KEYB_mp_next_octave (void)
{
	GTK_octave ++;
	while (GTK_octave >= 9)
	{
		GTK_octave -= 9;
	}
	MPAN_display_status (true);
}



void	KEYB_mp_prev_column (void)
{
	INTR_pattern_editor_track_ptr->prev_column ();
}



void	KEYB_mp_next_column (void)
{
	INTR_pattern_editor_track_ptr->next_column ();
}



void	KEYB_mp_prev_bar (void)
{
	int		bar_nbr;
	int		bar_len;

	const Player &	player = Player::use_instance ();

	bar_len = player.get_bar_length ();
	bar_nbr = (GTK_get_line_position () + bar_len - 1) / bar_len - 1;
	if (bar_nbr < 0)
	{
		bar_nbr = (PAT_get_current_pattern_height () - 1) / bar_len;
	}
	GTK_set_line_position (bar_nbr * bar_len);
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_next_bar (void)
{
	int		bar_nbr;
	int		bar_len;
	int		linepos;

	const Player &	player = Player::use_instance ();

	bar_len = player.get_bar_length ();
	bar_nbr = (GTK_get_line_position () + bar_len - 1) / bar_len + 1;
	linepos = bar_nbr * bar_len;
	if (linepos >= PAT_get_current_pattern_height ())
	{
		linepos = 0;
	}
	GTK_set_line_position (linepos);
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_prev_songpos (void)
{
	MPAN_set_song_position (INTR_CHGTYPE_REL, -1);
}



void	KEYB_mp_next_songpos (void)
{
	MPAN_set_song_position (INTR_CHGTYPE_REL, 1);
}



void	KEYB_mp_prev_preset (void)
{
	TRK_set_preset (INTR_CHGTYPE_REL, -1);
}



void	KEYB_mp_next_preset (void)
{
	TRK_set_preset (INTR_CHGTYPE_REL, 1);
}



void	KEYB_mp_dec_step (void)
{
	MPAN_set_line_step (INTR_CHGTYPE_REL, -1);
}



void	KEYB_mp_inc_step (void)
{
	MPAN_set_line_step (INTR_CHGTYPE_REL, 1);
}



/*==========================================================================*/
/*      Nom: KEYB_stop_edit                                                 */
/*      Description: Arrete le replay et passe le mode Edit en on ou off.   */
/*==========================================================================*/

void	KEYB_mp_stop_edit (void)
{

	const Player &	player = Player::use_instance ();
	const int		play_mode = player.get_play_mode ();
	if (play_mode != 0)
	{
		if (play_mode == 1)
		{
			MPAN_display_message ("Song stopped.");
		}
		else
		{
			MPAN_display_message ("Pattern stopped.");
		}
		GTK_set_edit_status (0);
		INTR_stop_song ();
	}

	else
	{
		GTK_set_edit_status (GTK_get_edit_status () == 0);
		INTR_stop_song ();
		if (GTK_get_edit_status ())
		{
			MPAN_display_message ("Edit mode activated.");
		}
		else
		{
			MPAN_display_message ("Edit mode deactivated.");
		}
	}
}



void	KEYB_mp_play_song (void)
{
	INTR_play_song (false);
}



void	KEYB_mp_cont_song (void)
{
	INTR_play_song (true);
}



void	KEYB_mp_play_pattern (void)
{
	INTR_play_pattern (false);
}



void	KEYB_mp_cont_pattern (void)
{
	INTR_play_pattern (true);
}



void	KEYB_mp_del_note (void)
{
	int		pattern;
	int		line;
	int		track_type;
	int		track;
	BYTE		*note_ptr;

	if (GTK_get_edit_status () != 0)
	{
		/* Variables preliminaires */
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);

		/* Memorise les informations pour l'undo */
		GTK_undo_list_ptr->add_action (UndoCellPatNote (track_type, pattern, line, track),
		                               "Delete note");

		/* Efface la note */
		switch (track_type)
		{
		case	Pattern_TYPE_SPL:
			*((WORD *)note_ptr) = 0;
			break;

		case	Pattern_TYPE_FX:
			*((LWORD *)note_ptr) = 0;
			break;

		case	Pattern_TYPE_MID:

			/*** A faire ***/

			break;
		}

		GTK_next_line ();
	}
}



void	KEYB_mp_del_fx ()
{
	int		pattern;
	int		line;
	int		track_type;
	int		track;
	BYTE		*note_ptr;

	if (GTK_get_edit_status () != 0)
	{
		/* Variables preliminaires */
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);

		/* Memorise les informations pour l'undo */
		GTK_undo_list_ptr->add_action (UndoCellPatNote (track_type, pattern, line, track),
		                               "Delete track command");

		/* Efface l'effet */
		switch (track_type)
		{
		case	Pattern_TYPE_SPL:
			*((WORD *) (note_ptr + 2)) = 0;
			break;

		case	Pattern_TYPE_AIN:
			*((WORD *) note_ptr) = 0;
			break;

		case	Pattern_TYPE_FX:
			*((WORD *) (note_ptr + 4)) = 0;
			break;

		case	Pattern_TYPE_MID:

			/*** A faire ***/

			break;
		}

		GTK_next_line ();
	}
}



void	KEYB_mp_del_volume (void)
{
	int		pattern;
	int		line;
	int		track_type;
	int		track;
	BYTE		*note_ptr;

	if (GTK_get_edit_status () != 0)
	{
		/* Variables preliminaires */
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);

		/* Memorise les informations pour l'undo */
		GTK_undo_list_ptr->add_action (UndoCellPatNote (track_type, pattern, line, track),
		                               "Delete volume");

		/* Efface le volume */
		switch (track_type)
		{
		case	Pattern_TYPE_SPL:
			*(note_ptr + 4) = 0;
			break;

		case	Pattern_TYPE_AIN:
			*(note_ptr + 2) = 0;
			break;

		case	Pattern_TYPE_FX:
			*(note_ptr + 6) = 0;
			break;

		case	Pattern_TYPE_MID:

			/*** A faire ***/

			break;
		}

		GTK_next_line ();
	}
}



void	KEYB_mp_del_whole_note (void)
{
	int		pattern;
	int		line;
	int		track_type;
	int		track;
	BYTE		*note_ptr;

	if (GTK_get_edit_status () != 0)
	{
		/* Variables preliminaires */
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);

		/* Memorise les informations pour l'undo */
		GTK_undo_list_ptr->add_action (UndoCellPatNote (track_type, pattern, line, track),
		                               "Delete whole note");

		/* Efface la toute la note */
		switch (track_type)
		{
		case	Pattern_TYPE_SPL:
			*((LWORD *) note_ptr) = 0;
			*(note_ptr + 4) = 0;
			break;

		case	Pattern_TYPE_AIN:
			*((WORD *) note_ptr) = 0;
			*(note_ptr + 2) = 0;
			break;

		case	Pattern_TYPE_FX:
			*((LWORD *) note_ptr) = 0;
			*((WORD *) (note_ptr + 4)) = 0;
			*(note_ptr + 6) = 0;
			break;

		case	Pattern_TYPE_MID:

			/*** A faire ***/

			break;
		}

		GTK_next_line ();
	}
}



void	KEYB_mp_chg_pad_mode (void)
{
	KEYB_pad_mode = (KEYB_pad_mode + 1) % KEYB_PAD_NBR_ELT;
	MPAN_display_status (true);
}



void	KEYB_mp_chg_display_type (void)
{
	TRK_set_disp_type (INTR_CHGTYPE_REL, 1);
}



void	KEYB_mp_interpol_one_track (void)
{
	int		track;
	bool		interpol_flag;
	char		message_0 [99+1];

	Player &			player = Player::use_instance ();

	track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
	interpol_flag = ! player.get_interpolation_flag (track);
	player.set_interpolation_flag (track, interpol_flag);

	if (interpol_flag)
	{
		sprintf (message_0, "Interpolation enable on track # %d.", track);
	}
	else
	{
		sprintf (message_0, "Interpolation disable on track # %d.", track);
	}
	MPAN_display_message (message_0);
}



void	KEYB_mp_interpol_all_tracks (void)
{
	int		track;
	bool		interpol_flag;

	Player &			player = Player::use_instance ();

	interpol_flag = ! player.get_interpolation_flag (0);
	for (track = 0; track < GTK_nbr_tracks [Pattern_TYPE_SPL]; track ++)
	{
		player.set_interpolation_flag (track, interpol_flag);
	}

	if (interpol_flag)
	{
		MPAN_display_message ("Interpolation enable on all the tracks.");
	}
	else
	{
		MPAN_display_message ("Interpolation disable on all the tracks.");
	}
}



void	KEYB_mp_block_start (void)
{
	SSMTOOL_set_block_start ();
}



void	KEYB_mp_block_end (void)
{
	SSMTOOL_set_block_end ();
}



void	KEYB_mp_insert_line (int scope, bool rot_flag)
{
	PatternTools	tool;
	int		pattern;
	int		line;
	int		track;
	int		track_type;
	List		undo_list;
	UndoCell	*undo_ptr;

	if (GTK_get_edit_status () != 0)
	{
		INTR_graph_ptr->mouse_bee ();

		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];

		if (rot_flag)
		{
			const int		line_end = PAT_get_pattern_height (pattern);
			tool.set_block_single_line (pattern, line_end, track_type, track);
			tool.set_scope (scope);
			tool.copy_block ();
		}

		tool.set_block_single_line (pattern, line, track_type, track);
		tool.set_scope (scope);
		tool.insert_selection (pattern, line, track_type, track, &undo_ptr);
		undo_list.add (undo_ptr);
		delete undo_ptr;
		tool.clear_selection (track_type, &undo_ptr);
		undo_list.add (undo_ptr);
		delete undo_ptr;

		if (rot_flag)
		{
			tool.paste_block (pattern, line, track_type, track, &undo_ptr);
			undo_list.add (undo_ptr);
			delete undo_ptr;
		}

		GTK_undo_list_ptr->add_action (UndoCellList (undo_list),
		                                 String ("Insert line (")
		                               + PatternTools::SCOPE_NAME_0 [scope]
		                               + ")");

		INTR_graph_ptr->mouse_arrow ();
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_delete_line (int scope, bool rot_flag)
{
	PatternTools	tool;
	int		pattern;
	int		line;
	int		track;
	int		track_type;
	List		undo_list;
	UndoCell	*undo_ptr;

	if (GTK_get_edit_status () != 0)
	{
		INTR_graph_ptr->mouse_bee ();

		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
		track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
		
		tool.set_block_single_line (pattern, line, track_type, track);
		tool.set_scope (scope);

		if (rot_flag)
		{
			tool.copy_block ();
		}

		tool.delete_selection (track_type, &undo_ptr);
		undo_list.add (undo_ptr);
		delete undo_ptr;

		if (rot_flag)
		{
			const int		line_end = PAT_get_pattern_height (pattern);
			tool.set_block_single_line (pattern, line_end, track_type, track);
			tool.paste_block (pattern, line_end, track_type, track, &undo_ptr);
			undo_list.add (undo_ptr);
			delete undo_ptr;
		}

		GTK_undo_list_ptr->add_action (UndoCellList (undo_list),
		                                 String ("Insert line (")
		                               + PatternTools::SCOPE_NAME_0 [scope]
		                               + ")");

		INTR_graph_ptr->mouse_arrow ();
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_cut_block (void)
{
	TRK_cut_block ();
}



void	KEYB_mp_copy_block (void)
{
	TRK_copy_block ();
}



void	KEYB_mp_paste_block (void)
{
	TRK_paste_block ();
}



void	KEYB_mp_first_line (void)
{
	GTK_set_line_position (0);
	const Player &	player = Player::use_instance ();
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_last_line (void)
{
	int		line;

	line = PAT_get_current_pattern_height () - 1;
	GTK_set_line_position (line);
	const Player &	player = Player::use_instance ();
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_select_track_pattern (void)
{
	int		pattern;
	bool		sel_flag;
	bool		track_flag;
	PatSelection	sel;
	
	pattern = GTK_get_current_pattern_number ();
	sel = INTR_pattern_editor_track_ptr->get_selection (sel_flag);
	track_flag = false;
	if (sel_flag)
	{
		if (   sel.get_pattern () == pattern
		    && sel.get_left_col () == TRK_cursor_col + TRK_cursor_offset
		    && sel.get_start_line () == 0
			 && sel.get_nbr_col () == 1
			 && sel.get_nbr_lines () == PAT_get_pattern_height (pattern))
		{
			track_flag = true;
		}
	}

	if (track_flag)
	{
		INTR_pattern_editor_track_ptr->set_selection_intr
			(0, 0, TRK_preset_data [TRK_preset_nbr].nbr, PAT_get_pattern_height (pattern));
	}
	else
	{
		INTR_pattern_editor_track_ptr->set_selection_intr
			(TRK_cursor_col + TRK_cursor_offset, 0, 1, PAT_get_pattern_height (pattern));
	}
}



void	KEYB_mp_select_top_left (void)
{
	INTR_pattern_editor_track_ptr->set_selection_top_left_intr
		(TRK_cursor_col + TRK_cursor_offset, GTK_get_line_position ());
}



void	KEYB_mp_select_bottom_right (void)
{
	INTR_pattern_editor_track_ptr->set_selection_bottom_right_intr
		(TRK_cursor_col + TRK_cursor_offset, GTK_get_line_position ());
}



void	KEYB_mp_quick_save (bool song_flag)
{
	String	filename;

	filename = FNAM_get_full_filename (FILE_module_filename);

	if (BASE_search_in_string_nocase ("noname", filename.c_str ()) != 0)
	{
		INTR_graph_ptr->mouse_bee ();
		MODS_save_module_direct (FILE_module_filename.c_str (), song_flag);
		INTR_graph_ptr->mouse_arrow ();
		MPAN_display_message ((FILE_module_filename + " saved.").c_str ());
	}
	else
	{
		MODS_save_module (song_flag);
	}
}



void	KEYB_mp_quick_load (bool song_flag)
{
	MODS_load_module (song_flag, NULL);
	MPAN_display_main_pannel ();
}



void	KEYB_mp_new_module (void)
{
	if (INTR_dialog_box ("CLEAR ALL",
	                     "Clear module ?",
								"OK\nCancel", 0, 1) == 0)
	{
		INTR_graph_ptr->mouse_bee ();
		if (GTK_clear_all ())
		{
			INTR_dialog_box ("Error",
								  "Couldn't clear module successfully.",
								  "Cancel", 0, 0);
		}
		else
		{
			FILE_generate_new_module_name ();
		}
		INTR_graph_ptr->mouse_arrow ();
		MPAN_display_main_pannel ();
	}
}



void	KEYB_mp_undo (void)
{
	String	message ("[");

	message += GTK_undo_list_ptr->undo_action () + "] undone.";
	MPAN_display_message (message.c_str ());

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->check_cursor ();
	MPAN_refresh_display ();
}



void	KEYB_mp_redo (void)
{
	String	message ("[");

	message += GTK_undo_list_ptr->redo_action () + "] redone.";
	MPAN_display_message (message.c_str ());

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->check_cursor ();
	MPAN_refresh_display ();
}



void	KEYB_mp_play_line (void)
{
	int		pattern;
	int		line;

	const Player &	player = Player::use_instance ();
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		GTK_play_one_line (pattern, line);
	}
	GTK_next_line ();
}



void	KEYB_mp_play_context (void)
{
	int		pattern;
	int		line;

	const Player &	player = Player::use_instance ();
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		pattern = GTK_get_current_pattern_number ();
		line = GTK_get_line_position ();
		GTK_play_context (pattern, line);
	}
}



void	KEYB_mp_change_replay_freq (void)
{
	bool		play_flag;
	int		play_mode;
	long		freq;
	SoundDriver_CONFIG	config;
	char		message_0 [99+1];

	Player &			player = Player::use_instance ();

	play_flag = player.get_player_started_flag ();
	if (play_flag)
	{
		play_mode = player.get_play_mode ();
		INTR_stop_song ();
		player.stop_player ();
	}

	OS_sound_driver.get_config (&config);
	freq = config.sample_freq / 2;
	if (freq < 11025)
	{
		freq = 44100;
	}
	config.sample_freq = freq;
	OS_sound_driver.set_config (&config);
	player.set_replay_freq (config.sample_freq);

	if (play_flag)
	{
		player.start_player ();
		player.set_play_mode (play_mode, -1, -1, true);
	}

	sprintf (message_0, "Replay frequency changed to %ld Hz.", (long)MIX_replay_freq);
	MPAN_display_message (message_0);
}



void	KEYB_mp_del_field (void)
{
	int		pattern;
	int		line;
	int		track;
	int		track_type;
	int		ext_track_type;
	int		test_pos;
	int		old_test_pos;
	int		content;
	int		byte_index;
	int		field;
	BYTE		*note_ptr;
	PatEvent	event;
	int		field_index;
	PatEventField	*field_ptr;

	/* On n'opere qu'en mode Edit */
	if (GTK_get_edit_status () == 0)
	{
		return;
	}

	/* Cherche les infos sur la note courante */
	pattern = GTK_get_current_pattern_number ();
	line = GTK_get_line_position ();
	track = TRK_preset_data [TRK_preset_nbr].track_nbr [TRK_cursor_col + TRK_cursor_offset];
	track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	note_ptr = (BYTE *) PAT_get_note_adr_pat (track_type, pattern, line, track);
	ext_track_type = GTK_get_ext_track_type (track_type, note_ptr);

	/* Recherche du champ dans lequel on est */
	field = 0;
	test_pos = 0;
	byte_index = 0;
	for ( ; ; )
	{
		content = TRK_note_content [ext_track_type] [field].content;
		old_test_pos = test_pos;
		test_pos += TRK_note_element [content].nbr_col;
		if (TRK_cursor_pos < test_pos)
		{
			break;
		}
		byte_index += TRK_note_element [content].nbr_bytes;
		field ++;
	}

	/* Enregistre la note pour l'Undo */
	UndoCellPatNote	undo_cell (track_type, pattern, line, track);
	GTK_undo_list_ptr->add_action (undo_cell, "Field deletion");

	/* Recupere toute la note */
	event.set_data (track_type, note_ptr);

	/* Efface le champ */
	field_index = 0;
	field_ptr = event.get_field_type (content, field_index);
	if (field_ptr != NULL)
	{
		field_ptr->clear ();
		event.set_field (field_index, field_ptr);
		delete field_ptr;
	}

	/* Si on efface une note, on efface aussi l'instrument associe */
	if (content == TRK_CONTENT_NOTE)
	{
		field_index = 0;
		field_ptr = event.get_field_type (TRK_CONTENT_INSTR, field_index);
		if (field_ptr != NULL)
		{
			field_ptr->clear ();
			event.set_field (field_index, field_ptr);
			delete field_ptr;
		}
	}

	/* Commit */
	event.get_data (track_type, note_ptr);

	GTK_next_line ();
}



/****************************************************************************/
/*                                                                          */
/*      PANNEAU PRINCIPAL - FONCTIONS DES GROUPES DE TOUCHES                */
/*                                                                          */
/****************************************************************************/



void	KEYB_mp_select_instrument (int group_nbr, LWORD key_code, LWORD param)
{
	const int		size = KEYB_key_group [group_nbr].nbr_keys;
	const int		base =   (((key_code & KEYB_SHIFT) != 0) ? size      : 0)
						       + (((key_code & KEYB_CTRL ) != 0) ? size << 1 : 0);
	const int		index = base + param;
	const bool		alt_flag = ((key_code & KEYB_ALT) != 0);

	switch (KEYB_pad_mode)
	{
	// Drumpad, record
	case	KEYB_PAD_DRUM:
		if (index < Drumpad::NBR_SLOTS)
		{
			if (alt_flag)
			{
				KEYB_drumpad.record_slot (index);
			}
			else
			{
				KEYB_press_drumpad (index);
			}
		}
		break;

	// Instrument selection
	default:
		MPAN_set_instrument_number (
			INTR_CHGTYPE_ABS,
			index + 1,
			RSC_OBJ_MP_VAR_INST_VAL
		);
		break;
	}
}



void	KEYB_mp_jump_to_bar (int group_nbr, LWORD key_code, LWORD param)
{
	const Player &	player = Player::use_instance ();
	GTK_set_line_position (param * player.get_bar_length ());
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
	}
}



void	KEYB_mp_jump_to_track (int group_nbr, LWORD key_code, LWORD param)
{
	int		base;
	int		size;

	size = KEYB_key_group [group_nbr].nbr_keys;
	base =   (((key_code & KEYB_SHIFT) != 0) ? size      : 0)
	       + (((key_code & KEYB_CTRL ) != 0) ? size << 1 : 0);

	INTR_pattern_editor_track_ptr->set_abs_column (base + param);
}



void	KEYB_mp_select_menu (int group_nbr, LWORD key_code, LWORD param)
{
	switch (param)
	{
	case	0:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_FILES);
		break;
	case	1:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_TOOLS);
		break;
	case	2:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_INSTR);
		break;
	case	3:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_SPL);
		break;
	case	4:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_ENV);
		break;
	case	5:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_MIX);
		break;
	case	6:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_FX);
		break;
	case	7:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_MIDI);
		break;
	case	8:
		INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_PREF);
		break;
	}
	RSC_display_object (RSC_OBJ_MP_SMICN);
	INTR_pattern_editor_menu_ptr->redraw ();
}



void	KEYB_press_drumpad (int index)
{
	const int		track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	if (track_type != Pattern_TYPE_SPL)
	{
		MPAN_display_message ("Drumpad works only on Sample tracks.");
	}

	else if (! KEYB_drumpad.is_slot_full (index))
	{
		MPAN_display_message ("Empty Drumpad slot.");
	}

	else
	{
		if (GTK_get_edit_status () != 0)
		{
			KEYB_drumpad.restitute_slot (index);

			Player &			player = Player::use_instance ();

			if (player.get_play_mode () == Player::MODE_STOP)
			{
				const int		pattern = GTK_get_current_pattern_number ();
				const int		line = GTK_get_line_position ();
				GTK_play_one_line (pattern, line);
				GTK_next_line ();
			}
			else
			{
				const TRK_PRESET &	preset = TRK_preset_data [TRK_preset_nbr];
				const int		cursor_pos = TRK_cursor_col + TRK_cursor_offset;
				const int		track = preset.track_nbr [cursor_pos];
				MODS_GT2_SPL_NOTE	note;
				KEYB_drumpad.get_slot (index, note);
				MODS_GT2_SPL_NOTE * note_ptr = (MODS_GT2_SPL_NOTE *) (
					&player._new_spl_notes [track * MODS_GT2_SPL_NOTE_LEN]
				);
				*note_ptr = note;
				player._new_note_flag [Pattern_TYPE_SPL] [track] = true;
				player._new_line_flag [Pattern_TYPE_SPL] = true;
			}
		}
	}
}



/****************************************************************************/
/*                                                                          */
/*                                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/

