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

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	<math.h>

#include	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"EditString.h"
#include	"edstring.h"
#include	"file.h"
#include	"gtracker.h"
#include	"inst.h"
#include	"Instrument.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"modstruc.h"
#include	"mpannel.h"
#include	"os.h"
#include	"patt.h"
#include	"PatternTools.h"
#include	"player.h"
#include	"Popup.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"samp.h"
#include	"Sample.h"
#include	"song.h"
#include	"String.h"
#include	"tracks.h"



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



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



/*\\\ PROTOTYPES DES FONCTIONS PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



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



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



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



/****************************************************************************/
/*                                                                          */
/*      GESTION DU PANNEAU PRINCIPAL                                        */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: MPAN_manage_all                                                */
/*      Description: Teste l'arbre de resource graphique du panneau         */
/*                   principal et effectue les actions necessaires.         */
/*                   A besoin de l'etat courant de la souris.               */
/*      Retour: true si un objet a ete clique, false sinon.                 */
/*==========================================================================*/

bool	MPAN_manage_all (void)
{
	signed int	sel_object;
	signed int	sel_aine;
	int		track_type;
	int		disp_type;
	char		s_name_0 [SONG_NAME_LEN+1];
	char		i_name_0 [Instrument_NAME_LEN+1];

	track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	disp_type = TRK_preset_data [TRK_preset_nbr].disp_type;

	/* Variables */
	RSC_gere_resource (RSC_OBJ_MP_VAR, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		MPAN_manage_variables (sel_object, sel_aine);
		return (true);
	}

	/* Icones principales */
	RSC_gere_resource (RSC_OBJ_MP_MICN, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		MPAN_manage_main_icons (sel_object, sel_aine);
		return (true);
	}

	/* Icones des sous-menus */
	RSC_gere_resource (RSC_OBJ_MP_SMICN, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		switch (sel_object)
		{
		case	RSC_OBJ_MP_SMICN_DISK:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_FILES);
			break;
		case	RSC_OBJ_MP_SMICN_TOOLS:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_TOOLS);
			break;
		case	RSC_OBJ_MP_SMICN_INSTR:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_INSTR);
			break;
		case	RSC_OBJ_MP_SMICN_SPL:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_SPL);
			break;
		case	RSC_OBJ_MP_SMICN_ENV:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_ENV);
			break;
		case	RSC_OBJ_MP_SMICN_MIX:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_MIX);
			break;
		case	RSC_OBJ_MP_SMICN_FX:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_FX);
			break;
		case	RSC_OBJ_MP_SMICN_MIDI:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_MIDI);
			break;
		case	RSC_OBJ_MP_SMICN_PREF:
			INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_PREF);
			break;
		}
		INTR_pattern_editor_menu_ptr->redraw ();
		return (true);
	}

	/* Sous-menus */
	RSC_gere_resource (RSC_OBJ_MP_SUBM, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		INTR_pattern_editor_menu_ptr->manage (sel_object, sel_aine);
		return (true);
	}

	/* Pistes */
	RSC_gere_resource (RSC_OBJ_MP_TRK, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		INTR_pattern_editor_track_ptr->manage (sel_object, sel_aine);
		return (true);
	}

	/* Types de piste */
	RSC_gere_resource (RSC_OBJ_MP_TICN, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		switch (sel_object)
		{
		case	RSC_OBJ_MP_TICN_SPL:
			TRK_set_track_type (INTR_CHGTYPE_ABS, Pattern_TYPE_SPL);
			break;

		case	RSC_OBJ_MP_TICN_AIN:
			TRK_set_track_type (INTR_CHGTYPE_ABS, Pattern_TYPE_AIN);
			break;

		case	RSC_OBJ_MP_TICN_FX:
			TRK_set_track_type (INTR_CHGTYPE_ABS, Pattern_TYPE_FX);
			break;

		case	RSC_OBJ_MP_TICN_MID:
			TRK_set_track_type (INTR_CHGTYPE_ABS, Pattern_TYPE_MID);
			break;
		}

		return (true);
	}

	/* Song name */
	RSC_gere_resource (RSC_OBJ_MP_SNGNAME, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		SONG_get_song_name (s_name_0);
		if (EDIT_edit_string (s_name_0, RSC_OBJ_MP_SNGNAME_NAME,
									 SONG_NAME_LEN, EditString_TYPE_ALPHA) == 0)
		{
			SONG_set_song_name (s_name_0);
			GTK_modified_flag = true;
		}
		MPAN_display_divers ();
		return (true);
	}

	/* Instrument name */
	RSC_gere_resource (RSC_OBJ_MP_INSNAME, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		INST_get_instr_name (GTK_instr_nbr, i_name_0);
		if (EDIT_edit_string (i_name_0, RSC_OBJ_MP_INSNAME_NAME,
									 Instrument_NAME_LEN, EditString_TYPE_ALPHA) == 0)
		{
			INST_set_instr_name (GTK_instr_nbr, i_name_0);
			GTK_modified_flag = true;
		}
		MPAN_display_divers ();
		return (true);
	}

	/* Icones relatives aux pistes */
	if (TRK_manage_tracks ())
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: MPAN_manage_main_icons                                         */
/*      Description: Gere les icones du haut du panneau principal.          */
/*        - sel_object: objet qui a ete selectionne.                        */
/*        - sel_aine: aine de l'objet selectionne.                          */
/*==========================================================================*/

void	MPAN_manage_main_icons (signed int sel_object, signed int sel_aine)
{
	int		track_type;
	int		song_len;
	int		position;
	signed int	current_pos;
	signed int	pattern;
	String	filename;

	switch (sel_object)
	{
	case	RSC_OBJ_MP_MICN_PLAYSNG:
		INTR_play_song ((RSC_mouse_key & 2) == 2);
		break;

	case	RSC_OBJ_MP_MICN_PLAYPAT:
		INTR_play_pattern ((RSC_mouse_key & 2) == 2);
		break;

	case	RSC_OBJ_MP_MICN_EDIT:
		GTK_set_edit_status (GTK_get_edit_status () == 0);
		if (GTK_get_edit_status ())
		{
			MPAN_display_message ("Edit mode activated.");
		}
		else
		{
			MPAN_display_message ("Edit mode deactivated.");
		}
		MPAN_display_play_status ();
		break;

	case	RSC_OBJ_MP_MICN_STOP:
		INTR_stop_song ();
		break;

	case	RSC_OBJ_MP_MICN_INSPOS:
		position = GTK_get_song_position ();
		if (SONG_insert_pos (position) == 0)
		{
			/* En attendant d'avoir un Undo pour la song... */
			GTK_modified_flag = true;

			/* Passe a la position suivante */
			position ++;
			GTK_set_song_position (position);

			/* Mets un nouveau pattern a cette position */
			if ((RSC_mouse_key & 2) == 2)
			{
				/* Cherche le premier pattern inutilise */
				pattern = 0;
				song_len = SONG_get_song_length ();
				for (current_pos = 0; current_pos < song_len; current_pos ++)
				{
					if (pattern >= PAT_NBRPATTERNS_MAXI)
					{
						break;
					}
					/* Teste si le pattern est dans la song */
					if (pattern == SONG_get_pattern_number (current_pos))
					{
						pattern ++;
						current_pos = -1;
					}
					/* Teste si le pattern est rempli */
					for (track_type = 0; track_type < Pattern_NBR_TYPES; track_type ++)
					{
						if (MODS_pattern_filled (track_type, pattern))
						{
							pattern ++;
							current_pos = -1;
							break;
						}
					}
				}

				if (pattern < PAT_NBRPATTERNS_MAXI)
				{
					SONG_set_pattern_number (position, pattern);
					PAT_set_pattern_height_intr (INTR_CHGTYPE_ABS, PAT_new_pattern_nbr_lines);
				}
			}
		}
		INTR_pattern_editor_track_ptr->check_cursor ();
		MPAN_display_var_icons (true, true);
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
		break;

	case	RSC_OBJ_MP_MICN_DELPOS:
		position = GTK_get_song_position ();
		SONG_delete_pos (position);
		GTK_modified_flag = true;
		INTR_pattern_editor_track_ptr->check_cursor ();
		MPAN_display_var_icons (true, true);
		INTR_pattern_editor_track_ptr->refresh_dynamic (true);
		break;

	case	RSC_OBJ_MP_MICN_HELP:
		filename =   FILE_program_pathname
                 + "manual" + FILE_path_separator_0 + "index.html";
		if (OS_launch_html_file (filename.c_str ()))
		{
			INTR_dialog_box ("ERROR",
			                 (String ("Could't open help file:\n") + filename).c_str (),
			                 "Cancel", 0, 0);
		}
		break;

	case	RSC_OBJ_MP_MICN_SE:
		INTR_main_screen = INTR_MAIN_SCREEN_SAMPLE_EDITOR;
		break;

	case	RSC_OBJ_MP_MICN_EXIT:
		GTK_ask_for_exit_flag = true;
		break;
	}
}



/*==========================================================================*/
/*      Nom: MPAN_manage_variables                                          */
/*      Description: Gere les variables du panneau principal.               */
/*      Parametres en entree:                                               */
/*        - sel_object: objet qui a ete selectionne.                        */
/*        - sel_aine: aine de l'objet selectionne.                          */
/*==========================================================================*/

void	MPAN_manage_variables (int sel_object, int sel_aine)
{
	switch (sel_object)
	{
	case	RSC_OBJ_MP_VAR_POS_U:
		MPAN_set_song_position (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_POS_D:
		MPAN_set_song_position (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_POS_VAL:
		MPAN_set_song_position (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_PAT_U:
		SONG_set_pattern_number_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_PAT_D:
		SONG_set_pattern_number_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_PAT_VAL:
		SONG_set_pattern_number_intr (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_LEN_U:
		SONG_set_song_length_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_LEN_D:
		SONG_set_song_length_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_LEN_VAL:
		SONG_set_song_length_intr (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_REP_U:
		SONG_set_song_repeat_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_REP_D:
		SONG_set_song_repeat_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_REP_VAL:
		SONG_set_song_repeat_intr (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_INST_U:
		MPAN_set_instrument_number (INTR_CHGTYPE_REL,
											 INTR_inc_speed [0] [RSC_mouse_key],
											 RSC_OBJ_MP_VAR_INST_VAL);
		break;

	case	RSC_OBJ_MP_VAR_INST_D:
		MPAN_set_instrument_number (INTR_CHGTYPE_REL,
											 -INTR_inc_speed [0] [RSC_mouse_key],
											 RSC_OBJ_MP_VAR_INST_VAL);
		break;

	case	RSC_OBJ_MP_VAR_INST_VAL:
		if (RSC_mouse_key == 2)
		{
			MPAN_set_instrument_number (INTR_CHGTYPE_POP, 0, RSC_OBJ_MP_VAR_INST_VAL);
		}
		else
		{
			MPAN_set_instrument_number (INTR_CHGTYPE_KBD, 0, RSC_OBJ_MP_VAR_INST_VAL);
		}
		break;

	case	RSC_OBJ_MP_VAR_SAMP_U:
		MPAN_set_sample_number (INTR_CHGTYPE_REL,
										INTR_inc_speed [0] [RSC_mouse_key],
										RSC_OBJ_MP_VAR_SAMP_VAL);
		break;

	case	RSC_OBJ_MP_VAR_SAMP_D:
		MPAN_set_sample_number (INTR_CHGTYPE_REL,
										-INTR_inc_speed [0] [RSC_mouse_key],
										RSC_OBJ_MP_VAR_SAMP_VAL);
		break;

	case	RSC_OBJ_MP_VAR_SAMP_VAL:
		if (RSC_mouse_key == 2)
		{
			MPAN_set_sample_number (INTR_CHGTYPE_POP, 0, RSC_OBJ_MP_VAR_SAMP_VAL);
		}
		else
		{
			MPAN_set_sample_number (INTR_CHGTYPE_KBD, 0, RSC_OBJ_MP_VAR_SAMP_VAL);
		}
		break;

	case	RSC_OBJ_MP_VAR_LINE_U:
		PAT_set_pattern_height_intr (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_LINE_D:
		PAT_set_pattern_height_intr (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_LINE_VAL:
		if (RSC_mouse_key == 2)
		{
			PAT_set_pattern_height_intr (INTR_CHGTYPE_ABS, PAT_new_pattern_nbr_lines);
		}
		else
		{
			PAT_set_pattern_height_intr (INTR_CHGTYPE_KBD, 0);
		}
		break;

	case	RSC_OBJ_MP_VAR_STEP_U:
		MPAN_set_line_step (INTR_CHGTYPE_REL, 1);
		break;

	case	RSC_OBJ_MP_VAR_STEP_D:
		MPAN_set_line_step (INTR_CHGTYPE_REL, -1);
		break;

	case	RSC_OBJ_MP_VAR_STEP_VAL:
		MPAN_set_line_step (INTR_CHGTYPE_POP, 0);
		break;

	case	RSC_OBJ_MP_VAR_TEMPO_U:
		MPAN_set_song_tempo (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_TEMPO_D:
		MPAN_set_song_tempo (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_TEMPO_VAL:
		MPAN_set_song_tempo (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_SPD_U:
		MPAN_set_song_speed (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_SPD_D:
		MPAN_set_song_speed (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	case	RSC_OBJ_MP_VAR_SPD_VAL:
		MPAN_set_song_speed (INTR_CHGTYPE_KBD, 0);
		break;

	case	RSC_OBJ_MP_VAR_CLK_VAL:
		{
			Player &			player = Player::use_instance ();
			player.reset_elapsed_time ();
			MPAN_display_var_icons (true, true);
		}
		break;
	}
}



/*==========================================================================*/
/*      Nom: MPAN_set_song_position                                         */
/*      Description: Change la position courante dans la song, de           */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entr‚e:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	MPAN_set_song_position (int type, signed int value)
{
	signed int	songpos;
	int			linepos;
	int			height;
	char			nbr3_0 [3+1];

	songpos = GTK_get_song_position ();

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		songpos += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, INTR_base_song_3, songpos);
		EDIT_edit_string_base (nbr3_0, RSC_OBJ_MP_VAR_POS_VAL, 3, INTR_base_song);
		songpos = (int) strtol (nbr3_0, NULL, INTR_base_song);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		songpos = value;
		break;
	}

	songpos = MIN (songpos, SONG_get_song_length () - 1);
	songpos = MAX (songpos, 0);
	GTK_set_song_position (songpos);
	height = PAT_get_pattern_height (SONG_get_pattern_number (songpos));
	linepos = GTK_get_line_position ();
	linepos = MIN (linepos, height - 1);
	GTK_set_line_position (linepos);
	MPAN_display_var_icons (true, true);
	INTR_pattern_editor_track_ptr->refresh_dynamic (true);
}



/*==========================================================================*/
/*      Nom: MPAN_set_instrument_number                                     */
/*      Description: Change le numero de l'instrument courant de            */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                2 = Entree au clavier,                                    */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MPAN_set_instrument_number (int type, signed int value, int object)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		line_0 [5 + Instrument_NAME_LEN+1];
	char		nbr3_0 [3+1];
	Popup		instr_popup;

	new_value = GTK_instr_nbr;

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, INTR_base_song_3, new_value);
		EDIT_edit_string_base (nbr3_0, object, 3, INTR_base_song);
		new_value = (int) strtol (nbr3_0, NULL, INTR_base_song);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		for (int instr = 1; instr <= INST_NBRINSTR_MAXI; instr ++)
		{
			sprintf (line_0, INTR_base_song_3, instr);
			line_0 [3] = ':';
			line_0 [4] = ' ';
			INST_get_instr_name (instr, line_0 + 5);
			instr_popup.add_line (line_0, instr);
		}
		line = instr_popup.select_radio_by_code (new_value);
		code = instr_popup.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}

		break;
	}

	GTK_set_cur_instr (new_value);

	MPAN_display_var_icons (true, true);
	MPAN_display_divers ();
	if (   INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_INSTR
	    || INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_ENV)
	{
		INTR_pattern_editor_menu_ptr->refresh ();
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MPAN_set_sample_number                                         */
/*      Description: Change le numero du sample courant de                  */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                2 = Entree au clavier,                                    */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	MPAN_set_sample_number (int type, signed int value, int object)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		line_0 [5 + Sample_NAME_LEN+1];
	char		nbr3_0 [3+1];
	Popup		sample_popup;

	new_value = GTK_sample_nbr;

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, INTR_base_song_3, new_value);
		EDIT_edit_string_base (nbr3_0, object, 3, INTR_base_song);
		new_value = (int) strtol (nbr3_0, NULL, INTR_base_song);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		for (int sample = 1; sample <= SAMP_NBRSAMPLES_MAXI; sample ++)
		{
			sprintf (line_0, INTR_base_song_3, sample);
			line_0 [3] = ':';
			line_0 [4] = ' ';
			SAMP_get_sample_name (sample, line_0 + 5);
			sample_popup.add_line (line_0, sample);
		}
		line = sample_popup.select_radio_by_code (new_value);
		code = sample_popup.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}
		break;
	}

	GTK_set_cur_sample (new_value);

	switch (INTR_main_screen)
	{
	case	INTR_MAIN_SCREEN_PATTERN_EDITOR:
		MPAN_display_var_icons (true, true);
		if (INTR_pattern_editor_menu_ptr->get_submenu () == PatEdMenus_SPL)
		{
			INTR_pattern_editor_menu_ptr->refresh ();
		}
		break;

	case	INTR_MAIN_SCREEN_SAMPLE_EDITOR:
		INTR_sample_editor_interface_ptr->refresh ();
		break;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: MPAN_set_line_step                                             */
/*      Description: Change le pas d'avance de ligne                        */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	MPAN_set_line_step (int type, signed int value)
{
	signed long	result;
	int		lastline;
	int		line;
	Popup		line_step_popup;

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		if (GTK_line_step >= -255 && GTK_line_step <= 255)
		{
			GTK_line_step += value;
		}
		else
		{
			GTK_line_step = 1;
		}
		break;

	/* Entree par pop-up */
	case	INTR_CHGTYPE_POP:
		line_step_popup.add_line ("Next empty FX", INTR_LINE_STEP_NEF);
		line_step_popup.add_line ("Next volume", INTR_LINE_STEP_NV);
		line_step_popup.add_line ("Next FX", INTR_LINE_STEP_NF);
		line_step_popup.add_line ("Next note", INTR_LINE_STEP_NN);
		line_step_popup.add_line ("Next line taken", INTR_LINE_STEP_NL);
		line_step_popup.add_line ("1 line", 1);
		line_step_popup.add_line ("2 lines", 2);
		line_step_popup.add_line ("4 lines", 4);
		line_step_popup.add_line ("8 lines", 8);

		line = line_step_popup.select_radio_by_code (GTK_line_step);
		result = line_step_popup.manage (line);
		if (result >= 0)
		{
			GTK_line_step = (signed int) result;
		}
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		GTK_line_step = value;
		break;
	}

	lastline = MAX (PAT_get_current_pattern_height () - 1, 1);
	if (GTK_line_step >= -255 && GTK_line_step <= 255)
	{
		GTK_line_step = MIN (GTK_line_step, lastline);
		GTK_line_step = MAX (GTK_line_step, -lastline);
		GTK_line_step = MAX (GTK_line_step, -15);
	}
	else
	{
		GTK_line_step = MIN (GTK_line_step, 260);
		GTK_line_step = MAX (GTK_line_step, 256);
	}
	MPAN_display_var_icons (true, true);
}



/*==========================================================================*/
/*      Nom: MPAN_set_song_speed                                            */
/*      Description: Change la vitesse de defilement courante, de           */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entr‚e:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	MPAN_set_song_speed (int type, signed int value)
{
	int			new_value;
	char			nbr3_0 [3+1];

	Player &			player = Player::use_instance ();

	new_value = player.get_speed ();

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value += value;
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr3_0, INTR_base_speed_3, new_value);
		EDIT_edit_string_base (nbr3_0, RSC_OBJ_MP_VAR_SPD_VAL, 3, INTR_base_speed);
		new_value = (int) strtol (nbr3_0, NULL, INTR_base_speed);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;
	}

	new_value = MIN (new_value, Player::MAX_SPEED);
	new_value = MAX (new_value, Player::MIN_SPEED);
	player.set_speed (new_value);
	GTK_modified_flag = true;
	MPAN_display_var_icons (true, true);
}



/*==========================================================================*/
/*      Nom: MPAN_set_song_tempo                                            */
/*      Description: Change le tempo du module, de                          */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entr‚e:                                               */
/*        - type: INTR_CHGTYPE_ABS = Le parametre est une position absolue, */
/*                INTR_CHGTYPE_REL = Le parametre est une position relative,*/
/*                INTR_CHGTYPE_KBD = La position doit etre rentree au       */
/*                                   clavier.                               */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	MPAN_set_song_tempo (int type, double value)
{
	double	new_value;
	double	disp_val;
	char		nbr7_0 [7+1];

	Player &			player = Player::use_instance ();
	new_value = player.get_tempo ();

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		new_value = floor (new_value + value + 0.5);
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		disp_val = floor (new_value * 1000 + 0.5) / 1000;
		sprintf (nbr7_0, "%7.3f", disp_val);
		EDIT_edit_string (nbr7_0, RSC_OBJ_MP_VAR_TEMPO_VAL, 7, EditString_TYPE_FIX);
		sscanf (nbr7_0, "%lf", &new_value);
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_value = value;
		break;
	}

	new_value = MIN (new_value, Player::MAX_TEMPO);
	new_value = MAX (new_value, Player::MIN_TEMPO);
	player.set_tempo (new_value);
	GTK_modified_flag = true;
	MPAN_display_var_icons (true, true);
}



/****************************************************************************/
/*                                                                          */
/*      AFFICHAGES                                                          */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: MPAN_display_main_pannel                                       */
/*      Description: Affiche le panneau principal                           */
/*==========================================================================*/

void	MPAN_display_main_pannel (void)
{
	RSC_display_object (RSC_OBJ_MP_BKGD);		// Affiche le fond

	RSC_display_object (RSC_OBJ_MP_MICN);
	RSC_display_object (RSC_OBJ_MP_SMICN);
	RSC_display_object (RSC_OBJ_MP_VAR);
	RSC_display_object (RSC_OBJ_MP_SNGNAME);
	RSC_display_object (RSC_OBJ_MP_INSNAME);
	MPAN_display_var_icons (true, true);		// Actualise les variables
	MPAN_display_status (true);					// ligne de status
	MPAN_display_message (NULL);
	INTR_pattern_editor_menu_ptr->redraw ();	// Sous-menus
	INTR_pattern_editor_track_ptr->redraw ();
	TRK_display_mute ();								// Icones de pistes muettes
	TRK_display_presets ();							// Icones des presets des pistes
	MPAN_display_divers ();							// Divers
}



/*==========================================================================*/
/*      Nom: MPAN_display_status                                            */
/*      Description: Affiche les informations generales du soundtracker     */
/*      Parametres en entree:                                               */
/*        - disp_flag: true si on doit afficher les information maintenant. */
/*==========================================================================*/

void	MPAN_display_status (bool disp_flag)
{
	PatternTools_BLOCK_INFO	selection;
	char		string_0 [19+1];

	/* Octave */
	sprintf (string_0, "%1d", GTK_octave + 1);
	RSC_set_string (RSC_OBJ_MP_STATLIN_OCT_BKGND_DATA, string_0);

	/* Block */
	selection = GTK_pattern_block_ptr->get_current_selection ();
	sprintf (string_0, "%2d/%03X:%02X-%02X",
	         selection.track [TRK_preset_data [TRK_preset_nbr].track_type] + 1,
	         selection.pattern, selection.start, selection.end);
	RSC_set_string (RSC_OBJ_MP_STATLIN_BLK_BKGND_DATA, string_0);

	/* Pave numerique */
	KEYB_print_pad_mode (string_0);
	RSC_set_string (RSC_OBJ_MP_STATLIN_PAD_BKGND_DATA, string_0);

	/* Memoire libre */
	sprintf (string_0, "------");
	RSC_set_string (RSC_OBJ_MP_STATLIN_FMEM_BKGND_DATA, string_0);

	/* CPU */
	MPAN_display_cpu_time (false);

	/* Clipping */
	MPAN_display_clipping_level (false);

	if (disp_flag)
	{
		RSC_display_object (RSC_OBJ_MP_STATLIN);
	}
}



/*==========================================================================*/
/*      Nom: MPAN_display_cpu_time                                          */
/*      Description: Affiche seulement le temps CPU sur la barre de status. */
/*      Parametres en entree:                                               */
/*        - disp_flag: true si on doit afficher les information maintenant. */
/*==========================================================================*/

void	MPAN_display_cpu_time (bool disp_flag)
{
	char		string_0 [9+1];

	sprintf (string_0, "%3d", OS_cpu_time);
	RSC_set_string (RSC_OBJ_MP_STATLIN_CPU_BKGND_DATA, string_0);

	if (disp_flag)
	{
		RSC_display_object (RSC_OBJ_MP_STATLIN_CPU_BKGND);
	}
}



/*==========================================================================*/
/*      Nom: MPAN_display_clipping_level                                    */
/*      Description: Affiche seulement le niveau de clipping sur la barre   */
/*                   de status.                                             */
/*      Parametres en entree:                                               */
/*        - disp_flag: true si on doit afficher les information maintenant. */
/*==========================================================================*/

void	MPAN_display_clipping_level (bool disp_flag)
{
	int		track_cnt;
	float		level;
	float		max_level;
	char		string_0 [9+1];

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

	max_level = 0;
	for (track_cnt = 0; track_cnt < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track_cnt ++)
	{
		level = player.get_clipping_level (track_cnt);
		max_level = MAX (max_level, level);
	}
	max_level = 100 * max_level / float (0x00800000L);
	max_level = MIN (max_level, 999);

	/* Si on n'atteind pas 100 %, pas de clipping,
	   on affiche que tout est normal */
	if (max_level < 100)
	{
		RSC_set_string (RSC_OBJ_MP_STATLIN_CLIP_BKGND_DATA, " - ");
	}

	/* Clipping: on affiche le niveau de saturation */
	else
	{
		sprintf (string_0, "%3.0f", max_level);
		RSC_set_string (RSC_OBJ_MP_STATLIN_CLIP_BKGND_DATA, string_0);
	}

	if (disp_flag)
	{
		RSC_display_object (RSC_OBJ_MP_STATLIN_CLIP_BKGND);
	}
}



/*==========================================================================*/
/*      Nom: MPAN_display_message                                           */
/*      Description: Affiche un message...                                  */
/*      Parametres en entree:                                               */
/*        - message_0: pointeur sur le message, se terminant par 0.         */
/*                     S'il vaut NULL, le precedent message est simplement  */
/*                     reaffiche.                                           */
/*      Retour: 0 si aucune erreur ne s'est produite.                       */
/*==========================================================================*/

signed int	MPAN_display_message (const char *message_0)
{
	char		*string_0;

	if (message_0 != NULL)
	{
		string_0 = new char [RSC_info_line_len+1];
		strncpy (string_0, message_0, RSC_info_line_len);
		string_0 [RSC_info_line_len] = 0;
		RSC_set_string (RSC_OBJ_MP_INFLIN_TEXT, string_0);
		delete [] string_0;
	}

	RSC_display_object (RSC_OBJ_MP_INFLIN);

	return (0);
}



/*==========================================================================*/
/*      Nom: MPAN_display_var_icons                                         */
/*      Description: Met a jour les icones de variables et les affiche.     */
/*      Parametres en entree:                                               */
/*        - disp_flag: true si on doit afficher les nouvelles valeurs.      */
/*        - force_flag: true si on doit forcer le rafraichissement.         */
/*==========================================================================*/

void	MPAN_display_var_icons (bool disp_flag, bool force_flag)
{
	char			string_0 [255+1];
	static int	old_pos = -1;
	static int	old_pat = -1;
	static int	old_len = -1;
	static int	old_rep = -1;
	static int	old_ins = -1;
	static int	old_spl = -1;
	static int	old_lines = -1;
	static int	old_step = -1;
	static int	old_tempo = -1;
	static int	old_speed = -1;
	static int	old_clk_val = -1;
	int		pos;
	int		pat;
	int		len;
	int		rep;
	int		lines;
	int		tempo;
	int		speed;

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

	if (force_flag)
	{
		old_pos = -1;
		old_pat = -1;
		old_len = -1;
		old_rep = -1;
		old_ins = -1;
		old_spl = -1;
		old_lines = -1;
		old_step = -1;
		old_tempo = -1;
		old_speed = -1;
		old_clk_val = -1;
	}

	/* Position */
	pos = GTK_get_song_position ();
	if (pos != old_pos)
	{
		sprintf (string_0, INTR_base_song_3, pos);
		RSC_set_string (RSC_OBJ_MP_VAR_POS_VAL, string_0);
		old_pos = pos;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_POS_VAL);
		}
	}

	/* Pattern */
	pat = GTK_get_current_pattern_number ();
	if (pat != old_pat)
	{
		sprintf (string_0, INTR_base_song_3, pat);
		RSC_set_string (RSC_OBJ_MP_VAR_PAT_VAL, string_0);
		old_pat = pat;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_PAT_VAL);
		}
	}

	/* Song length */
	len = SONG_get_song_length ();
	if (len != old_len)
	{
		sprintf (string_0, INTR_base_song_3, len);
		RSC_set_string (RSC_OBJ_MP_VAR_LEN_VAL, string_0);
		old_len = len;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_LEN_VAL);
		}
	}

	/* Song repeat */
	rep = SONG_get_song_repeat ();
	if (rep != old_rep)
	{
		sprintf (string_0, INTR_base_song_3, rep);
		RSC_set_string (RSC_OBJ_MP_VAR_REP_VAL, string_0);
		old_rep = rep;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_REP_VAL);
		}
	}

	/* Instrument */
	if (GTK_instr_nbr != old_ins)
	{
		sprintf (string_0, INTR_base_song_3, GTK_instr_nbr);
		RSC_set_string (RSC_OBJ_MP_VAR_INST_VAL, string_0);
		old_ins = GTK_instr_nbr;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_INST_VAL);
		}
	}

	/* Sample */
	if (GTK_sample_nbr != old_spl)
	{
		sprintf (string_0, INTR_base_song_3, GTK_sample_nbr);
		RSC_set_string (RSC_OBJ_MP_VAR_SAMP_VAL, string_0);
		old_spl = GTK_sample_nbr;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_SAMP_VAL);
		}
	}

	/* Lines */
	lines = PAT_get_current_pattern_height ();
	if (lines != old_lines)
	{
		sprintf (string_0, INTR_base_lines_3, lines);
		RSC_set_string (RSC_OBJ_MP_VAR_LINE_VAL, string_0);
		old_lines = lines;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_LINE_VAL);
		}
	}

	/* Step */
	if (GTK_line_step != old_step)
	{
		if (GTK_line_step >= 0 && GTK_line_step < 256)
		{
			sprintf (string_0, INTR_base_lines_3, GTK_line_step);
		}
		else if (GTK_line_step >= -255 && GTK_line_step < 0)
		{
			*string_0 = '-';
			sprintf (string_0 + 1, INTR_base_lines_2, -GTK_line_step);
		}
		else
		{
			switch (GTK_line_step)
			{
			case	INTR_LINE_STEP_NL:
				sprintf (string_0, " NL");
				break;
			case	INTR_LINE_STEP_NN:
				sprintf (string_0, " NN");
				break;
			case	INTR_LINE_STEP_NF:
				sprintf (string_0, " NF");
				break;
			case	INTR_LINE_STEP_NV:
				sprintf (string_0, " NV");
				break;
			case	INTR_LINE_STEP_NEF:
				sprintf (string_0, "NEF");
				break;
			}
		}
		RSC_set_string (RSC_OBJ_MP_VAR_STEP_VAL, string_0);
		old_step = GTK_line_step;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_STEP_VAL);
		}
	}

	/* Tempo */
	tempo = (int) floor (player.get_tempo () + 0.0005);
	if (tempo != old_tempo)
	{
		sprintf (string_0, INTR_base_speed_3, tempo);
		RSC_set_string (RSC_OBJ_MP_VAR_TEMPO_VAL, string_0);
		old_tempo = tempo;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_TEMPO_VAL);
		}
	}

	/* Speed */
	speed = player.get_speed ();
	if (speed != old_speed)
	{
		sprintf (string_0, INTR_base_speed_3, speed);
		RSC_set_string (RSC_OBJ_MP_VAR_SPD_VAL, string_0);
		old_speed = speed;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_SPD_VAL);
		}
	}

	/* Clock */
	int				clk_min;
	int				clk_sec;
	int				clk_ms;
	player.get_elapsed_time (clk_min, clk_sec, clk_ms);
	if (clk_min > 99)
	{
		clk_min = 99;
		clk_sec = 59;
	}
	const long		clk_val = static_cast <long> (clk_min) * 60 + clk_sec;
	if (clk_val != old_clk_val)
	{
		sprintf (string_0, INTR_base_clock_min_sec, clk_min, clk_sec);
		RSC_set_string (RSC_OBJ_MP_VAR_CLK_VAL, string_0);
		old_clk_val = clk_val;
		if (disp_flag)
		{
			RSC_display_object (RSC_OBJ_MP_VAR_CLK_VAL);
		}
	}
}



/*==========================================================================*/
/*      Nom: MPAN_display_play_status                                       */
/*      Description: Affiche et reajuste les icones Play song/Play pat/Edit */
/*==========================================================================*/

void	MPAN_display_play_status (void)
{
	const Player &	player = Player::use_instance ();

	if (player.get_play_mode () == Player::MODE_SONG)
	{
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYSNG, true);
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYPAT, false);
	}
	else if (player.get_play_mode () == Player::MODE_PATTERN)
	{
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYSNG, false);
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYPAT, true);
	}
	else
	{
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYSNG, false);
		RSC_select_object_display (RSC_OBJ_MP_MICN_PLAYPAT, false);
	}

	RSC_select_object_display (RSC_OBJ_MP_MICN_EDIT, GTK_get_edit_status () != 0);

	INTR_graph_ptr->show_mouse ();
}



/*==========================================================================*/
/*      Nom: MPAN_display_divers                                            */
/*      Description: Affiches diverses donnees du panneau principal.        */
/*==========================================================================*/

void	MPAN_display_divers (void)
{
	char		songname_0 [SONG_NAME_LEN+1];
	char		instrname_0 [Instrument_NAME_LEN+1];

	/* Song name */
	SONG_get_song_name (songname_0);
	RSC_set_string (RSC_OBJ_MP_SNGNAME_NAME, songname_0);
	RSC_display_object (RSC_OBJ_MP_SNGNAME_NAME);

	BASE_trim_string (songname_0);
	if (strlen (songname_0) <= 0)
	{
		strcpy (songname_0, "(Untitled)");
	}
	OS_change_app_title (songname_0);

	/* Instrument name */
	INST_get_instr_name (GTK_instr_nbr, instrname_0);
	RSC_set_string (RSC_OBJ_MP_INSNAME_NAME, instrname_0);
	RSC_display_object (RSC_OBJ_MP_INSNAME_NAME);
}



/*==========================================================================*/
/*      Nom: MPAN_refresh_display                                           */
/*      Description: Rafraichit tous les affichages de variables            */
/*==========================================================================*/

void	MPAN_refresh_display (void)
{
	/* Pattern */
	INTR_pattern_editor_track_ptr->refresh ();

	/* Temps CPU */
	MPAN_display_cpu_time (true);

	/* Clipping */
	MPAN_display_clipping_level (true);

	/* Infos sur la partition */
	MPAN_display_var_icons (true, true);

	/* Infos des sous-menus */
	INTR_pattern_editor_menu_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: MPAN_refresh_dynamic_display                                   */
/*      Description: Rafraichit tous les affichages dynamiques.             */
/*==========================================================================*/

void	MPAN_refresh_dynamic_display (void)
{
	/* Pattern */
	INTR_pattern_editor_track_ptr->refresh_dynamic (false);

	/* Temps CPU */
	MPAN_display_cpu_time (true);

	/* Clipping */
	MPAN_display_clipping_level (true);

	/* Infos sur la partition */
	MPAN_display_var_icons (true, false);

	/* Infos des sous-menus */
	INTR_pattern_editor_menu_ptr->refresh_dynamic (false);
}



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



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