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

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	"archi.h"
#include	"base.h"
#include	"base_ct.h"
#include	"EditString.h"
#include	"edstring.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"log.h"
#include	"mixp.h"
#include	"MixPreset.h"
#include	"PatEdMenuMix.h"
#include	"player.h"
#include	"Popup.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"tracks.h"



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



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

/* Nom des types de pistes, en fonction egalement du dry/wet */
const char	*const PatEdMenuMix::track_type_name_0_ptr [5] [2] =
{
	{ "Samples", "Samples" },
	{ "Audio In", "Audio In" },
	{ "FX wet", "FX Dry" },
	{ "MIDI", "MIDI" },
	{ "AudioOut", "AudioOut" }
};



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

typedef struct
{
	BYTE		track_type;		/* Numero du type de piste source */
	int		dry_state;		/* Pour les pistes d'effet, 0 = wet, 1 = dry */
	WORD		track;			/* Voie d'entree */
	UWORD		volume;			/* 0..1000..FFFF */
	WORD		balance;			/* 0..800..FFF */
	int		stereo;
	int		active_state;
} PatEdMenuMix_SOURCE_CONF;



/*\\\ DEFINITION DES VARIABLES DE CLASSE PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ DEFINITION DES VARIABLES DE CLASSE PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ METHODES PUBLIQUES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise                                             */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*==========================================================================*/

PatEdMenuMix::PatEdMenuMix (void)
{
	const SLID_INFO	source_slider =
	{
		RSC_OBJ_MP_SUBM_MIX_IN_LIFT_SBAR,
		RSC_OBJ_MP_SUBM_MIX_IN_LIFT_SBAR_SLIDER,
		0, 0, 0, 0, 0, -1,
		SLID_DIRECTION_VERTICAL,
		false, false, 0, 0
	};

	_rsc_object_ptr = NULL;

	_source_slider = source_slider;
	_dest_track_type = GTK_TRACK_TYPE_AOU;
	_dest_track_nbr = 0;
	_dest_dry_flag = false;
	_disp_pos = 0;
	_nbr_disp_lines = 6;

	_rsc_object_ptr = new PatEdMenuMix_OBJECT_LINE [1];
	_rsc_object_ptr [0].vol = RSC_OBJ_MP_SUBM_MIX_IN_VOL_SL;
	_rsc_object_ptr [0].pan = RSC_OBJ_MP_SUBM_MIX_IN_PAN_SL;
	_nbr_rsc_lines = 1;
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit                                                */
/*==========================================================================*/

PatEdMenuMix::~PatEdMenuMix (void)
{
	if (_rsc_object_ptr != NULL)
	{
		delete [] _rsc_object_ptr;
		_rsc_object_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: check_ok                                                       */
/*      Description: Verifie l'intergrite de l'objet.                       */
/*      Retour: 0 si l'objet parait correct.                                */
/*==========================================================================*/

signed int	PatEdMenuMix::check_ok (void) const
{
	if (this == NULL)
	{
		LOG_printf ("PatEdMenuMix::check_ok: Error: \"this\" pointer is NULL.\n");
		return (-1);
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: self_display                                                   */
/*      Description: Affiche "textuellement" le contenu courant de l'objet. */
/*==========================================================================*/

void	PatEdMenuMix::self_display (void) const
{

	/*** A faire ***/

}



/*==========================================================================*/
/*      Nom: redraw                                                         */
/*      Description: Affiche la composante d'interface.                     */
/*==========================================================================*/

void	PatEdMenuMix::redraw (void)
{
	int		line;
	int		object;
	PatEdMenuMix_OBJECT_LINE	*temp_ptr;

	/* Hauteur de la fenetre des entrees */
	_nbr_disp_lines =   RSC_get_height (RSC_OBJ_MP_SUBM_MIX_IN_TYPE)
	                  / RSC_CHAR_H;

	/* Fabrique les objets de chaque ligne */
	if (_nbr_rsc_lines < _nbr_disp_lines)
	{
		temp_ptr = new PatEdMenuMix_OBJECT_LINE [_nbr_disp_lines];
		memcpy (temp_ptr, _rsc_object_ptr, sizeof (*temp_ptr) * _nbr_rsc_lines);
		delete [] _rsc_object_ptr;
		_rsc_object_ptr = temp_ptr;

		for (line = _nbr_rsc_lines; line < _nbr_disp_lines; line ++)
		{
			object = RSC_duplicate_object (_rsc_object_ptr [0].vol);
			_rsc_object_ptr [line].vol = object;
			RSC_set_relative_object_position (object, 0, line * RSC_CHAR_H);
			RSC_object_ptr [_rsc_object_ptr [line - 1].vol]->brother.number = object;

			object = RSC_duplicate_object (_rsc_object_ptr [0].pan);
			_rsc_object_ptr [line].pan = object;
			RSC_set_relative_object_position (object, 0, line * RSC_CHAR_H);
			RSC_object_ptr [_rsc_object_ptr [line - 1].pan]->brother.number = object;
		}

		_nbr_rsc_lines = _nbr_disp_lines;
	}

	clear_lines ();
	RSC_display_object (RSC_OBJ_MP_SUBM);

	refresh ();
}



/*==========================================================================*/
/*      Nom: refresh                                                        */
/*      Description: Rafraichit l'affichage des donnees.                    */
/*==========================================================================*/

void	PatEdMenuMix::refresh (void)
{
	char		nbr3_0 [3+1];
	char		preset_name_0 [MixPreset_NAME_LEN+1];

	/* Verifie que la piste destination existe bien */
	if (GTK_nbr_tracks [_dest_track_type] <= 0)
	{
		_dest_track_type = GTK_TRACK_TYPE_AOU;
		_dest_track_nbr = 0;
	}
	else
	{
		_dest_track_nbr = MIN (_dest_track_nbr, GTK_nbr_tracks [_dest_track_type]);
		_dest_track_nbr = MAX (_dest_track_nbr, 0);
	}

	/* Numero du preset */
	sprintf (nbr3_0, INTR_base_song_3, GTK_mix_nbr);
	RSC_set_string (RSC_OBJ_MP_SUBM_MIX_COM_PRESET_VAL, nbr3_0);
	RSC_display_object (RSC_OBJ_MP_SUBM_MIX_COM_PRESET_VAL);

	/* Nom du preset */
	MIXP_get_preset_name (GTK_mix_nbr, preset_name_0);
	RSC_set_string (RSC_OBJ_MP_SUBM_MIX_COM_PRESET_NAME, preset_name_0);
	RSC_display_object (RSC_OBJ_MP_SUBM_MIX_COM_PRESET_NAME);

	refresh_dynamic (true);
}



/*==========================================================================*/
/*      Nom: refresh_dynamic                                                */
/*      Description: Rafraichit l'affichage des donnees dynamiques.         */
/*        - force_flag: true indique qu'on doit forcer le rafraichissement. */
/*==========================================================================*/

void	PatEdMenuMix::refresh_dynamic (bool force_flag)
{
	/* Piste destination */
	update_dest_track (force_flag);

	/* Pistes sources */
	update_source_tracks (force_flag);
}



/*==========================================================================*/
/*      Nom: manage                                                         */
/*      Description: Gere la composante d'interface                         */
/*      Parametres en entree:                                               */
/*        - sel_object: objet detecte par le gestionnaire d'interface       */
/*        - sel_elder: aine de sel_object                                   */
/*==========================================================================*/

void	PatEdMenuMix::manage (int sel_object, int sel_elder)
{
	int		full_bar_width;
	int		bar_width;
	int		bar_hot_point;
	int		line;
	int		pos_x;
	int		track;
	int		track_type;
	int		nbr_tracks;
	int		volume;
	int		balance;
	int		index;
	int		real_col;
	int		bidon;
	int		stereo;
	signed int	ret_val;
	SLWORD	code;
	bool		dry_flag;
	bool		active_flag;
	PLAY_SOURCE_TRACK_CONF_BLOCK	*track_conf_ptr;
	PLAY_SOURCE_TRACK_CONF_BLOCK	track_conf;
	char		preset_name_0 [MixPreset_NAME_LEN+1];
	char		track_name_0 [TRK_NAME_LEN+1];
	Popup		popup_menu;

	Player &			player = Player::use_instance ();

	player.get_track_nbr_s_tracks (_dest_track_type,
	                             _dest_track_nbr,
	                             &nbr_tracks, &track_conf_ptr);

	switch (sel_object)
	{
	/* Insert */
	case	RSC_OBJ_MP_SUBM_MIX_COM_INS:
		real_col = TRK_cursor_offset + TRK_cursor_col;
		track_conf.track_type = (BYTE) TRK_preset_data [TRK_preset_nbr].track_type;
		track_conf.track_nbr = TRK_preset_data [TRK_preset_nbr].track_nbr [real_col];
		track_conf.wet_flag = true;
		track_conf.inpvol = 0x1000;
		track_conf.inppan = 0x8000U;
		if (track_conf.track_type == Pattern_TYPE_FX)
		{
			if (player.check_track_is_in_sources (_dest_track_type,
			                                    _dest_track_nbr,
			                                    &bidon, &track_conf) == 1)
			{
				track_conf.wet_flag = false;
			}
		}
		ret_val = player.add_source_track (_dest_track_type, _dest_track_nbr,
		                                 &track_conf, Player::MIX_PRESET_REPL);
		if (ret_val == 1)
		{
			INTR_dialog_box ("ADD SOURCE TRACK",
			                 "This track is already\n"
			                 "in the source tracks.",
			                 "Cancel", 0, 0);
		}
		else if (ret_val == 2)
		{
			INTR_dialog_box ("ADD SOURCE TRACK",
			                 "Adding this track may cause an\n"
			                 "infinite loop in the effect chain.",
			                 "Cancel", 0, 0);
		}
		else if (ret_val < 0)
		{
			INTR_dialog_box ("ADD SOURCE TRACK",
			                 "Error reported.",
			                 "Cancel", 0, 0);
		}
		else
		{
			GTK_modified_flag = true;
		}
		refresh ();
		break;

	/* Load */
	case	RSC_OBJ_MP_SUBM_MIX_COM_LOAD:
		if (player.load_mix_preset (_dest_track_type, _dest_track_nbr,
		                          GTK_mix_nbr, Player::MIX_PRESET_REPL))
		{
			INTR_dialog_box ("LOAD MIX PRESET", "Error reported.", "Cancel", 0, 0);
		}
		else
		{
			GTK_modified_flag = true;
		}
		refresh ();
		break;

	/* Save */
	case	RSC_OBJ_MP_SUBM_MIX_COM_SAVE:
		if (player.save_mix_preset (_dest_track_type, _dest_track_nbr,
		                          GTK_mix_nbr))
		{
			INTR_dialog_box ("SAVE MIX PRESET", "Error reported.", "Cancel", 0, 0);
		}
		refresh ();
		break;

	/* Preset (numero) */
	case	RSC_OBJ_MP_SUBM_MIX_COM_PRESET_VAL:
		if (RSC_mouse_key & 2)
		{
			set_mix_preset_number (INTR_CHGTYPE_POP, 0);
		}
		else
		{
			set_mix_preset_number (INTR_CHGTYPE_KBD, 0);
		}
		break;

	/* Preset (nom) */
	case	RSC_OBJ_MP_SUBM_MIX_COM_PRESET_NAME:
		MIXP_get_preset_name (GTK_mix_nbr, preset_name_0);
		if (EDIT_edit_string (preset_name_0, RSC_OBJ_MP_SUBM_MIX_COM_PRESET_NAME,
									 MixPreset_NAME_LEN, EditString_TYPE_ALPHA) == 0)
		{
			MIXP_set_preset_name (GTK_mix_nbr, preset_name_0);
			GTK_modified_flag = true;
		}
		refresh ();
		break;

	/* Preset (up) */
	case	RSC_OBJ_MP_SUBM_MIX_COM_PRESET_UP:
		set_mix_preset_number (INTR_CHGTYPE_REL, INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Preset (down) */
	case	RSC_OBJ_MP_SUBM_MIX_COM_PRESET_DOWN:
		set_mix_preset_number (INTR_CHGTYPE_REL, -INTR_inc_speed [0] [RSC_mouse_key]);
		break;

	/* Piste destination: type */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_TYPE:
		popup_menu.add_line ("Audio Out", GTK_TRACK_TYPE_AOU);
		popup_menu.add_line ("Effects, wet part", Pattern_TYPE_FX);
		popup_menu.add_line ("Effects, dry part", Pattern_TYPE_FX + 0x100);
		line = popup_menu.select_radio_by_code (  _dest_track_type
		                                        + (_dest_dry_flag ? 0x100 : 0x000));
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			track_type = code & 0xFF;
			dry_flag = ((code & 0xFF00) != 0);
			if (GTK_nbr_tracks [track_type] > 0)
			{
				_dest_track_type = track_type;
				_dest_dry_flag = dry_flag;
				_dest_track_nbr = MIN (_dest_track_nbr,
				                       GTK_nbr_tracks [_dest_track_type] - 1);
				_dest_track_nbr = MAX (_dest_track_nbr, 0);
				if (! player.check_track_validity (_dest_track_type, _dest_track_nbr))
				{
					_dest_track_nbr = 0;
				}
				_disp_pos = 0;
				refresh ();
			}
		}
		break;

	/* Piste destination: piste */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_TRACK:
		if (RSC_mouse_key & 2)
		{
			if (set_dest_track (INTR_CHGTYPE_POP, 0))
			{
				LOG_printf ("PatEdMenuMix::manage: Error: couldn't change destination track (pop-up menu).\n");
			}
		}
		else
		{
			if (set_dest_track (INTR_CHGTYPE_KBD, 0))
			{
				LOG_printf ("PatEdMenuMix::manage: Error: couldn't change destination track (keyboard).\n");
			}
		}
		break;

	/* Piste destination: nom de la piste */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_TNAME:
		player.get_track_name (_dest_track_type,
		                     _dest_track_nbr, track_name_0);
		if (EDIT_edit_string (track_name_0, RSC_OBJ_MP_SUBM_MIX_OUT_TNAME,
									 TRK_NAME_LEN, EditString_TYPE_ALPHA) == 0)
		{
			player.set_track_name (_dest_track_type,
			                     _dest_track_nbr, track_name_0);
			GTK_modified_flag = true;
		}
		refresh ();
		break;

	/* Piste destination: stereo */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_STEREO:
		if (! _dest_dry_flag)
		{
			stereo = player.get_track_stereo (_dest_track_type, _dest_track_nbr, false);
			if (stereo == 1)
			{
				stereo = 2;
			}
			else
			{
				stereo = 1;
			}
			ret_val = player.set_track_stereo (_dest_track_type, _dest_track_nbr, stereo);
			if (ret_val < 0)
			{
				INTR_dialog_box ("SET DESTINATION TRACK STEREO",
				                 "Error: failed to change track stereo.",
				                 "Cancel", 0, 0);
			}
			else if (ret_val == 1)
			{
				INTR_dialog_box ("SET DESTINATION TRACK STEREO",
				                 "This track can't have its stereo changed.",
				                 "Cancel", 0, 0);
			}
			else
			{
				GTK_modified_flag = true;
			}
			refresh ();
		}
		break;

	/* Piste destination: mute */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_MUTE:
		if (_dest_track_type != GTK_TRACK_TYPE_AOU)
		{
			active_flag = ! player.get_track_onoff (_dest_track_type,
			                                      _dest_track_nbr,
			                                      _dest_dry_flag);
			player.set_track_onoff (_dest_track_type, _dest_track_nbr,
			                      _dest_dry_flag, active_flag);
			INTR_wait_mouse (true);
			TRK_display_mute ();
		}
		break;

	/* Piste destination: volume */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_VOL:
		pos_x = INTR_mouse.x - RSC_absolute_object_pixxpos [sel_object];
		full_bar_width = RSC_get_width (sel_object);
		volume = ((long)pos_x * 0x600) / full_bar_width + 0x800;
		volume = MIN (volume, 0xFFF);
		volume = MAX (volume, 0x000);
		if (_dest_track_type == GTK_TRACK_TYPE_AOU)
		{
			player.set_log_master_volume (volume + 0x400);
		}
		else
		{
			player.set_track_log_volume (_dest_track_type,
			                           _dest_track_nbr,
			                           _dest_dry_flag, volume);
		}
		update_dest_track (false);
		GTK_modified_flag = true;
		break;

	/* Piste destination: panning */
	case	RSC_OBJ_MP_SUBM_MIX_OUT_PAN:
		if (_dest_track_type != GTK_TRACK_TYPE_AOU)
		{
			pos_x = INTR_mouse.x - RSC_absolute_object_pixxpos [sel_object];
			full_bar_width = RSC_get_width (sel_object);
			bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
			bar_hot_point = (bar_width - 1) / 2;
			balance = ((long)(pos_x - bar_hot_point) * 0x10000) / (full_bar_width - bar_width);
			balance = MIN (balance, 0xFFFF);
			balance = MAX (balance, 0);
			player.set_track_panning (_dest_track_type,
			                        _dest_track_nbr,
			                        _dest_dry_flag, balance);
			update_dest_track (false);
			GTK_modified_flag = true;
		}
		break;

	/* Piste source: type */
	case	RSC_OBJ_MP_SUBM_MIX_IN_TYPE:
		line = (INTR_mouse.y - RSC_absolute_object_pixypos [sel_object]) / RSC_CHAR_H;
		index = line + _disp_pos;
		if (index < nbr_tracks)
		{
			/* Clic droit: effacement */
			if (RSC_mouse_key & 2)
			{
				if (nbr_tracks > 1)
				{
					if (INTR_dialog_box ("REMOVE SOURCE TRACK",
					                     "Do you really want to\n"
					                     "remove this track form\n"
					                     "the sources \?",
					                     "OK\nCancel", 0, 1) == 0)
					{
						track_conf = track_conf_ptr [index];
						player.remove_source_track (_dest_track_type,
						                          _dest_track_nbr,
						                          &track_conf);
						GTK_modified_flag = true;
					}
				}
				else
				{
					INTR_dialog_box ("REMOVE SOURCE TRACK",
					                 "This track can't be removed.\n"
					                 "Number of source tracks can't be 0.",
					                 "Cancel", 0, 0);
				}
				refresh ();
			}

			/* Clic gauche: changement du type de la piste source */
			else
			{
				set_source_track_type (INTR_CHGTYPE_POP, 0, line);
			}
		}
		break;

	/* Piste source: piste */
	case	RSC_OBJ_MP_SUBM_MIX_IN_TRACK:
		line = (INTR_mouse.y - RSC_absolute_object_pixypos [sel_object]) / RSC_CHAR_H;
		index = line + _disp_pos;
		if (index < nbr_tracks)
		{
			if (RSC_mouse_key & 2)
			{
				set_source_track (INTR_CHGTYPE_POP, 0, line);
			}
			else
			{
				set_source_track (INTR_CHGTYPE_KBD, 0, line);
			}
		}
		break;

	/* Piste source: nom de la piste */
	case	RSC_OBJ_MP_SUBM_MIX_IN_TNAME:
		line = (INTR_mouse.y - RSC_absolute_object_pixypos [sel_object]) / RSC_CHAR_H;
		index = line + _disp_pos;
		if (index < nbr_tracks)
		{
			track_conf_ptr += index;
			track = track_conf_ptr->track_nbr;
			track_type = track_conf_ptr->track_type;
			player.get_track_name (track_type, track, track_name_0);
			RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT,
			                                  0, line * RSC_CHAR_H);
			if (EDIT_edit_string (track_name_0, RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT,
										 TRK_NAME_LEN, EditString_TYPE_ALPHA) == 0)
			{
				player.set_track_name (track_type, track, track_name_0);
				GTK_modified_flag = true;
			}
			refresh ();
		}
		break;

	/* Piste source: stereo */
	case	RSC_OBJ_MP_SUBM_MIX_IN_STEREO:
		line = (INTR_mouse.y - RSC_absolute_object_pixypos [sel_object]) / RSC_CHAR_H;
		index = line + _disp_pos;
		if (index < nbr_tracks)
		{
			track_conf_ptr += index;
			track = track_conf_ptr->track_nbr;
			track_type = track_conf_ptr->track_type;
			stereo = player.get_track_stereo (track_type, track, false);
			if (stereo == 1)
			{
				stereo = 2;
			}
			else
			{
				stereo = 1;
			}
			ret_val = player.set_track_stereo (track_type, track, stereo);
			if (ret_val < 0)
			{
				INTR_dialog_box ("SET SOURCE TRACK STEREO",
				                 "Error: failed to change track stereo.",
				                 "Cancel", 0, 0);
			}
			else if (ret_val == 1)
			{
				INTR_dialog_box ("SET SOURCE TRACK STEREO",
				                 "This track can't have its stereo changed.",
				                 "Cancel", 0, 0);
			}
			else
			{
				GTK_modified_flag = true;
			}
			refresh ();
		}
		break;

	/* Piste source: mute */
	case	RSC_OBJ_MP_SUBM_MIX_IN_MUTE:
		line = (INTR_mouse.y - RSC_absolute_object_pixypos [sel_object]) / RSC_CHAR_H;
		index = line + _disp_pos;
		if (index < nbr_tracks)
		{
			track_conf_ptr += index;
			track = track_conf_ptr->track_nbr;
			track_type = track_conf_ptr->track_type;
			dry_flag = ! track_conf_ptr->wet_flag && track_type == Pattern_TYPE_FX;
			active_flag = ! player.get_track_onoff (track_type, track, dry_flag);
			player.set_track_onoff (track_type, track, dry_flag, active_flag);
			INTR_wait_mouse (true);
			TRK_display_mute ();
			refresh ();
		}
		break;

	/* Ascenseur */
	case	RSC_OBJ_MP_SUBM_MIX_IN_LIFT_SBAR:
	case	RSC_OBJ_MP_SUBM_MIX_IN_LIFT_SBAR_SLIDER:
		SLID_gere_slider (&_source_slider, sel_object);
		if (_source_slider.changed_pos_flag)
		{
			_disp_pos = MIN (_source_slider.virtual_pos,
			                 nbr_tracks - _nbr_disp_lines);
			_disp_pos = MAX (_disp_pos, 0);
			update_source_tracks (true);
		}
		break;

	/* Ascenseur, up */
	case	RSC_OBJ_MP_SUBM_MIX_IN_LIFT_UP:
		_disp_pos --;
		_disp_pos = MIN (_disp_pos,
		                 nbr_tracks - _nbr_disp_lines);
		_disp_pos = MAX (_disp_pos, 0);
		update_source_tracks (true);
		break;

	/* Ascenseur, down */
	case	RSC_OBJ_MP_SUBM_MIX_IN_LIFT_DOWN:
		_disp_pos ++;
		_disp_pos = MIN (_disp_pos,
		                 nbr_tracks - _nbr_disp_lines);
		_disp_pos = MAX (_disp_pos, 0);
		update_source_tracks (true);
		break;

	default:
		for (line = 0; line < _nbr_disp_lines; line ++)
		{
			index = line + _disp_pos;
			if (index < nbr_tracks)
			{
				/* Volume */
				if (sel_object == _rsc_object_ptr [line].vol)
				{
					track_conf_ptr += index;
					track = track_conf_ptr->track_nbr;
					track_type = track_conf_ptr->track_type;
					dry_flag = ! track_conf_ptr->wet_flag && track_type == Pattern_TYPE_FX;
					pos_x = INTR_mouse.x - RSC_absolute_object_pixxpos [sel_object];
					full_bar_width = RSC_get_width (sel_object);
					volume = ((long)pos_x * 0x600) / full_bar_width + 0x800;
					volume = MIN (volume, 0xFFF);
					volume = MAX (volume, 0x000);
					if (_dest_track_type == GTK_TRACK_TYPE_AOU)
					{
						player.set_track_log_volume (track_type, track, dry_flag, volume);
					}
					else
					{
						track_conf_ptr->inpvol = Player::log_to_lin_volume (volume);
					}
					GTK_modified_flag = true;
					update_source_tracks (false);
					break;	// Sorite du for (line)
				}

				/* Panning */
				if (sel_object == _rsc_object_ptr [line].pan)
				{
					track_conf_ptr += index;
					track = track_conf_ptr->track_nbr;
					track_type = track_conf_ptr->track_type;
					dry_flag = ! track_conf_ptr->wet_flag && track_type == Pattern_TYPE_FX;
					pos_x = INTR_mouse.x - RSC_absolute_object_pixxpos [sel_object];
					full_bar_width = RSC_get_width (sel_object);
					bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
					bar_hot_point = (bar_width - 1) / 2;
					balance = ((long)(pos_x - bar_hot_point) * 0x10000) / (full_bar_width - bar_width);
					balance = MIN (balance, 0xFFFF);
					balance = MAX (balance, 0);
					if (_dest_track_type == GTK_TRACK_TYPE_AOU)
					{
						player.set_track_panning (track_type, track, dry_flag, balance);
					}
					else
					{
						track_conf_ptr->inppan = balance;
					}
					GTK_modified_flag = true;
					update_source_tracks (false);
					break;	// Sorite du for (line)
				}
			}

		}
		break;
	}
}



/*==========================================================================*/
/*      Nom: activate                                                       */
/*      Description: Active ou desactive le menu. Celui-ci n'est pas        */
/*                   reaffiche.                                             */
/*      Parametres en entree:                                               */
/*        - activated_flag: indique si le menu doit etre active ou          */
/*                          desactive.                                      */
/*==========================================================================*/

void	PatEdMenuMix::activate (bool activated_flag)
{
	RSC_pos_flag (RSC_OBJ_MP_SUBM_MIX, RSC_ATTR_NOTDISP, ! activated_flag);
	RSC_pos_flag (RSC_OBJ_MP_SMICN_MIX, RSC_ATTR_SELECTED, activated_flag);
}



/*\\\ METHODES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



void	PatEdMenuMix::clear_lines (void)
{
	for (int line = 0; line < _nbr_rsc_lines; line ++)
	{
		RSC_set_flag (_rsc_object_ptr [line].vol, RSC_ATTR_NOTDISP);
		RSC_set_flag (_rsc_object_ptr [line].pan, RSC_ATTR_NOTDISP);
	}
}



void	PatEdMenuMix::update_dest_track (bool force_flag)
{
	static signed int	old_track = -1;
	static signed int	old_track_type = -1;
	static signed int	old_balance = -1;
	static signed long	old_volume = -1;
	static int	old_active_state = -1;
	static signed int	old_stereo = -1;
	static int	old_dry_state = -1;

	int		full_bar_width;
	int		bar_width;
	int		bar_hot_point;
	int		bar_pos;
	int		stereo;
	bool		active_flag;
	long		volume;
	long		balance;
	char		nbr2_0 [2+1];
	char		nbr3_0 [3+1];
	char		name_0 [TRK_NAME_LEN+1];

	if (force_flag)
	{
		old_track = -1;
		old_track_type = -1;
		old_dry_state = -1;
	}

	if (   old_track != _dest_track_nbr
	    || old_track_type != _dest_track_type
		 || old_dry_state != (_dest_dry_flag ? 1 : 0))
	{
		old_track = -1;
		old_track_type = -1;
		old_volume = -1;
		old_balance = -1;
		old_active_state = -1;
		old_dry_state = -1;
		old_stereo = -1;
	}

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

	/* Type de piste */
	if (   _dest_track_type != old_track_type
	    || old_dry_state != (_dest_dry_flag ? 1 : 0))
	{
		old_track_type = _dest_track_type;
		old_dry_state = _dest_dry_flag ? 1 : 0;
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_OUT_TYPE,
		                track_type_name_0_ptr [_dest_track_type]
		                                      [_dest_dry_flag ? 1 : 0]);
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_TYPE);
	}

	/* Numero et nom de piste */
	if (_dest_track_nbr != old_track)
	{
		old_track = _dest_track_nbr;

		/* Numero */
		sprintf (nbr2_0, INTR_base_track_2, _dest_track_nbr + 1);
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_OUT_TRACK, nbr2_0);
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_TRACK);

		/* Nom */
		player.get_track_name (_dest_track_type,
		                     _dest_track_nbr, name_0);
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_OUT_TNAME, name_0);
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_TNAME);
	}

	/* Stereo */
	stereo = player.get_track_stereo (_dest_track_type, _dest_track_nbr, _dest_dry_flag);
	if (stereo != old_stereo)
	{
		old_stereo = stereo;
		sprintf (nbr3_0, " %1d ", stereo);
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_OUT_STEREO, nbr3_0);
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_STEREO);
	}

	/* Mute */
	active_flag =    player.get_track_onoff (_dest_track_type,
	                                       _dest_track_nbr,
	                                       _dest_dry_flag)
	              || _dest_track_type == GTK_TRACK_TYPE_AOU;
	if ((active_flag ? 1 : 0) != old_active_state)
	{
		old_active_state = active_flag ? 1 : 0;
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_OUT_MUTE,
		                active_flag ? "   " : " X ");
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_MUTE);
	}

	/* Volume */
	volume = player.get_track_log_volume (_dest_track_type,
	                                    _dest_track_nbr,
	                                    _dest_dry_flag);
	if (volume != old_volume)
	{
		old_volume = volume;

		full_bar_width = RSC_get_width (RSC_OBJ_MP_SUBM_MIX_OUT_VOL);

		/* Fond */
		RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_VUMETRE_H_DARK), 0, 0,
		                   full_bar_width, RSC_CHAR_H,
		                   RSC_OBJ_MP_SUBM_MIX_OUT_VOL, 0, 0);

		volume = MAX (volume - 0x800, 0);
		volume = MIN (volume, 0x600);
		bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
		bar_hot_point = (bar_width - 1) / 2;
		bar_pos = (int) ((long)volume * full_bar_width / 0x600);
		bar_pos = MIN (bar_pos, full_bar_width - bar_width + bar_hot_point);
		bar_pos = MAX (bar_pos, bar_hot_point);

		/* Potentiometre */
		RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G), 0, 0,
		                   bar_width, RSC_CHAR_H,
		                   RSC_OBJ_MP_SUBM_MIX_OUT_VOL, bar_pos - bar_hot_point, 0,
		                   255);
	}

	/* Balance */
	balance = player.get_track_panning (_dest_track_type,
	                                  _dest_track_nbr,
	                                  _dest_dry_flag) >> 4;
	if (_dest_track_type == GTK_TRACK_TYPE_AOU)
	{
		balance = 0;
	}

	if (old_balance != balance)
	{
		old_balance = balance;

		if (_dest_track_type == GTK_TRACK_TYPE_AOU)
		{
			RSC_display_object (RSC_OBJ_MP_SUBM_MIX_OUT_PAN);
		}

		else
		{
			full_bar_width = RSC_get_width (RSC_OBJ_MP_SUBM_MIX_OUT_PAN);

			/* Fond */
			RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_PANMETRE_112), 0, 0,
			                   full_bar_width, RSC_CHAR_H,
			                   RSC_OBJ_MP_SUBM_MIX_OUT_PAN, 0, 0);
			
			/* Potentiometre */
			bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
			bar_pos = (int) (balance * (full_bar_width - bar_width + 1) / 0x1000L);
			RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G), 0, 0,
			                   bar_width, RSC_CHAR_H,
			                   RSC_OBJ_MP_SUBM_MIX_OUT_PAN, bar_pos, 0,
			                   255);
		}
	}
}



void	PatEdMenuMix::update_source_tracks (bool force_flag)
{
	/* Les volumes sont ici stoques sous forme logarithmique 0..C00..FFF */
	static PatEdMenuMix_SOURCE_CONF	old_track [GTK_NBRTRACKS_MAXI];
	static int	old_nbr_tracks = 0;
	static signed int	old_disp_pos = -1;

	int		nbr_tracks;
	int		line;
	int		current_pos;
	int		current_disp_line;
	int		track_type;
	int		track;
	long		volume;
	long		balance;
	int		stereo;
	int		full_bar_width;
	int		bar_width;
	int		bar_pos;
	int		bar_hot_point;
	int		max_len;
	bool		active_flag;
	bool		dry_flag;
	PatEdMenuMix_SOURCE_CONF	*old_track_ptr;
	PLAY_SOURCE_TRACK_CONF_BLOCK	*track_conf_ptr;
	char		nbr2_0 [2+1];
	char		nbr3_0 [3+1];
	char		name_0 [TRK_NAME_LEN+1];

	if (force_flag)
	{
		old_nbr_tracks = 0;
	}

	Player &			player = Player::use_instance ();

	/* Recupere l'adresse des informations de la piste destination courante */
	player.get_track_nbr_s_tracks (_dest_track_type,
	                             _dest_track_nbr,
	                             &nbr_tracks, &track_conf_ptr);

	_disp_pos = MIN (_disp_pos, nbr_tracks - _nbr_disp_lines);
	_disp_pos = MAX (_disp_pos, 0);

	/* Ascenseur */
	if (   old_nbr_tracks != nbr_tracks
	    || old_disp_pos != _disp_pos)
	{
		_source_slider.virtual_len = nbr_tracks;
		_source_slider.virtual_pos = _disp_pos;
		_source_slider.virtual_win = _nbr_disp_lines;
		SLID_display_slider (&_source_slider);
	}
	
	if (_disp_pos != old_disp_pos)
	{
		old_disp_pos = _disp_pos;
		old_nbr_tracks = 0;
	}

	/* Effacage du fond */
	if (nbr_tracks < old_nbr_tracks || old_nbr_tracks <= 0)
	{
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TYPE_TEXT, "");
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT, "");
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT, "");
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_STEREO_TEXT, "");
		RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_MUTE_TEXT, "");
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TYPE_TEXT, 0, 0);
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT, 0, 0);
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT, 0, 0);
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_STEREO_TEXT, 0, 0);
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_MUTE_TEXT, 0, 0);
		clear_lines ();
		RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN);
		old_nbr_tracks = 0;
	}

	current_pos = _disp_pos;
	current_disp_line = 0;
	track_conf_ptr += current_pos;
	old_track_ptr = old_track + current_pos;
	for (line = 0; line < _nbr_disp_lines; line ++)
	{
		/* Update de la ligne */
		if (current_pos < nbr_tracks)
		{
			track_type = track_conf_ptr->track_type;
			dry_flag =    ! track_conf_ptr->wet_flag
			           && track_type == Pattern_TYPE_FX;
			track = track_conf_ptr->track_nbr;
			volume = Player::lin_to_log_volume (track_conf_ptr->inpvol);
			balance = track_conf_ptr->inppan;

			if (current_pos >= old_nbr_tracks)
			{
				old_track_ptr->track_type = -1;
			}

			/* type de piste */
			if (   track_type != old_track_ptr->track_type
			    || (dry_flag ? 1 : 0) != old_track_ptr->dry_state)
			{
				old_track_ptr->track_type = track_type;
				old_track_ptr->dry_state = dry_flag ? 1 : 0;
				old_track_ptr->track = -1;
				old_track_ptr->volume = 0xFFFF;
				old_track_ptr->balance = 0x1000;
				old_track_ptr->stereo = -1;
				old_track_ptr->active_state = -1;

				RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TYPE_TEXT,
				                track_type_name_0_ptr [track_type]
				                                      [dry_flag ? 1 : 0]);
				RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TYPE_TEXT,
				                                  0, current_disp_line);
				RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN_TYPE_TEXT);
			}

			/* Numero et nom de piste */
			if (old_track_ptr->track != track)
			{
				old_track_ptr->track = track;

				/* Numero */
				sprintf (nbr2_0, INTR_base_track_2, track + 1);
				RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT, nbr2_0);
				RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT,
				                                  0, current_disp_line);
				RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT);

				/* Nom */
				player.get_track_name (track_type, track, name_0);
				max_len = RSC_get_width (RSC_OBJ_MP_SUBM_MIX_IN_TNAME) / RSC_CHAR_W;
				if (max_len < TRK_NAME_LEN)
				{
					name_0 [max_len] = '\0';
				}
				RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT, name_0);
				RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT,
				                                  0, current_disp_line);
				RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN_TNAME_TEXT);
			}

			/* Stereo */
			stereo = player.get_track_stereo (track_type, track, dry_flag);
			if (stereo != old_track_ptr->stereo)
			{
				old_track_ptr->stereo = stereo;
				sprintf (nbr3_0, " %1d ", stereo);
				RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_STEREO_TEXT, nbr3_0);
				RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_STEREO_TEXT,
				                                  0, current_disp_line);
				RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN_STEREO_TEXT);
			}


			/* Mute */
			active_flag = player.get_track_onoff (track_type, track, dry_flag);
			if (old_track_ptr->active_state != (active_flag ? 1 : 0))
			{
				old_track_ptr->active_state = active_flag ? 1 : 0;
				RSC_set_string (RSC_OBJ_MP_SUBM_MIX_IN_MUTE_TEXT,
									 active_flag ? "   " : " X ");
				RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_MUTE_TEXT,
				                                  0, current_disp_line);
				RSC_display_object (RSC_OBJ_MP_SUBM_MIX_IN_MUTE_TEXT);
			}

			/* Volume */
			RSC_clear_flag (_rsc_object_ptr [line].vol, RSC_ATTR_NOTDISP);
			if (_dest_track_type == GTK_TRACK_TYPE_AOU)
			{
				volume = player.get_track_log_volume (track_type, track, dry_flag);
			}
			else
			{
				volume = Player::lin_to_log_volume (track_conf_ptr->inpvol);
			}
			if (old_track_ptr->volume != volume)
			{
				old_track_ptr->volume = (UWORD)volume;

				full_bar_width = RSC_get_width (RSC_OBJ_MP_SUBM_MIX_IN_VOL);
				volume = MAX (volume - 0x800, 0);
				volume = MIN (volume, 0x600);

				/* Fond */
				RSC_display_object (_rsc_object_ptr [line].vol);

				/* Potentiometre */
				bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
				bar_hot_point = (bar_width - 1) / 2;
				bar_pos = (int) ((long)volume * full_bar_width / 0x600);
				bar_pos = MIN (bar_pos, full_bar_width - bar_width + bar_hot_point);
				bar_pos = MAX (bar_pos, bar_hot_point);

				RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G), 0, 0,
				                   bar_width, RSC_CHAR_H,
				                   _rsc_object_ptr [line].vol, bar_pos - bar_hot_point,
				                   0, 255);
			}

			/* Panning */
			RSC_clear_flag (_rsc_object_ptr [line].pan, RSC_ATTR_NOTDISP);
			if (_dest_track_type == GTK_TRACK_TYPE_AOU)
			{
				balance = player.get_track_panning (track_type, track, dry_flag) / 0x10;
			}
			else
			{
				balance = track_conf_ptr->inppan / 0x10;
			}
			if (old_track_ptr->balance != balance)
			{
				old_track_ptr->balance = (WORD)balance;

				full_bar_width = RSC_get_width (RSC_OBJ_MP_SUBM_MIX_IN_PAN);
				
				/* Fond */
				RSC_display_object (_rsc_object_ptr [line].pan);
				
				/* Potentiometre */
				bar_width = RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G)->get_width ();
				bar_pos = (int) (balance * (full_bar_width - bar_width + 1) / 0x1000L);

				RSC_display_image (RSC_get_gfx_ptr (RSC_GFX_KNOB_1_H_G), 0, 0,
				                   bar_width, RSC_CHAR_H,
				                   _rsc_object_ptr [line].pan, bar_pos, 0, 255);
			}
		}

		current_pos ++;
		current_disp_line += RSC_CHAR_H;
		track_conf_ptr ++;
		old_track_ptr ++;
	}
	
	old_nbr_tracks = nbr_tracks;

	INTR_graph_ptr->show_mouse ();
}



/*==========================================================================*/
/*      Nom: set_mix_preset_number                                          */
/*      Description: Change le numero du preset de mixage 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	PatEdMenuMix::set_mix_preset_number (int type, signed int value)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		line_0 [5 + MixPreset_NAME_LEN+1];
	char		nbr3_0 [3+1];
	Popup		popup_menu;

	new_value = GTK_mix_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, RSC_OBJ_MP_SUBM_MIX_COM_PRESET_VAL, 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 count = 1; count <= MIXP_NBRMIXP_MAXI; count ++)
		{
			sprintf (line_0, INTR_base_song_3, count);
			line_0 [3] = ':';
			line_0 [4] = ' ';
			MIXP_get_preset_name (count, line_0 + 5);
			popup_menu.add_line (line_0, count);
		}

		line = popup_menu.select_radio_by_code (new_value);
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}
		break;
	}

	new_value = MIN (new_value, MIXP_NBRMIXP_MAXI);
	new_value = MAX (new_value, 1);
	GTK_mix_nbr = new_value;

	refresh ();

	return (0);
}



/*==========================================================================*/
/*      Nom: set_dest_track                                                 */
/*      Description: Change le numero de piste destination 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	PatEdMenuMix::set_dest_track (int type, signed int value)
{
	int		new_value;
	signed int	line;
	signed long	code;
	char		nbr2_0 [2+1];
	char		line_0 [4 + TRK_NAME_LEN+1];
	int		stereo;
	Popup		popup_menu;

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

	new_value = _dest_track_nbr;

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

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		sprintf (nbr2_0, INTR_base_track_2, new_value + 1);
		EDIT_edit_string_base (nbr2_0, RSC_OBJ_MP_SUBM_MIX_OUT_TRACK, 2, INTR_base_track);
		new_value = (int) strtol (nbr2_0, NULL, INTR_base_track) - 1;
		break;

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

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		for (int track = 0; track < GTK_nbr_tracks [_dest_track_type]; track ++)
		{
			sprintf (line_0, INTR_base_track_2, track + 1);
			line_0 [2] = ':';
			line_0 [3] = ' ';
			player.get_track_name (_dest_track_type, track, line_0 + 4);
			popup_menu.add_line (line_0, track);
		}

		if (_dest_track_type == GTK_TRACK_TYPE_AOU)
		{
			for (int track = 0; track < GTK_nbr_tracks [GTK_TRACK_TYPE_AOU]; track ++)
			{
				stereo = player.get_track_stereo (GTK_TRACK_TYPE_AOU, track, false) - 1;
				while (stereo > 0)
				{
					track ++;
					if (popup_menu.disable_by_code (track, true) < 0)
					{
						LOG_printf ("PatEdMenuMix::set_dest_track: Warning: line of Audio Out track # %d not found in pop-up menu.\n",
						            track);
					}
					stereo --;
				}
			}
		}

		line = popup_menu.select_radio_by_code (_dest_track_nbr);
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			new_value = (int) code;
		}
		break;
	}

	new_value = MIN (new_value, GTK_nbr_tracks [_dest_track_type] - 1);
	new_value = MAX (new_value, 0);
	if (player.check_track_validity (_dest_track_type, new_value))
	{
		_dest_track_nbr = new_value;
		_disp_pos = 0;
	}

	refresh ();

	return (0);
}



/*==========================================================================*/
/*      Nom: set_source_track_type                                          */
/*      Description: Change le type de piste source de                      */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*                   Le numero de piste source peut eventuellement etre     */
/*                   change.                                                */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*                3 = Entree au pop-up.                                     */
/*        - value: le parametre en question.                                */
/*        - win_line: numero de la ligne de la fenetre sur laquelle on a    */
/*                    clique.                                               */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	PatEdMenuMix::set_source_track_type (int type, signed int value, int win_line)
{
	signed int	ret_val;
	signed int	line;
	signed long	code;
	int		index;
	int		nbr_tracks;
	int		old_value;
	PLAY_SOURCE_TRACK_CONF_BLOCK	*track_conf_ptr;
	PLAY_SOURCE_TRACK_CONF_BLOCK	new_track_conf;
	Popup		popup_menu;

	GTK_mutex.wait ();

	Player &			player = Player::use_instance ();

	player.get_track_nbr_s_tracks (_dest_track_type,
	                             _dest_track_nbr,
	                             &nbr_tracks, &track_conf_ptr);
	index = win_line + _disp_pos;
	new_track_conf = track_conf_ptr [index];
	new_track_conf.wet_flag =    new_track_conf.wet_flag
	                          || new_track_conf.track_type != Pattern_TYPE_FX;

	old_value = new_track_conf.track_type;

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

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_track_conf.track_type = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		popup_menu.add_line ("Samples", Pattern_TYPE_SPL);
		popup_menu.add_line ("Effects, wet part", Pattern_TYPE_FX);
		popup_menu.add_line ("Effects, dry part", Pattern_TYPE_FX + 0x100);
		popup_menu.add_line ("Audio In", Pattern_TYPE_AIN);
		line = popup_menu.select_radio_by_code (  new_track_conf.track_type
		                                        + (new_track_conf.wet_flag ? 0x000 : 0x100));
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			new_track_conf.track_type = static_cast <BYTE> (code & 0xFF);
			new_track_conf.wet_flag = ((code & 0xFF00) == 0);
		}
		break;
	}

	/* Verification de la validite de la nouvelle piste source */
	if (new_track_conf.track_type != old_value)
	{
		ret_val = player.find_track_to_add (_dest_track_type, _dest_track_nbr, &new_track_conf);
		if (ret_val == 0 || ret_val == 1)
		{
			track_conf_ptr [index] = new_track_conf;
			GTK_modified_flag = true;
		}
		else if (ret_val < 0)
		{
			GTK_mutex.signal ();
			LOG_printf ("PatEdMenuMix::set_source_track_type: Error: couldn't change source track number.\n");
			refresh ();
			return (-1);
		}
	}

	GTK_mutex.signal ();

	refresh ();

	return (0);
}



/*==========================================================================*/
/*      Nom: set_source_track                                               */
/*      Description: Change le numero d'une piste source 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.                                */
/*        - win_line: numero de la ligne de la fenetre sur laquelle on a    */
/*                    clique.                                               */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	PatEdMenuMix::set_source_track (int type, signed int value, int win_line)
{
	signed int	line;
	signed int	ret_val;
	signed long	code;
	char		nbr2_0 [2+1];
	char		line_0 [4 + TRK_NAME_LEN+1];
	int		stereo;
	int		index;
	int		nbr_tracks;
	PLAY_SOURCE_TRACK_CONF_BLOCK	*track_conf_ptr;
	PLAY_SOURCE_TRACK_CONF_BLOCK	new_track_conf;
	PLAY_SOURCE_TRACK_CONF_BLOCK	old_track_conf;
	Popup		popup_menu;

	GTK_mutex.wait ();

	Player &			player = Player::use_instance ();

	player.get_track_nbr_s_tracks (_dest_track_type,
	                             _dest_track_nbr,
	                             &nbr_tracks, &track_conf_ptr);
	index = win_line + _disp_pos;
	new_track_conf = track_conf_ptr [index];
	new_track_conf.wet_flag =    new_track_conf.wet_flag
	                          || new_track_conf.track_type != Pattern_TYPE_FX;
	old_track_conf = new_track_conf;

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

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		RSC_set_relative_object_position (RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT,
		                                  0, win_line * RSC_CHAR_H);
		sprintf (nbr2_0, INTR_base_track_2, new_track_conf.track_nbr + 1);
		EDIT_edit_string_base (nbr2_0, RSC_OBJ_MP_SUBM_MIX_IN_TRACK_TEXT, 2, INTR_base_track);
		new_track_conf.track_nbr = (int) strtol (nbr2_0, NULL, INTR_base_track) - 1;
		break;

	/* Position absolue  */
	case	INTR_CHGTYPE_ABS:
		new_track_conf.track_nbr = value;
		break;

	/* Pop-up */
	case	INTR_CHGTYPE_POP:
		for (int track = 0; track < GTK_nbr_tracks [new_track_conf.track_type]; track ++)
		{
			sprintf (line_0, INTR_base_track_2, track + 1);
			line_0 [2] = ':';
			line_0 [3] = ' ';
			player.get_track_name (new_track_conf.track_type, track, line_0 + 4);
			popup_menu.add_line (line_0, track);
		}

		if (new_track_conf.track_type == Pattern_TYPE_AIN)
		{
			for (int track = 0; track < GTK_nbr_tracks [Pattern_TYPE_AIN]; track ++)
			{
				stereo = player.get_track_stereo (Pattern_TYPE_AIN, track, false) - 1;
				while (stereo > 0)
				{
					track ++;
					if (popup_menu.disable_by_code (track, true) < 0)
					{
						LOG_printf ("PatEdMenuMix::set_source_track: Warning: line of Audio In track # %d not found in pop-up menu.\n",
						            track);
					}
					stereo --;
				}
			}
		}

		line = popup_menu.select_radio_by_code (new_track_conf.track_nbr);
		code = popup_menu.manage (line);
		if (code >= 0)
		{
			new_track_conf.track_nbr = (int) code;
		}
		break;
	}

	new_track_conf.track_nbr = MIN (new_track_conf.track_nbr, GTK_nbr_tracks [new_track_conf.track_type] - 1);
	new_track_conf.track_nbr = MAX (new_track_conf.track_nbr, 0);
	ret_val = player.find_track_to_add (_dest_track_type,
	                                  _dest_track_nbr,
	                                  &new_track_conf);
	if (ret_val == 0)
	{
		if (nbr_tracks > 1)
		{
			if (player.remove_source_track (_dest_track_type, _dest_track_nbr, &old_track_conf) < 0)
			{
				GTK_mutex.signal ();
				LOG_printf ("PatEdMenuMix::set_source_track: Error: couldn't remove the old source track config.\n");
				refresh ();
				return (-1);
			}
			GTK_modified_flag = true;
		}

		if (player.add_source_track (_dest_track_type, _dest_track_nbr,
		                           &new_track_conf, Player::MIX_PRESET_REPL) < 0)
		{
			GTK_mutex.signal ();
			LOG_printf ("PatEdMenuMix::set_source_track: Error: couldn't add the new source track config.\n");
			refresh ();
			return (-1);
		}
		GTK_modified_flag = true;

		if (nbr_tracks <= 1)
		{
			if (player.remove_source_track (_dest_track_type, _dest_track_nbr, &old_track_conf) < 0)
			{
				GTK_mutex.signal ();
				LOG_printf ("PatEdMenuMix::set_source_track: Error: couldn't remove the source track config.\n");
				refresh ();
				return (-1);
			}
		}
	}

	else if (ret_val < 0)
	{
		GTK_mutex.signal ();
		LOG_printf ("PatEdMenuMix::set_source_track: Error: couldn't change source track number.\n");
		refresh ();
		return (-1);
	}

	GTK_mutex.signal ();

	refresh ();

	return (0);
}



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



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