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

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	"archi.h"
#include	"base.h"
#include	"ClipBPat.h"
#include	"Graphic.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"mpannel.h"
#include	"PatEdTracks.h"
#include	"PatSelection.h"
#include	"patt.h"
#include	"Pattern.h"
#include	"Player.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"sliders.h"
#include	"tracks.h"
#include	"UndoCell.h"



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



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



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



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

int	TRK_cursor_col = 0;					/* Colonne (ecran) du curseur */
int	TRK_cursor_pos = 0;					/* Position du curseur dans la colonne */
int	TRK_cursor_offset = 0;				/* Offset de la premiere colonne affichee a l'ecran */

const	TRK_NOTE_ELEMENT	TRK_note_element [TRK_NBR_CONTENTS] =
{
	{ 1, 1, 3 },	// Note
	{ 2, 1, 1 },	// Instr
	{ 4, 2, 1 },	// Commande
	{ 2, 1, 1 },	// Volume
	{ 8, 4, 1 },	// Effect 6H
	{ 4, 2, 1 },	// Effect
	{ 4, 2, 1 },	// 4H
	{ 5, 2, 1 },	// 2D
	{ 5, 2, 1 },	// 3D
	{ 5, 2, 1 },	// Int
	{ 2, 1, 1 }		// 2H
};

/* Derniere position du curseur pour chaque type de
	piste et d'affichage. Les types superieurs a 3 sont
	les differentes formes d'affichage de FX et MIDI.  */
const int	TRK_cursor_last_pos [] [TRK_NBR_DISP_TYPE_MAX] =
{
	/* Types normaux */
	{ 6, 2, 8, 0 },	/* Samples */
	{ 5 },				/* Audio in */
	{ 7, 13 },			/* FX - Type CC XXX XXX CCCCVV */
	{ 7 },				/* MIDI - Type par defaut */

	/* Types etendus */
	{ 7, 13 },			/* FX - Type CCCC XXXX  CCCCVV */
	{ 8, 14 },			/* FX - Type CCCC DDD.DDCCCCVV */
	{ 8, 14 },			/* FX - Type CCCC DD.DDDCCCCVV */
	{ 8, 14 },			/* FX - Type CCCC DDDDD CCCCVV */
	{ 6, 12 }			/* FX - Type CCCC NNNHH CCCCVV */
};

/* Abscisses du curseur selon sa position et le type de piste. Les types
	superieurs a 3 sont les differentes formes d'affichage de FX et MIDI. */
const int	TRK_cursor_colxpos [] [16] =
{
	/* Types normaux */
	/* 0: Samples */
	{
		0,
		3, 4,
		5, 6, 7, 8,
		9, 10
	},

	/* 1: Audio In */
	{
		0, 1, 2, 3,
		5, 6
	},

	/* 2: FX - Type CC XXX XXX CCCCVV */
	{
		0, 1,
		3, 4, 5,
		7, 8, 9,
		11, 12, 13, 14,
		15, 16
	},

	/* 3: MIDI - Type par defaut */
	{
		0, 1, 2, 3,
		5, 6, 7, 8
	},

	/* Types etendus */
	/* 4: FX - Type CCCC XXXX  CCCCVV */
	{
		0, 1, 2, 3,
		6, 7, 8, 9,
		11, 12, 13, 14,
		15, 16
	},

	/* 5: FX - Type CCCCDDD.DD CCCCVV */
	{
		0, 1, 2, 3,
		4, 5, 6,
		8, 9,
		11, 12, 13, 14,
		15, 16
	},

	/* 6: FX - Type CCCCDD.DDD CCCCVV */
	{
		0, 1, 2, 3,
		4, 5,
		7, 8, 9,
		11, 12, 13, 14,
		15, 16
	},

	/* 7: FX - Type CCCC DDDDD CCCCVV */
	{
		0, 1, 2, 3,
		5, 6, 7, 8, 9,
		11, 12, 13, 14,
		15, 16
	},

	/* 8: FX - Type CCCC NNNHH CCCCVV */
	{
		0, 1, 2, 3,
		5, 8, 9,
		11, 12, 13, 14,
		15, 16
	}
};

/* Indique le contenu de chaque type de piste */
const TRK_NOTE_CONTENT	TRK_note_content [TRK_NBR_EXT_TRACK_TYPES] [6] =
{
	/* 0: Samples */
	{
		{ TRK_CONTENT_NOTE, 0 },
		{ TRK_CONTENT_INSTR, 0 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 1: Audio In */
	{
		{ TRK_CONTENT_COMMAND, 1 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 2: FX - Type CC XXX XXX CCCCVV */
	{
		{ TRK_CONTENT_EFFECT_6H, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 3: MIDI - Type par defaut */
	{
		{ TRK_CONTENT_4H, 1 },
		{ TRK_CONTENT_4H, 0 },
		{ -1, 0 }
	},

	/* Types etendus */
	/* 4: FX - Type CCCC  XXXX CCCCVV */
	{
		{ TRK_CONTENT_EFFECT, 2 },
		{ TRK_CONTENT_4H, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 5: FX - Type CCCCDDD.DD CCCCVV */
	{
		{ TRK_CONTENT_EFFECT, 0 },
		{ TRK_CONTENT_2D, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 6: FX - Type CCCCDD.DDD CCCCVV */
	{
		{ TRK_CONTENT_EFFECT, 0 },
		{ TRK_CONTENT_3D, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 7: FX - Type CCCC DDDDD CCCCVV */
	{
		{ TRK_CONTENT_EFFECT, 1 },
		{ TRK_CONTENT_INT, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	},

	/* 8: FX - Type CCCC NNNHH CCCCVV */
	{
		{ TRK_CONTENT_EFFECT, 1 },
		{ TRK_CONTENT_NOTE, 0 },
		{ TRK_CONTENT_INSTR, 1 },
		{ TRK_CONTENT_COMMAND, 0 },
		{ TRK_CONTENT_VOLUME, 0 },
		{ -1, 0 }
	}
};

/* Taille des colonnes pour chaque type de piste (normal) et d'affichage */
const int	TRK_column_width [Pattern_NBR_TYPES] [TRK_NBR_DISP_TYPE_MAX] =
{
	{ 9, 5, 11, 3 },
	{ 7 },
	{ 11, 17 },
	{ 9 }
};

/* Tableau indiquant le type d'affichage pour chaque type de piste */
/* N'est utilise que quand on cree un nouveau preset */
int	TRK_init_display_type [Pattern_NBR_TYPES] =
{
	TRK_DISP_TYPE_SPL_EXP,
	TRK_DISP_TYPE_AIN_NORM,
	TRK_DISP_TYPE_FX_EXP,
	TRK_DISP_TYPE_MID_NORM
};
/* Indique quel type de piste est actuellement affiche */
/* N'est utilise que quand on cree un nouveau preset */
int	TRK_init_displayed_track_type = TRK_DISP_TYPE_SPL_NORM;
/* Indique le nombre de types d'affichage pour chaque type de piste */
int	TRK_nbr_disp_type [Pattern_NBR_TYPES] =
{
	TRK_NBR_DISP_TYPE_SPL,
	TRK_NBR_DISP_TYPE_AIN,
	TRK_NBR_DISP_TYPE_FX,
	TRK_NBR_DISP_TYPE_MID
};

TRK_PRESET	TRK_preset_data [TRK_NBRPRESETS_MAXI];	/* Chaque preset. A initialiser */
int	TRK_preset_nbr = 0;					/* Preset courant */

/* Premier caractere de la colonne de volume */
const char	TRK_vol_col_fx [0x10] =
{
	'*', '0', '1', '2', '3', '4', '-', '+', 'D', 'U', 'S', 'V', 'P', 'L', 'R', 'M'
};



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

/* Clipboard */
ClipBPat	TRK_clipboard;



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



/*==========================================================================*/
/*      Nom: TRK_init                                                       */
/*      Description: Initialise toutes les donnees relatives a l'affichage  */
/*                   des pistes.                                            */
/*==========================================================================*/

void	TRK_init (void)
{
	/* Init des presets */
	TRK_init_presets ();
}



void	TRK_restore (void)
{
}



/*==========================================================================*/
/*      Nom: TRK_set_column_track                                           */
/*      Description: Change la piste de la colonne du preset courant de     */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - column: numero de la colonne (0 - ...)                          */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	TRK_set_column_track (int column, int type, signed int value)
{
	int		new_value;
	int		track_type;

	new_value = TRK_preset_data [TRK_preset_nbr].track_nbr [column];

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

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

	track_type = TRK_preset_data [TRK_preset_nbr].track_type;
	new_value = MIN (new_value, GTK_nbr_tracks [track_type] - 1);
	new_value = MAX (new_value, 0);
	TRK_preset_data [TRK_preset_nbr].track_nbr [column] = new_value;

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->refresh ();
}



/*==========================================================================*/
/*      Nom: TRK_set_track_type                                             */
/*      Description: Change le type de piste du preset 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,                             */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	TRK_set_track_type (int type, signed int value)
{
	signed int	old_value;
	signed int	new_value;

	old_value = TRK_preset_data [TRK_preset_nbr].track_type;
	new_value = old_value;

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

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

	new_value = MAX (MIN (new_value, Pattern_NBR_TYPES - 1), 0);
	if (GTK_nbr_tracks [new_value] > 0)
	{
		TRK_preset_data [TRK_preset_nbr].track_type = new_value;
		TRK_init_1_preset (TRK_preset_nbr);
		TRK_check_presets ();
		INTR_pattern_editor_track_ptr->check_cursor ();
	}

	else
	{
		INTR_dialog_box ("CHANGE TRACK TYPE",
							  "There is no track of this type in\n"
							  "the module. Go in the TOOLS->TRACKS\n"
							  "menu and add tracks to the module.",
							  "Cancel", 0, 0);
	}

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->check_cursor ();
	TRK_display_presets ();
	TRK_display_mute (false);
	INTR_pattern_editor_menu_ptr->redraw ();
	INTR_pattern_editor_track_ptr->redraw ();
}



/*==========================================================================*/
/*      Nom: TRK_set_disp_type                                              */
/*      Description: Change le type d'affichage de piste du preset courant  */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	TRK_set_disp_type (int type, signed int value)
{
	signed int	new_value;
	int		nbr;

	new_value = TRK_preset_data [TRK_preset_nbr].disp_type;

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

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

	nbr = TRK_nbr_disp_type [TRK_preset_data [TRK_preset_nbr].track_type];
	while (new_value < 0)
	{
		new_value += nbr;
	}
	new_value %= nbr;

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

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->check_cursor ();
	TRK_display_mute (false);
	INTR_pattern_editor_track_ptr->redraw ();
}



/*==========================================================================*/
/*      Nom: TRK_display_mute                                               */
/*      Description: Affiche les pistes muettes                             */
/*==========================================================================*/

void	TRK_display_mute (bool refresh_flag)
{
	int		type;
	int		track;
	int		nbr_mute;

	RSC_display_object (RSC_OBJ_MP_MUTE_MUTEICN);

	type = TRK_preset_data [TRK_preset_nbr].track_type;
	RSC_set_width (RSC_OBJ_MP_MUTE_BKGND,
	                 TRK_NBRONOFF_MAXI
	               * RSC_get_width (RSC_OBJ_MP_MUTE_TRKNUM));
	RSC_display_object (RSC_OBJ_MP_MUTE_BKGND);

	nbr_mute = MIN (GTK_nbr_tracks [type], TRK_NBRONOFF_MAXI);
	for (track = 0; track < nbr_mute; track ++)
	{
		TRK_activate_mute (track);
		RSC_display_object (RSC_OBJ_MP_MUTE_TRKNUM);
	}

	if (refresh_flag)
	{
		INTR_pattern_editor_track_ptr->refresh ();
	}
}



/*==========================================================================*/
/*      Nom: TRK_activate_mute                                              */
/*      Description: Place et configure l'icone de mute correspondant a la  */
/*                   piste demandee.                                        */
/*      Parametres en entree:                                               */
/*        - track: piste dont on veut preparer l'icone mute.                */
/*==========================================================================*/

void	TRK_activate_mute (int track)
{
	int		type;
	int		pixxpos;
	int		pixypos;
	int		pixwidth;
	char		string_0 [3+1];

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

	sprintf (string_0, "%d", track + 1);
	RSC_set_string (RSC_OBJ_MP_MUTE_TRKNUM, string_0);
	type = TRK_preset_data [TRK_preset_nbr].track_type;
	pixxpos =   RSC_absolute_object_pixxpos [RSC_OBJ_MP_MUTE]
				 + RSC_get_width (RSC_OBJ_MP_MUTE_MUTEICN);
	pixypos = RSC_absolute_object_pixypos [RSC_OBJ_MP_MUTE];
	pixwidth = RSC_get_width (RSC_OBJ_MP_MUTE_TRKNUM);

	RSC_set_absolute_object_position (RSC_OBJ_MP_MUTE_TRKNUM, pixxpos + pixwidth * track, pixypos);
	RSC_pos_flag (RSC_OBJ_MP_MUTE_TRKNUM, RSC_ATTR_SELECTED, ! player.get_track_onoff (type, track, false));
}



/*==========================================================================*/
/*      Nom: TRK_manage_tracks                                              */
/*      Description: Teste les resources graphiques relatives aux pistes    */
/*                   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	TRK_manage_tracks (void)
{
	int		track_type;
	int		track;
	int		track_cnt;
	int		preset;
	int		pix_height;
	int		pix_xpos;
	int		pix_ypos;
	int		nbr_mute;
	signed int	sel_object;
	signed int	sel_aine;
	bool		active_flag;

	Player &			player = Player::use_instance ();

	track_type = TRK_preset_data [TRK_preset_nbr].track_type;

	/* Pistes muettes */
	RSC_gere_resource (RSC_OBJ_MP_MUTE_MUTEICN, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		/* Renverse l'etat de toutes les pistes */
		if (RSC_mouse_key == 2)
		{
			player.invert_all_mute ();
		}

		/* Toutes les pistes on */
		else
		{
			player.no_mute ();
		}
		TRK_display_mute ();
		return (true);
	}

	/* Chaque icone mute */
	pix_xpos =   RSC_absolute_object_pixxpos [RSC_OBJ_MP_MUTE]
				  + RSC_get_width (RSC_OBJ_MP_MUTE_MUTEICN);
	pix_ypos = RSC_absolute_object_pixypos [RSC_OBJ_MP_MUTE];
	pix_height = RSC_BOXTEXT_H;
	if (   INTR_mouse.x >= pix_xpos
	    && INTR_mouse.y >= pix_ypos
	    && INTR_mouse.y < pix_ypos + pix_height)
	{
		nbr_mute = MIN (GTK_nbr_tracks [track_type], TRK_NBRONOFF_MAXI);
		for (track = 0; track < nbr_mute; track ++)
		{
			TRK_activate_mute (track);
			RSC_gere_resource (RSC_OBJ_MP_MUTE_TRKNUM, &sel_object, &sel_aine);
			if (sel_object >= 0)
			{
				/* Bouton droit: Solo */
				if (RSC_mouse_key & 2)
				{
					for (track_cnt = 0; track_cnt < GTK_nbr_tracks [track_type]; track_cnt ++)
					{
						active_flag = (track_cnt == track);
						player.set_track_onoff (track_type, track_cnt, false, active_flag);
						player.set_track_onoff (track_type, track_cnt, true, active_flag);
					}
				}

				/* Bouton gauche: inverse l'etat mute */
				else
				{
					active_flag = ! player.get_track_onoff (track_type, track, false);
					player.set_track_onoff (track_type, track, false, active_flag);
					player.set_track_onoff (track_type, track, true, active_flag);
				}
				TRK_display_mute ();
				INTR_wait_mouse (true);
				/*** Seul moyen de corriger le bug du bloquage. Pour eviter ca,
					reprendre tout le concept des mute buttons. ***/
				RSC_flush_resource ();
				return (true);
			}
		}
	}

	/* Presets */
	RSC_gere_resource (RSC_OBJ_MP_TPRE_PRESICN, &sel_object, &sel_aine);
	if (sel_object >= 0)
	{
		/* Reinitialisation des presets */
		if (INTR_dialog_box ("PRESET RESET",
		                     "Reset all presets \?",
									"OK\nCancel", 0, 1) == 0)
		{
			TRK_init_presets ();
			TRK_check_presets ();
			INTR_pattern_editor_track_ptr->check_cursor ();
			MPAN_display_main_pannel ();
		}
		return (true);
	}

	/* Chaque icone de preset */
	pix_xpos =   RSC_absolute_object_pixxpos [RSC_OBJ_MP_TPRE]
				  + RSC_get_width (RSC_OBJ_MP_TPRE_PRESICN);
	pix_ypos = RSC_absolute_object_pixypos [RSC_OBJ_MP_TPRE];
	pix_height = RSC_BOXTEXT_H;
	if (   INTR_mouse.x >= pix_xpos
	    && INTR_mouse.y >= pix_ypos
	    && INTR_mouse.y < pix_ypos + pix_height)
	{
		for (preset = 0; preset < TRK_NBRPRESETS_MAXI; preset ++)
		{
			TRK_activate_preset (preset);
			RSC_gere_resource (RSC_OBJ_MP_TPRE_PRESET, &sel_object, &sel_aine);
			if (sel_object >= 0)
			{
				TRK_set_preset (INTR_CHGTYPE_ABS, preset);
				return (true);
			}
		}
	}

	return (false);
}



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

void	TRK_cut_block (void)
{
	UndoCell	*undo_ptr;

	if (INTR_pattern_editor_track_ptr->_selection_ptr != NULL)
	{
		INTR_pattern_editor_track_ptr->_selection_ptr->check ();
		TRK_clipboard.cut (*(INTR_pattern_editor_track_ptr->_selection_ptr), &undo_ptr);
		GTK_undo_list_ptr->add_action (*undo_ptr, "Cut block");
		delete undo_ptr;

		const Player &	player = Player::use_instance ();
		if (player.get_play_mode () == Player::MODE_STOP)
		{
			INTR_pattern_editor_track_ptr->refresh ();
		}
	}
}



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

void	TRK_copy_block (void)
{
	if (INTR_pattern_editor_track_ptr->_selection_ptr != NULL)
	{
		INTR_pattern_editor_track_ptr->_selection_ptr->check ();
		TRK_clipboard.copy (*(INTR_pattern_editor_track_ptr->_selection_ptr));
	}
}



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

void	TRK_paste_block (void)
{
	int		pattern;
	int		line_pos;
	int		col;
	UndoCell	*undo_ptr;

	pattern = GTK_get_current_pattern_number ();
	line_pos = GTK_get_line_position ();
	col = TRK_cursor_col + TRK_cursor_offset;
	TRK_clipboard.paste (PatSelection (pattern, TRK_preset_nbr,
	                                   col, line_pos,
	                                   TRK_preset_data [TRK_preset_nbr].nbr - col,
	                                   PAT_get_pattern_height (pattern) - line_pos),
	                     &undo_ptr);
	GTK_undo_list_ptr->add_action (*undo_ptr, "Paste clipboard");
	delete undo_ptr;

	const Player &	player = Player::use_instance ();
	if (player.get_play_mode () == Player::MODE_STOP)
	{
		INTR_pattern_editor_track_ptr->refresh ();
	}
}



/****************************************************************************/
/*                                                                          */
/*      PRESETS                                                             */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: TRK_init_presets                                               */
/*      Description: Initialise les presets suivant le nombre de voies.     */
/*==========================================================================*/

void	TRK_init_presets (void)
{
	int		preset;

	for (preset = 0; preset < TRK_NBRPRESETS_MAXI; preset ++)
	{
		TRK_preset_data [preset].track_type = TRK_init_displayed_track_type;
		TRK_init_1_preset (preset);
	}
}



/*==========================================================================*/
/*      Nom: TRK_init_1_preset                                              */
/*      Description: Initialise un preset suivant le nombre de voies.       */
/*      Parametres en entree:                                               */
/*        - preset: numero du preset a initialiser.                         */
/*==========================================================================*/

void	TRK_init_1_preset (int preset)
{
	int		colonne;
	int		track_type;

	track_type = TRK_preset_data [preset].track_type;
	TRK_preset_data [preset].disp_type = TRK_init_display_type [track_type];
	TRK_preset_data [preset].nbr = GTK_nbr_tracks [track_type];
	for (colonne = 0; colonne < GTK_nbr_tracks [track_type]; colonne ++)
	{
		TRK_preset_data [preset].track_nbr [colonne] = (preset + colonne) % GTK_nbr_tracks [track_type];
	}
}



/*==========================================================================*/
/*      Nom: TRK_set_preset                                                 */
/*      Description: Change le preset courant d'affichage de                */
/*                   differentes manieres. L'interface est ici mise en jeu. */
/*      Parametres en entree:                                               */
/*        - type: 0 = Le parametre est absolu,                              */
/*                1 = Le parametre est relatif,                             */
/*        - value: le parametre en question.                                */
/*==========================================================================*/

void	TRK_set_preset (int type, signed int value)
{
	int		old_track_type;
	int		old_disp_type;

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

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

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

	TRK_preset_nbr = TRK_preset_nbr % TRK_NBRPRESETS_MAXI;
	if (TRK_preset_nbr < 0 || TRK_preset_nbr >= TRK_NBRPRESETS_MAXI)
	{
		TRK_preset_nbr -= TRK_NBRPRESETS_MAXI * SGN (TRK_preset_nbr);
	}

	TRK_check_presets ();
	INTR_pattern_editor_track_ptr->check_cursor ();

	if (   old_track_type != TRK_preset_data [TRK_preset_nbr].track_type
	    || old_disp_type != TRK_preset_data [TRK_preset_nbr].disp_type)
	{
		MPAN_display_main_pannel ();
	}
	else
	{
		INTR_pattern_editor_track_ptr->redraw ();
		TRK_display_presets ();
	}
}



/*==========================================================================*/
/*      Nom: TRK_check_presets                                              */
/*      Description: Verifie et corrige les presets apres une modification  */
/*                   du nombre de piste ou de l'affichage.                  */
/*==========================================================================*/

void	TRK_check_presets (void)
{
	int		preset;
	int		track_type;
	int		src_track;
	int		dest_track;

	for (preset = 0; preset < TRK_NBRPRESETS_MAXI; preset ++)
	{
		/* Verifie que le preset courant affiche un type de piste qui contient
		   bien des pistes. */
		track_type = TRK_preset_data [preset].track_type;
		if (GTK_nbr_tracks [track_type] <= 0)
		{
			TRK_preset_data [preset].track_type = TRK_init_displayed_track_type;
			TRK_init_1_preset (preset);
		}

		/* Verifie qu'on est dans un mode de format de piste qui existe */
		if (TRK_preset_data [preset].disp_type >= TRK_nbr_disp_type [track_type])
		{
			TRK_preset_data [preset].disp_type = 0;
		}

		/* Retire du preset toutes les pistes qui n'existent pas */
		src_track = 0;
		for (dest_track = 0; dest_track < GTK_NBRTRACKS_MAXI; dest_track ++)
		{
			for ( ; src_track < GTK_NBRTRACKS_MAXI; src_track ++)
			{
				if (TRK_preset_data [preset].track_nbr [src_track] < GTK_nbr_tracks [track_type])
				{
					break;
				}
			}
			if (src_track < GTK_NBRTRACKS_MAXI)
			{
				TRK_preset_data [preset].track_nbr [dest_track] = TRK_preset_data [preset].track_nbr [src_track];
				src_track ++;
			}
			else
			{
				TRK_preset_data [preset].track_nbr [dest_track] = 0;
			}
		}
		
		TRK_preset_data [preset].nbr = MAX (TRK_preset_data [preset].nbr, 1);
	}
}



/*==========================================================================*/
/*      Nom: TRK_display_presets                                            */
/*      Description: Affiche les presets                                    */
/*==========================================================================*/

void	TRK_display_presets (void)
{
	int		preset;
	int		counter;
	int		t_t_object_list [Pattern_NBR_TYPES] =
	{
		RSC_OBJ_MP_TICN_SPL,
		RSC_OBJ_MP_TICN_AIN,
		RSC_OBJ_MP_TICN_FX,
		RSC_OBJ_MP_TICN_MID
	};

	/* Affiche les presets */
	RSC_display_object (RSC_OBJ_MP_TPRE_PRESICN);
	for (preset = 0; preset < TRK_NBRPRESETS_MAXI; preset ++)
	{
		TRK_activate_preset (preset);
		RSC_display_object (RSC_OBJ_MP_TPRE_PRESET);
	}

	/* Temporaire: on desactive la possibilite d'editer les pistes
	   Audio In et MIDI tant qu'elles ne sont pas implementees. */
	RSC_set_flag (RSC_OBJ_MP_TICN_AIN, RSC_ATTRP_DISABLE);
	RSC_set_flag (RSC_OBJ_MP_TICN_MID, RSC_ATTRP_DISABLE);

	/* Affiche le type de piste du preset courant */
	for (counter = 0; counter < Pattern_NBR_TYPES; counter ++)
	{
		RSC_pos_flag (t_t_object_list [counter], RSC_ATTR_SELECTED,
		              TRK_preset_data [TRK_preset_nbr].track_type == counter);
	}
	RSC_display_object (RSC_OBJ_MP_TICN);
}



/*==========================================================================*/
/*      Nom: TRK_activate_preset                                            */
/*      Description: Place et configure l'icone de preset demandee.         */
/*      Parametres en entree:                                               */
/*        - preset: numero de preset dont on veut preparer l'icone.         */
/*==========================================================================*/

void	TRK_activate_preset (int preset)
{
	int		pixxpos;
	int		pixypos;
	int		pixwidth;
	char		string_0 [3+1];

	sprintf (string_0, "%d", preset + 1);
	pixxpos = RSC_absolute_object_pixxpos [RSC_OBJ_MP_TPRE]
				 + ((RSC_OBJTYPE_BOXTEXT *) RSC_object_ptr [RSC_OBJ_MP_TPRE_PRESICN])->l;
	pixypos = RSC_absolute_object_pixypos [RSC_OBJ_MP_TPRE];
	pixwidth = ((RSC_OBJTYPE_BOXTEXT *) RSC_object_ptr [RSC_OBJ_MP_TPRE_PRESET])->l;
	RSC_set_string (RSC_OBJ_MP_TPRE_PRESET, string_0);

	RSC_set_absolute_object_position (RSC_OBJ_MP_TPRE_PRESET, pixxpos + pixwidth * preset, pixypos);
	RSC_pos_flag (RSC_OBJ_MP_TPRE_PRESET, RSC_ATTR_SELECTED, preset == TRK_preset_nbr);
}



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



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