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

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

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



#if defined (_MSC_VER)
	#pragma warning (1 : 4130 4223 4705 4706)
	#pragma warning (4 : 4355 4786 4800)
#endif



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

#include	<stdio.h>
#include	<stdlib.h>
#include	<math.h>
#include	<time.h>

#include	"archi.h"
#include	"base.h"
#include	"base.hpp"
#include	"base_ct.h"
#include	"config.h"
#include	"ConfigFile.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include	"DialBox.h"
#include	"EditString.h"
#include	"edstring.h"
#include	"file.h"
#include	"FileSelector.h"
#include	"gt_limit.h"
#include	"gtracker.h"
#include	"Image.h"
#include	"intrface.h"
#include	"List.h"
#include	"log.h"
#include	"mixer.h"
#include	"Mouse.h"
#include	"mpannel.h"
#include	"PatEdMenus.h"
#include	"player.h"
#include	"Popup.h"
#include	"ProgressBox.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"samp.h"
#include	"SplEdMainLoop.h"
#include	"String.h"
#include	"Thread.h"
#include	"tracks.h"



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



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



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



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

const char	INTR_hexa_2_ascii [256*2+1] =
	"000102030405060708090A0B0C0D0E0F"
	"101112131415161718191A1B1C1D1E1F"
	"202122232425262728292A2B2C2D2E2F"
	"303132333435363738393A3B3C3D3E3F"
	"404142434445464748494A4B4C4D4E4F"
	"505152535455565758595A5B5C5D5E5F"
	"606162636465666768696A6B6C6D6E6F"
	"707172737475767778797A7B7C7D7E7F"
	"808182838485868788898A8B8C8D8E8F"
	"909192939495969798999A9B9C9D9E9F"
	"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
	"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
	"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
	"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
	"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
	"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";
const char	INTR_hexa_2_ascii_space [256*2+1] =
	"   1 2 3 4 5 6 7 8 9 A B C D E F"
	"101112131415161718191A1B1C1D1E1F"
	"202122232425262728292A2B2C2D2E2F"
	"303132333435363738393A3B3C3D3E3F"
	"404142434445464748494A4B4C4D4E4F"
	"505152535455565758595A5B5C5D5E5F"
	"606162636465666768696A6B6C6D6E6F"
	"707172737475767778797A7B7C7D7E7F"
	"808182838485868788898A8B8C8D8E8F"
	"909192939495969798999A9B9C9D9E9F"
	"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
	"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
	"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
	"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
	"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
	"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";
const char	INTR_hexa_2_ascii_point [256*2+1] =
	"\x09\x09" "\x09" "1" "\x09" "2" "\x09" "3"
	"\x09" "4" "\x09" "5" "\x09" "6" "\x09" "7"
	"\x09" "8" "\x09" "9" "\x09" "A" "\x09" "B"
	"\x09" "C" "\x09" "D" "\x09" "E" "\x09" "F"
	"101112131415161718191A1B1C1D1E1F"
	"202122232425262728292A2B2C2D2E2F"
	"303132333435363738393A3B3C3D3E3F"
	"404142434445464748494A4B4C4D4E4F"
	"505152535455565758595A5B5C5D5E5F"
	"606162636465666768696A6B6C6D6E6F"
	"707172737475767778797A7B7C7D7E7F"
	"808182838485868788898A8B8C8D8E8F"
	"909192939495969798999A9B9C9D9E9F"
	"A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
	"B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
	"C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
	"D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
	"E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
	"F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF";

const char	INTR_note_names [GTK_NBRNOTES_MAXI] [3+1] =
{
	"---", "C#=", "D-=", "D#=", "E-=", "F-=", "F#=", "G-=", "G#=", "A-=", "A#=", "B-=",
	"C--", "C#-", "D--", "D#-", "E--", "F--", "F#-", "G--", "G#-", "A--", "A#-", "B--",
	"C-0", "C#0", "D-0", "D#0", "E-0", "F-0", "F#0", "G-0", "G#0", "A-0", "A#0", "B-0",
	"C-1", "C#1", "D-1", "D#1", "E-1", "F-1", "F#1", "G-1", "G#1", "A-1", "A#1", "B-1",
	"C-2", "C#2", "D-2", "D#2", "E-2", "F-2", "F#2", "G-2", "G#2", "A-2", "A#2", "B-2",
	"C-3", "C#3", "D-3", "D#3", "E-3", "F-3", "F#3", "G-3", "G#3", "A-3", "A#3", "B-3",
	"C-4", "C#4", "D-4", "D#4", "E-4", "F-4", "F#4", "G-4", "G#4", "A-4", "A#4", "B-4",
	"C-5", "C#5", "D-5", "D#5", "E-5", "F-5", "F#5", "G-5", "G#5", "A-5", "A#5", "B-5",
	"C-6", "C#6", "D-6", "D#6", "E-6", "F-6", "F#6", "G-6", "G#6", "A-6", "A#6", "B-6",
	"C-7", "C#7", "D-7", "D#7", "E-7", "F-7", "F#7", "G-7", "G#7", "A-7", "A#7", "B-7",
	"C-8", "C#8", "D-8", "D#8", "E-8", "F-8", "F#8", "G-8"
};

const char	*const INTR_time_unit_name_0_ptr [INTR_NBR_TIME_UNITS] =
{
	"s", "ms", "beats", "spl", "Hz", "note"
};


/* Conversion decimal -> ascii */
char	INTR_dec_2_ascii_units_char [1000] [4];	/* A initialiser */
char	INTR_dec_2_ascii_1000_char_s1 [256] [2];	/* A initialiser */
char	INTR_dec_2_ascii_1000_char_s2 [256] [2];	/* A initialiser */
UWORD	INTR_dec_2_ascii_1000_int [257];	/* A initialiser */

/* Operations en attente (calcul apres le relachement
d'une touche a action continue) */
int		INTR_waiting_operation_type = 0;		/* Type de l'operation en attente d'execution, 0 si aucune */

/*--------------------------------------------------------------------------*/
/*      Affichage                                                           */
/*--------------------------------------------------------------------------*/

/* Objet de l'interface graphique */
Mouse	*INTR_graph_ptr = NULL;
int	INTR_init_sreen_resol_width = 800;
int	INTR_init_sreen_resol_height = 600;

/* L'etat de la souris */
Mouse_CURRENT_STATE	INTR_mouse = { 0, 0, 0, 0, 0 };

/* L'ecran principal courant */
int	INTR_main_screen = INTR_MAIN_SCREEN_PATTERN_EDITOR;

/* Pointeur sur le gestionnaire de l'interface de l'editeur de samples */
SplEdMainLoop	*INTR_sample_editor_interface_ptr = NULL;

/* Pointeur sur le gestionnaire des sous-menus de la page principale */
PatEdMenus	*INTR_pattern_editor_menu_ptr = NULL;
PatEdTracks	*INTR_pattern_editor_track_ptr = NULL;

int	INTR_double_click_speed = INTR_CLICK_SPEED;		/* Duree d'un double-click en 100emes de seconde (min = 2) */
const int	INTR_inc_speed [] [4] =	/* Vitesses de defilement des valeurs lors d'appui sur les fleches */
{
	{ 0, 1, 2, 8 },			/* Valeurs normales */
	{ 0, 1, 0x10, 0x40 },	/* Valeurs rapides */
	{ 0, 1, 0x10, 0x1000 },	/* Valeurs de samples */
	{ 0, 1, 5, 10 }			/* Valeurs pour les sliders d'une fenetre (de longueur 10) */
};

/* Bases d'affichage. Nombre de chiffres a afficher en fin de variable. */
int	INTR_base_song = 16;					/* Song: Pos, Pat, Len, Rep, No inst, No sample, No preset, No Fx */
char	INTR_base_song_3 [4+1] = "%3X";
char	INTR_base_song_5 [4+1] = "%5X";

int	INTR_base_track = 10;				/* Numero de piste */
char	INTR_base_track_2 [4+1] = "%2d";

int	INTR_base_lines = 16;				/* Numeros des lignes des patterns */
char	INTR_base_lines_3 [4+1] = "%3X ";
char	INTR_base_lines_2 [4+1] = "%02X";

int	INTR_base_speed = 10;				/* Vitesse */
char	INTR_base_speed_3 [4+1] = "%3d";

const char * INTR_base_clock_min_sec = "%02d:%02d";	// Clock

int INTR_splpos_type = INTR_SPLPOS_TYPE_HEXA;	/* Type d'affichage des positions de samples */
bool	INTR_splpos_byte_flag = false;	/* Affichage en octets si true, en samples sinon. */

int	INTR_time_unit = INTR_TIME_UNIT_MSEC;	/* Unite de temps pour l'edition de certains parametres */
int	INTR_freq_unit = INTR_TIME_UNIT_HZ;		/* Unite de frequence pour l'edition de certains parametres */



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



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

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



/*==========================================================================*/
/*      Nom: INTR_init_interface                                            */
/*      Description: Initialise correctement les donnees de l'interface.    */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

WORD	INTR_init_interface (void)
{
	LOG_printf ("INTR_init_interface:\n");

	/* Initialisation du mode graphique */
	INTR_graph_ptr = new Mouse (INTR_init_sreen_resol_width, INTR_init_sreen_resol_height);
	if (INTR_graph_ptr == NULL)
	{
		LOG_printf ("INTR_init_interface: Error: couldn't allocate memory for the graphic device object.\n");
		return (-1);
	}
	else if (INTR_graph_ptr->check_ok ())
	{
		LOG_printf ("INTR_init_interface: Error: couldn't init graphic engine.\n");
		return (-1);
	}
	INTR_graph_ptr->mouse_bee ();

	if (RSC_init_resource () != 0)
	{
		LOG_printf ("INTR_init_interface: Error: couldn't init graphic resource system.\n");
		return (-1);
	}

	/* Init des pistes */
	TRK_init ();

	/* Init des tables de conversion decimal -> ascii */
	INTR_init_dec_2_ascii ();

/*______________________________________________
 *
 * Init de quelques variables diverses
 *______________________________________________
 */

	INTR_main_screen = INTR_MAIN_SCREEN_PATTERN_EDITOR;

	/* Intialisation des sous-menus de la page principale */
	INTR_pattern_editor_menu_ptr = new PatEdMenus ();
	if (INTR_pattern_editor_menu_ptr->check_ok ())
	{
		LOG_printf ("INTR_init_interface: Error: couldn't create main GUI (menus).\n");
		return (-1);
	}
	INTR_pattern_editor_menu_ptr->set_submenu (PatEdMenus_FILES);

	INTR_pattern_editor_track_ptr = new PatEdTracks ();
	if (INTR_pattern_editor_track_ptr == NULL)
	{
		LOG_printf ("INTR_init_interface: Error: couldn't allocate memory for the pattern GUI.\n");
		return (-1);
	}
	INTR_pattern_editor_track_ptr->set_width (INTR_graph_ptr->get_width ());
	INTR_pattern_editor_track_ptr->set_height (  INTR_graph_ptr->get_height ()
	                                           - RSC_get_height (RSC_OBJ_MP_BKGD));

	/* Initialisation de l'interface de l'editeur de samples */
	INTR_sample_editor_interface_ptr = new SplEdMainLoop ();
	if (INTR_sample_editor_interface_ptr->check_ok ())
	{
		LOG_printf ("INTR_init_interface: Error: couldn't create Sample Editor GUI.\n");
		return (-1);
	}

/*______________________________________________
 *
 * Initialisations d'affichages
 *______________________________________________
 */

	RSC_set_string (RSC_OBJ_MP_INFLIN_TEXT, "");

	LOG_printf ("\tOK.\n");

	return (0);
}



void	INTR_restore_interface (void)
{
	if (INTR_sample_editor_interface_ptr != NULL)
	{
		delete INTR_sample_editor_interface_ptr;
		INTR_sample_editor_interface_ptr = NULL;
	}

	if (INTR_pattern_editor_track_ptr)
	{
		delete INTR_pattern_editor_track_ptr;
		INTR_pattern_editor_track_ptr = NULL;
	}

	if (INTR_pattern_editor_menu_ptr)
	{
		delete INTR_pattern_editor_menu_ptr;
		INTR_pattern_editor_menu_ptr = NULL;
	}

	TRK_restore ();
	RSC_restore_resource ();

	if (INTR_graph_ptr != NULL)
	{
		delete INTR_graph_ptr;
		INTR_graph_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: INTR_init_dec_2_ascii                                          */
/*      Description: Initialise les tableaux de conversion decimal-ascii.   */
/*==========================================================================*/

void	INTR_init_dec_2_ascii (void)
{
	int		count;
	UWORD		millier;
	char		text_0 [3+1];

	for (count = 0; count < 256; count ++)
	{
		millier = (count << 8) / 1000;
		INTR_dec_2_ascii_1000_int [count] = millier * 1000;
		sprintf (text_0, "%3d", millier * 10);
		INTR_dec_2_ascii_1000_char_s2 [count] [0] = text_0 [0];
		INTR_dec_2_ascii_1000_char_s2 [count] [1] = text_0 [1];
		sprintf (text_0, "%2d", millier);
		INTR_dec_2_ascii_1000_char_s1 [count] [0] = text_0 [0];
		INTR_dec_2_ascii_1000_char_s1 [count] [1] = text_0 [1];
	}
	INTR_dec_2_ascii_1000_int [256] = 0;

	for (count = 0; count < 1000; count ++)
	{
		sprintf (INTR_dec_2_ascii_units_char [count], "%03d", count);
	}

}



signed int	INTR_read_display_conf (const std::list <ConfigKey> &key_list)
{
	int		height;
	int		width;
	bool		bool_val;

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

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

		/* Cle anonyme: interdit */
		if (BASE_compare_string (key_name.c_str (), "") == 0)
		{
			LOG_printf ("INTR_read_display_conf: Warning: (in \"display\") unexpected unassigned value.\n");
		}

		/* Skin path */
		else if (BASE_compare_string (key_name.c_str (), "skin_path") == 0)
		{
			String			val_str;
			if (CFG_get_string (key_val, "display/skin_path", val_str))
			{
				String	path (val_str.left (FNAM_PATHNAME_MAXLEN - 1));

				if (strcmp (path.right (1).c_str (), FILE_path_separator_0) != 0)
				{
					path += FILE_path_separator_0;
				}
				path = FNAM_get_abs_path_from_rel (FILE_program_pathname, path);
				strcpy (GTK_skin_pathname_0, path.c_str ());
				GTK_ask_for_loading_skin_flag = true;
			}
		}

		/* Screen resolution */
		else if (BASE_compare_string (key_name.c_str (), "screen_resolution") == 0)
		{
			if (CFG_get_int_2 (key_val, "display/screen_resolution", width, height))
			{
				INTR_init_sreen_resol_width = MAX (width, 800);
				INTR_init_sreen_resol_height = MAX (height, 600);
			}
		}

		/* hexa_song */
		else if (BASE_compare_string (key_name.c_str (), "hexa_song") == 0)
		{
			if (CFG_get_bool (key_val, "display/hexa_song", bool_val))
			{
				if (bool_val)
				{
					INTR_base_song = 16;
					strcpy (INTR_base_song_3, "%3X");
					strcpy (INTR_base_song_5, "%5X");
				}
				else
				{
					INTR_base_song = 10;
					strcpy (INTR_base_song_3, "%3d");
					strcpy (INTR_base_song_5, "%5d");
				}
			}
		}

		/* hexa_track */
		else if (BASE_compare_string (key_name.c_str (), "hexa_track") == 0)
		{
			if (CFG_get_bool (key_val, "display/hexa_track", bool_val))
			{
				if (bool_val)
				{
					INTR_base_track = 16;
					strcpy (INTR_base_track_2, "%2X");
				}
				else
				{
					INTR_base_track = 10;
					strcpy (INTR_base_track_2, "%2d");
				}
			}
		}

		/* hexa_lines */
		else if (BASE_compare_string (key_name.c_str (), "hexa_lines") == 0)
		{
			if (CFG_get_bool (key_val, "display/hexa_lines", bool_val))
			{
				if (bool_val)
				{
					INTR_base_lines = 16;
					strcpy (INTR_base_lines_2, "%02X");
					strcpy (INTR_base_lines_3, "%3X");
				}
				else
				{
					INTR_base_lines = 10;
					strcpy (INTR_base_lines_2, "%02d");
					strcpy (INTR_base_lines_3, "%3d");
				}
			}
		}

		/* hexa_speed */
		else if (BASE_compare_string (key_name.c_str (), "hexa_speed") == 0)
		{
			if (CFG_get_bool (key_val, "display/hexa_speed", bool_val))
			{
				if (bool_val)
				{
					INTR_base_speed = 16;
					strcpy (INTR_base_speed_3, "%3X");
				}
				else
				{
					INTR_base_speed = 10;
					strcpy (INTR_base_speed_3, "%3d");
				}
			}
		}

		/* spl_pos_value */
		else if (BASE_compare_string (key_name.c_str (), "spl_pos_value") == 0)
		{
			CFG_get_bool (key_val, "display/spl_pos_value", INTR_splpos_byte_flag, "byte");
		}

		/* spl_pos_unit */
		else if (BASE_compare_string (key_name.c_str (), "spl_pos_unit") == 0)
		{
			CFG_get_enum (key_val, "display/spl_pos_unit", INTR_splpos_type, "hexa,dec,msc,sm");
		}

		/* time_unit */
		else if (BASE_compare_string (key_name.c_str (), "time_unit") == 0)
		{
			CFG_get_enum (key_val, "display/time_unit", INTR_time_unit, "sec,ms,beat,sample,hz,note");
		}

		/* freq_unit */
		else if (BASE_compare_string (key_name.c_str (), "freq_unit") == 0)
		{
			CFG_get_enum (key_val, "display/freq_unit", INTR_freq_unit, "sec,ms,beat,sample,hz,note");
		}

		/* Cle inconnue */
		else
		{
			LOG_printf ("INTR_read_display_conf: Warning: (in \"display\") syntax error \"%s\".\n",
			            key_name.c_str ());
		}
	}

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      SOURIS                                                              */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: INTR_test_mouse_click                                          */
/*      Description: Teste le bouton de la souris en specifiant le type de  */
/*                   clic (s'il a lieu). Si le bouton est enfonce au moment */
/*                   du test, la routine attend un certain temps pour       */
/*                   determiner le type de clic.                            */
/*      Retour: 0 = pas de clic                                             */
/*              1 = clic & relachement                                      */
/*              2 = double-clic                                             */
/*              3 = clic maintenu                                           */
/*              + 0x100 * bouton de clic.                                   */
/*==========================================================================*/

int	INTR_test_mouse_click (void)
{
	int		button;
	clock_t	debut_click;
	clock_t	duree_ticks;
	long		duree_centsec;

	/* Teste l'enfoncement du bouton */
	INTR_graph_ptr->get_mouse (&INTR_mouse);
	button = INTR_mouse.k;
	if (button == 0)
	{
		return (INTR_CLICK_RELEASED);
	}
	debut_click = clock ();

	/* Attend le relachement du bouton */
	do
	{
		duree_ticks = clock () - debut_click;
		duree_centsec = (long) ((double)duree_ticks * 100 / CLOCKS_PER_SEC);
		INTR_graph_ptr->get_mouse (&INTR_mouse);
	}
	while (INTR_mouse.k != 0 && duree_centsec < INTR_double_click_speed);

	if (INTR_mouse.k != 0)
	{
		return ((button << 8) + INTR_CLICK_HELD);
	}

	/* Attend le deuxieme enfoncement */
	do
	{
		duree_ticks = clock () - debut_click;
		duree_centsec = (long) ((double)duree_ticks * 100 / CLOCKS_PER_SEC);
		INTR_graph_ptr->get_mouse (&INTR_mouse);
	}
	while (INTR_mouse.k == 0 && duree_centsec < INTR_double_click_speed);

	if (INTR_mouse.k != 0)
	{
		return ((button << 8) + INTR_CLICK_DOUBLE);
	}

	return ((button << 8) + INTR_CLICK_SINGLE);
}



/*==========================================================================*/
/*      Nom: INTR_wait_mouse                                                */
/*      Description: Attend que le bouton de la souris soit relache.        */
/*                   relache.                                               */
/*      Parametres en entree:                                               */
/*        - etat: true = attend le relachement, false = n'attend pas.       */
/*==========================================================================*/

void	INTR_wait_mouse (bool etat)
{
	Mouse_CURRENT_STATE	mouse;

	INTR_graph_ptr->show_mouse ();

	if (etat)
	{
		do
		{
			INTR_graph_ptr->get_mouse (&mouse);
			Thread::sleep (50);
		}
		while (mouse.k != 0);
	}
}



/****************************************************************************/
/*                                                                          */
/*      DIVERS                                                              */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: INTR_redraw_all                                                */
/*      Description: Redessine tout l'ecran.                                */
/*==========================================================================*/

void	INTR_redraw_all (void)
{
	RSC_flush_resource ();

/*______________________________________________
 *
 * Affichage de l'interface normale
 *______________________________________________
 */

	switch (INTR_main_screen)
	{

	/* Ecran des patterns */
	case	INTR_MAIN_SCREEN_PATTERN_EDITOR:
		RSC_set_width (RSC_OBJ_MP_BKGD, INTR_graph_ptr->get_width ());
		INTR_pattern_editor_track_ptr->set_width (INTR_graph_ptr->get_width ());
		INTR_pattern_editor_track_ptr->set_height (  INTR_graph_ptr->get_height ()
		                                           - RSC_get_height (RSC_OBJ_MP_BKGD));
		MPAN_display_main_pannel ();
		break;

	/* Editeur de samples */
	case	INTR_MAIN_SCREEN_SAMPLE_EDITOR:
		INTR_sample_editor_interface_ptr->redraw ();
		break;

	/* Editeur de partition */
	case	INTR_MAIN_SCREEN_SCORE_EDITOR:

		/*** A faire ***/

		break;

	/* Editeur de batterie */
	case	INTR_MAIN_SCREEN_DRUM_EDITOR:

		/*** A faire ***/

		break;

	}

/*______________________________________________
 *
 * Affichage des boites de dialogue et
 * autres menus supplementaires
 *______________________________________________
 */

	if (FileSelector::displayed_file_selector_ptr != NULL)
	{
		FileSelector::displayed_file_selector_ptr->redraw ();
	}
	if (ProgressBox::displayed_progress_box_ptr != NULL)
	{
		ProgressBox::displayed_progress_box_ptr->redraw ();
	}
	if (DialBox::displayed_dialog_box_ptr != NULL)
	{
		DialBox::displayed_dialog_box_ptr->redraw ();
	}
	if (Popup::displayed_popup_ptr != NULL)
	{
		Popup::displayed_popup_ptr->redraw ();
	}
	if (EditString::displayed_edit_string_ptr != NULL)
	{
		EditString::displayed_edit_string_ptr->redraw ();
	}
}



/*==========================================================================*/
/*      Nom: INTR_do_waiting_op                                             */
/*      Description: Gere les operations qui attendent que l'utilisateur    */
/*                   ait relache le bouton de la souris (boutons d'action   */
/*                   continue).                                             */
/*      Parametres en entree:                                               */
/*        act_flag: true si on doit forcer les operations a se faire        */
/*                  maintenant.                                             */
/*==========================================================================*/

void	INTR_do_waiting_op (bool act_flag)
{
	if (INTR_waiting_operation_type == 0)
	{
		return;
	}

	if (INTR_mouse.k != 0 && !act_flag)
	{
		return;
	}

	switch (INTR_waiting_operation_type)
	{
	case	INTR_WAITOP_SPL_RPOS:
		SAMP_set_sample_repeat_intr (INTR_CHGTYPE_NONE, 0, false);
		break;

	case	INTR_WAITOP_SPL_RLEN:
		SAMP_set_sample_replen_intr (INTR_CHGTYPE_NONE, 0, false);
		break;

	case	INTR_WAITOP_SPL_DOFFSET:
		SAMP_set_file_data_offset_intr (INTR_CHGTYPE_NONE, 0, false);
		break;

	}
	INTR_waiting_operation_type = 0;
}



/*==========================================================================*/
/*      Nom: INTR_splpos_2_string                                           */
/*      Description: Transforme une position (ou longueur) en samples en    */
/*                   une chaine selon le type d'affichage choisi dans les   */
/*                   preferences.                                           */
/*      Parametres en entree:                                               */
/*        - spl_pos: position en samples.                                   */
/*        - freq_ech: frequence d'echantillonnage (en samples/seconde)      */
/*        - nbr_chan: nombre de cannaux du sample (1, 2 ou plus)            */
/*        - nbr_bytes: nombre d'octets par samples (1 ou 2)                 */
/*      Parametres en entree/sortie:                                        */
/*        - string_0: pointeur sur une chaine de 8 caracteres + 0 final.    */
/*==========================================================================*/

void	INTR_splpos_2_string (char *string_0, long spl_pos, long freq_ech, int nbr_chan, int nbr_bytes)
{
	long		length;
	long		secondes;
	long		minutes;
	long		centiemes;

	length = spl_pos;
	if (INTR_splpos_byte_flag)
	{
		length *= nbr_chan * nbr_bytes;
	}

	switch (INTR_splpos_type)
	{
	case	INTR_SPLPOS_TYPE_HEXA:
		sprintf (string_0, "%8lX", (long)length);
		break;

	case	INTR_SPLPOS_TYPE_DECI:
		sprintf (string_0, "%8lu", (unsigned long)length);
		break;

	case	INTR_SPLPOS_TYPE_MSC:
		if (freq_ech == 0)
		{
			sprintf (string_0, "Freq = 0");
			break;
		}
		minutes = spl_pos / (freq_ech * 60);
		secondes = spl_pos/freq_ech - minutes*60;
		centiemes = (ULWORD)((double)spl_pos*100/freq_ech) - minutes*6000 - secondes*100;
		sprintf (string_0, "%2d:%02d.%02d", minutes, secondes, centiemes);
		break;

	case	INTR_SPLPOS_TYPE_SM:
		if (freq_ech == 0)
		{
			sprintf (string_0, "Freq = 0");
			break;
		}
		sprintf (string_0, "%#8.3g", (double)spl_pos/freq_ech);
		break;

	default:
		sprintf (string_0, "Error.  ");
	}
}



void	INTR_select_splpos_unit (void)
{
	signed int	line;
	signed long	code;
	Popup		popup_menu;

	popup_menu.add_line ("Time (MM:SS.CC)", INTR_SPLPOS_TYPE_MSC << 2);
	popup_menu.add_line ("Seconds (SSS.MMMM)", INTR_SPLPOS_TYPE_SM << 2);
	popup_menu.add_line ("Samples, hexadecimal", (INTR_SPLPOS_TYPE_HEXA << 2) + 1);
	popup_menu.add_line ("Samples, decimal", (INTR_SPLPOS_TYPE_DECI << 2) + 1);
	popup_menu.add_line ("Bytes, hexadecimal", (INTR_SPLPOS_TYPE_HEXA << 2) + 2);
	popup_menu.add_line ("Bytes, decimal", (INTR_SPLPOS_TYPE_DECI << 2) + 2);

	line = popup_menu.select_radio_by_code ((INTR_splpos_type << 2) + (INTR_splpos_byte_flag ? 2 : 1));
	if (line < 0)
	{
		line = popup_menu.select_radio_by_code (INTR_splpos_type << 2);
	}
	code = popup_menu.manage (line);
	if (code >= 0)
	{
		INTR_splpos_type = code >> 2;
		if (code & 0x03)
		{
			INTR_splpos_byte_flag = ((code & 0x02) != 0);
		}
	}
}




/*==========================================================================*/
/*      Nom: INTR_time_2_string                                             */
/*      Description: Convertit un temps en secondes en une chaine suivant   */
/*                   le mode d'affichage demande.                           */
/*      Parametres en entree:                                               */
/*        - unit: mode d'affichage (unite).                                 */
/*        - sec: le temps, en secondes.                                     */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*        - length: longueur max de la chaine en caracteres.                */
/*      Parametres en sortie:                                               */
/*        - string_0: pointeur sur la chaine a convertir. length + 1 octets */
/*                    doivent deja etre reserves.                           */
/*==========================================================================*/

void	INTR_time_2_string (char *string_0, int unit, double sec, int length, double tempo)
{
	int		numerateur;
	int		denominateur;
	int		note;
	double	finetune;
	char		format_0 [15+1];
	char		temp_0 [255+1];

	switch (unit)
	{

	case	INTR_TIME_UNIT_SEC:
		BASE_double_to_float (string_0, sec, length);
		break;

	case	INTR_TIME_UNIT_MSEC:
		BASE_double_to_float (string_0, sec * 1000, length);
		break;

	case	INTR_TIME_UNIT_SPL:
		BASE_double_to_float (string_0, sec * MIX_replay_freq, length);
		break;

	case	INTR_TIME_UNIT_BEATS:
		if (length >= 6)
		{
			if (tempo <= 0)
			{
				const Player &	player = Player::use_instance ();
				tempo = player.get_tempo ();
			}

			if (BASE_double_to_unsigned_q (sec * tempo / 60.0,
			                               numerateur, denominateur, 999, 99))
			{
				sprintf (string_0, "%3d/%2d", numerateur, denominateur);
			}
			else
			{
				strcpy (string_0, "\?\?\?/\?\?");
			}
		}

		else
		{
			*string_0 = '\0';
		}
		break;

	case	INTR_TIME_UNIT_HZ:
		if (! BASE_is_null (sec))
		{
			if (BASE_is_infinite (sec))
			{
				sprintf (string_0, "%*d", length, 0);
			}
			else
			{
				BASE_double_to_float (string_0, 1.0 / sec, length);
			}
		}
		else
		{
			strcpy (string_0, "\?");
		}
		break;

	case	INTR_TIME_UNIT_NOTE:
		if (   BASE_is_null (sec)
		    || BASE_is_infinite (sec))
		{
			strcpy (string_0, "\?");
		}

		else
		{
			const Player &	player = Player::use_instance ();
			note = player.convert_freq_to_note (1.0 / sec, finetune);
			if (note > 0 && note < GTK_NBRNOTES_MAXI)
			{
				if (length >= 8)
				{
					sprintf (format_0, "%%3s %%+.%df", length - 7);
					sprintf (temp_0, format_0, INTR_note_names [note], finetune);
					temp_0 [length] = '\0';
					strcpy (string_0, temp_0);
				}
				else
				{
					strcpy (string_0, INTR_note_names [note]);
				}
			}
			else
			{
				strcpy (string_0, "\?\?\?");
				if (length >= 8)
				{
					strcat (string_0, " +\?.\?");
				}
			}
		}
		break;
	}
}



/*==========================================================================*/
/*      Nom: INTR_string_2_time                                             */
/*      Description: Convertit une chaine temporelle en un temps en         */
/*                   secondes suivant le mode d'affichage demande.          */
/*      Parametres en entree:                                               */
/*        - string_0: pointeur sur la chaine a convertir.                   */
/*        - unit: mode d'affichage (unite).                                 */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*      Parametres en entree/sortie:                                        */
/*        - sec: temps trouve. Si la chaine n'a pas pu etre convertie, ce   */
/*               parametre reste inchange.                                  */
/*      Retour: true si la conversion s'est effectuee correctement.         */
/*==========================================================================*/

bool	INTR_string_2_time (const char *string_0, int unit, double &sec, double tempo)
{
	double	finetune;
	int		num;
	int		denom;
	signed int	note;
	signed int	note_letter;
	signed int	octave;
	char		*temp_0;
	char		trim_0 [255+1];
	const int	halftone_table [7] =
	{
		9, 11, 0, 2, 4, 5, 7
	};

	if (strchr (string_0, '\?') != NULL)
	{
		return (false);
	}

	switch (unit)
	{

	case	INTR_TIME_UNIT_SEC:
		sscanf (string_0, "%lf", &sec);
		break;

	case	INTR_TIME_UNIT_MSEC:
		sscanf (string_0, "%lf", &sec);
		sec /= 1000.0;
		break;

	case	INTR_TIME_UNIT_SPL:
		sscanf (string_0, "%lf", &sec);
		sec /= (double)MIX_replay_freq;
		break;

	case	INTR_TIME_UNIT_BEATS:
		if (tempo <= 0)
		{
			const Player &	player = Player::use_instance ();
			tempo = player.get_tempo ();
		}
		num = (int) strtol (string_0, &temp_0, 10);
		while (   *temp_0 != '\n'
		       && (   *temp_0 < '0'
		           || *temp_0 > '9'))
		{
			temp_0 ++;
		}
		denom = (int) strtol (temp_0, NULL, 10);
		if (denom > 0)
		{
			sec = 60.0 * num / (tempo * denom);
		}
		else
		{
			return (false);
		}
		break;

	case	INTR_TIME_UNIT_HZ:
		sscanf (string_0, "%lf", &sec);
		if (BASE_is_null (sec))
		{
			sec = INFINITY;
		}
		else
		{
			sec = 1.0 / sec;
		}
		break;

	case	INTR_TIME_UNIT_NOTE:
		strcpy (trim_0, string_0);

		/* On recupere la note de base */
		BASE_trim_string (trim_0);
		note_letter = toupper (*trim_0) - 'A';
		*trim_0 = ' ';
		if (   note_letter < 0
		    || note_letter > 7)
		{
			return (false);
		}
		note = halftone_table [note_letter];
		
		/* Regarde s'il y a une diese ou un bemol */
		BASE_trim_string (trim_0);
		if (*trim_0 == '#')
		{
			note ++;
			*trim_0 = ' ';
		}
		else if (tolower (*trim_0) == 'b')
		{
			note --;
			*trim_0 = ' ';
		}

		/* Cherche l'octave */
		octave = 4;
		BASE_trim_string (trim_0);
		temp_0 = trim_0;
		while (   *temp_0 != '\n'
		       && (   *temp_0 < '0'
		           || *temp_0 > '9'))
		{
			temp_0 ++;
		}
		if (*temp_0 != '\n')
		{
			if (temp_0 - trim_0 >= 2)
			{
				/* Format C -1, ou C--1 (le caractere de separation
					apres la note importe peu). */
				if (temp_0 [-1] == '-')
				{
					temp_0 --;
				}
			}
			octave = (signed int) strtol (temp_0, &temp_0, 10) + 2;
		}
		else
		{
			temp_0 = strchr (trim_0, '=');
			if (temp_0 != NULL)
			{
				octave = 0;
				temp_0 ++;
			}
			else
			{
				temp_0 = strchr (trim_0, '-');
				if (temp_0 != NULL)
				{
					octave = 1;
					temp_0 ++;
				}
				else
				{
					temp_0 = trim_0;
				}
			}
		}
		note += octave * 12;

		/* Recupere le finetune */
		if (sscanf (temp_0, "%lf", &finetune) != 1)
		{
			finetune = 0.0;
		}

		/* Retrouve la frequence */
		const Player &	player = Player::use_instance ();
		sec = 1.0 / player.convert_note_to_freq (note, finetune);
		break;
	}

	return (true);
}



/*==========================================================================*/
/*      Nom: INTR_select_time_unit                                          */
/*      Description: Selectionne une unite de temps (ou de frequence) a     */
/*                   l'aide de l'interface.                                 */
/*      Parametres en entree:                                               */
/*        - unit: unite qui sera modifiee, -1 en entree si aucune.          */
/*      Retour: true si on a change l'unite (ou en tout cas pas annule).    */
/*==========================================================================*/

bool	INTR_select_time_unit (int &unit)
{
	signed long	code;
	signed int	line;
	Popup		menu_popup;

	for (int cnt = 0; cnt < INTR_NBR_TIME_UNITS; cnt ++)
	{
		menu_popup.add_line (INTR_time_unit_name_0_ptr [cnt], cnt);
	}

	line = menu_popup.select_radio_by_code (unit);
	code = menu_popup.manage (line);
	if (code >= 0)
	{
		unit = (int) code;
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: INTR_set_time_intr                                             */
/*      Description: Modifie a l'aide de l'interface une valeur temporelle, */
/*                   suivant un parametre (ou autre).                       */
/*      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.                                */
/*        - unit: mode d'affichage (unite).                                 */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*      Parametres en entree/sortie:                                        */
/*        - sec: la valeur a modifier, en secondes.                         */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	INTR_set_time_intr (int type, double value, int object, int unit, double &sec, double tempo)
{
	double	new_value;
	double	freq;
	double	dummy;
	int		note;
	int		num;
	int		denom;
	Popup		popup_menu;

	new_value = sec;

	switch (type)
	{
	/* Position relative */
	case	INTR_CHGTYPE_REL:
		switch (unit)
		{

		case	INTR_TIME_UNIT_SEC:
			new_value += value;
			break;

		case	INTR_TIME_UNIT_MSEC:
			new_value += (double)value / 1000.0;
			break;

		case	INTR_TIME_UNIT_SPL:
			new_value += (double)value / MIX_replay_freq;
			break;

		case	INTR_TIME_UNIT_HZ:
			freq = BASE_safe_double_inversion (new_value);
			freq += value;
			new_value = BASE_safe_double_inversion (freq);
			break;

		case	INTR_TIME_UNIT_BEATS:
			if (tempo <= 0)
			{
				const Player &	player = Player::use_instance ();
				tempo = player.get_tempo ();
			}
			if (BASE_double_to_unsigned_q (sec * tempo / 60.0,
			                               num, denom, 999, 99))
			{
				new_value = 60.0 * (num + value) / (tempo * denom);
			}
			break;

		case	INTR_TIME_UNIT_NOTE:
			{
				const Player &	player = Player::use_instance ();
				note = player.convert_freq_to_note (1.0 / new_value, dummy);
				new_value = 1.0 / player.convert_note_to_freq (note + (int)value, 0.0);
			}
			break;

		}
		break;

	/* Entree au clavier */
	case	INTR_CHGTYPE_KBD:
		EDIT_edit_time (object, unit, new_value, tempo);
		break;

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

	/* Pop-up */
	case	INTR_CHGTYPE_POP:

		/* Rien pour l'instant. */

		break;
	}

	sec = MAX (new_value, 0.0);

	return (0);
}



/*==========================================================================*/
/*      Nom: INTR_freq_2_string                                             */
/*      Description: Convertit une frequence en Hz en une chaine suivant    */
/*                   le mode d'affichage demande.                           */
/*      Parametres en entree:                                               */
/*        - unit: mode d'affichage (unite).                                 */
/*        - freq: la frequence, en Hz.                                      */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*        - length: longueur max de la chaine en caracteres.                */
/*      Parametres en sortie:                                               */
/*        - string_0: pointeur sur la chaine a convertir. length + 1 octets */
/*                    doivent deja etre reserves.                           */
/*==========================================================================*/

void	INTR_freq_2_string (char *string_0, int unit, double freq, int length, double tempo)
{
	double	sec;

	sec = BASE_safe_double_inversion (freq);
	INTR_time_2_string (string_0, unit, sec, length, tempo);
}



/*==========================================================================*/
/*      Nom: INTR_string_2_freq                                             */
/*      Description: Convertit une chaine temporelle en une frequence en    */
/*                   Hz suivant le mode d'affichage demande.                */
/*      Parametres en entree:                                               */
/*        - string_0: pointeur sur la chaine a convertir.                   */
/*        - unit: mode d'affichage (unite).                                 */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*      Parametres en entree/sortie:                                        */
/*        - freq: frequence trouvee. Si la chaine n'a pas pu etre           */
/*               convertie, ce parametre reste inchange.                    */
/*      Retour: true si la conversion s'est effectuee correctement.         */
/*==========================================================================*/

bool	INTR_string_2_freq (const char *string_0, int unit, double &freq, double tempo)
{
	double	sec;
	bool		ret_val;

	sec = BASE_safe_double_inversion (freq);
	ret_val = INTR_string_2_time (string_0, unit, sec, tempo);
	freq = BASE_safe_double_inversion (sec);

	return (ret_val);
}



/*==========================================================================*/
/*      Nom: INTR_set_freq_intr                                             */
/*      Description: Modifie a l'aide de l'interface une valeur             */
/*                   frequencielle suivant un parametre (ou autre).         */
/*      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.                                */
/*        - unit: mode d'affichage (unite).                                 */
/*        - tempo: le tempo demande. negatif = tempo courant.               */
/*      Parametres en entree/sortie:                                        */
/*        - freq: la valeur a modifier, en Hz.                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	INTR_set_freq_intr (int type, double value, int object, int unit, double &freq, double tempo)
{
	double	sec;
	signed int	ret_val;

	sec = BASE_safe_double_inversion (freq);
	ret_val = INTR_set_time_intr (type, value, object, unit, sec, tempo);
	freq = BASE_safe_double_inversion (sec);

	return (ret_val);
}



/*==========================================================================*/
/*      Nom: INTR_stop_song                                                 */
/*      Description: Arrete la song, et met a jour l'affichage.             */
/*==========================================================================*/

void	INTR_stop_song (void)
{
	float		clipping_level;
	char		text_0 [99+1];

	GTK_stop_all ();
	MPAN_display_play_status ();
	INTR_pattern_editor_track_ptr->refresh_dynamic (true);

	Player &			player = Player::use_instance ();
	clipping_level = player.get_max_clipping_level ();
	player.set_max_clipping_level (0);
	if (clipping_level > float (0x007FFFFF))
	{
		sprintf (text_0,
		         "Warning! Clipping required. Maximum output level was %d %%.",
		         (int) (100 * clipping_level / float (0x00800000)));
		MPAN_display_message (text_0);
	}
}



/*==========================================================================*/
/*      Nom: INTR_play_song                                                 */
/*      Description: Joue la song, et met a jour l'affichage.               */
/*      Parametres en entree:                                               */
/*        - cont_flag: true si on doit jouer a partir de la ligne courante, */
/*                     false si on doit joue depuis le debut du pattern.    */
/*==========================================================================*/

void	INTR_play_song (bool cont_flag)
{
	GTK_play_song (cont_flag);
	MPAN_display_play_status ();
}



/*==========================================================================*/
/*      Nom: INTR_play_pattern                                              */
/*      Description: Joue le pattern courant, et met a jour l'affichage.    */
/*      Parametres en entree:                                               */
/*        - cont_flag: true si on doit jouer a partir de la ligne courante, */
/*                     false si le pattern doit etre joue depuis le debut.  */
/*==========================================================================*/

void	INTR_play_pattern (bool cont_flag)
{
	GTK_play_pattern (cont_flag);
	MPAN_display_play_status ();
}



/*==========================================================================*/
/*      Nom: INTR_dialog_box                                                */
/*      Description: Ouvre une boite de dialogue sous la souris et la gere. */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur le titre de la boite, termine par 0.      */
/*        - text_0: pointeur sur le texte de la boite, termine par 0. Il    */
/*                  peut y avoir 8 lignes de texte de 80 caracteres maxi,   */
/*                  separees par un \n.                                     */
/*        - buttons_0: pointeur sur le texte des boutons, termine par un 0. */
/*                     Il peut y avoir 8 boutons de 20 caracteres maxi,     */
/*                     separes par des \n.                                  */
/*        - default_button: Numero du bouton sous lequel doit etre la       */
/*                          souris quand la boite de dialogue apparait, ou  */
/*                          -1 si aucun.                                    */
/*        - cancel_button: Bouton d'annulation par defaut (touche ESC). -1  */
/*                         si aucun.                                        */
/*      Retour: 0 a (n-1): numero du bouton clique, ou -1 en cas d'erreur.  */
/*==========================================================================*/

signed int	INTR_dialog_box (const char *title_0, const char *text_0, const char *buttons_0, signed int default_button, signed int cancel_button)
{
	DialBox	dialog_box (title_0, text_0, buttons_0);

	return (dialog_box.manage (default_button, cancel_button));
}



/*==========================================================================*/
/*      Nom: INTR_progress_box                                              */
/*      Description: Affiche et gere une boite de progression destinee a    */
/*                   montrer l'etat d'avancement d'un thread a              */
/*                   l'utilisateur.                                         */
/*      Parametres en entree:                                               */
/*        - title_0: pointeur sur la chaine de titre de la boite.           */
/*        - total_ptr: pointeur sur un nombre indiquant la longueur totale  */
/*                     de l'operation (unite arbitraire).                   */
/*        - position_ptr: pointeur sur un nombre indiquant la progression   */
/*                        de la tache; ce nombre doit evoluer au cours du   */
/*                        temps entre 0 (0 %) et *total_ptr (100 %).        */
/*        - active_flag_ptr: pointeur sur un flag qui indique si la tache   */
/*                           est active. Ce flag doit etre mis a un avant   */
/*                           de rentrer dans la fonction. Quand la tache se */
/*                           termine (normalement ou a cause d'une erreur), */
/*                           elle doit mettre le flag a 0 pour signaler a   */
/*                           la fonction qu'elle a fini.                    */
/*      Parametres en sortie:                                               */
/*        - cancel_flag_ptr: pointeur sur un flag qui sert a la fonction a  */
/*                           signale a la tache que l'utilisateur a annule  */
/*                           l'operation. Apres avoir recu l'annulation,    */
/*                           *active_flag_ptr doit etre remis a 0 par la    */
/*                           tache. *cancel_flag_ptr doit etre a 0 avant    */
/*                           d'entrer dans la fonction. Si il est interdit  */
/*                           d'annuler,  cancel_flag_ptr doit etre NULL.    */
/*      Retour: 0 si tout s'est bien passe,                                 */
/*              1 en cas d'annulation,                                      */
/*              -1 si une erreur interne s'est produite (sans consequence   */
/*                 sur la tache courante qui se termine normalement).       */
/*==========================================================================*/

signed int	INTR_progress_box (const char *title_0,
				                   const volatile long *total_ptr,
				                   const volatile long *position_ptr,
				                   const volatile bool *active_flag_ptr,
				                   volatile bool *cancel_flag_ptr)
{
	ProgressBox	progress_box (title_0);

	return (progress_box.manage (total_ptr, position_ptr,
	                             active_flag_ptr, cancel_flag_ptr));
}



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

String	INTR_file_selector (const char *path_0, const char *file_0, const char *label_0, const char *format_string_0)
{
	int		exit_button;
	FileSelector	file_selector (path_0, file_0, label_0, format_string_0);

	do
	{
		exit_button = file_selector.manage ();
	}
	while (exit_button == FileSelector::BUTTON_NONE);

	/* Annuler ? */
	if (exit_button != FileSelector::BUTTON_OK)
	{
		return ("");
	}

	/* Recompose le nom de fichier entier */
	return (  file_selector.get_pathname ()
	        + file_selector.get_filename ());
}



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

