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

        GRAOUMF TRACKER 2
        Author: Laurent de Soras, 1996-2016

--- Legal stuff ---

This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.

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




/*\\\ 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	"file.h"
#include	"FileSelector.h"
#include	"fnames.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"log.h"
#include	"memory.h"
#include	"os.h"
#include	"Popup.h"
#include	"resource.h"
#include	"rsc01.h"
#include	"sliders.h"



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

/* Reponses renvoyees par le manager */
const int	FileSelector::BUTTON_NONE = 0;
const int	FileSelector::BUTTON_OK = 1;
const int	FileSelector::BUTTON_CANCEL = 2;



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

const int	FileSelector::NBR_DISP_FILENAMES = 20;	/* Affiche 20 fichiers a la fois */
const int	FileSelector::FILENAME_DISPLEN = 48;	/* Nombre de caracteres affiches pour un nom de fichier (min: 12) */

/* Types de tri des fichiers */
const char	*FileSelector::SORT_TYPES_0 [5] =
{
	"No sort", "By name", "By ext.", "By date", "By size"
};



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



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

/* Pointeur sur l'objet affiche s'il existe, NULL sinon. */
FileSelector	*FileSelector::displayed_file_selector_ptr = NULL;



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



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



/*==========================================================================*/
/*      Nom: (constructeur)                                                 */
/*      Description: Initialise le selecteur de fichiers.                   */
/*==========================================================================*/

FileSelector::FileSelector (const char *path_in_0, const char *file_in_0, const char *label_0, const char *filter_string_0)
{
	int		temp_filter;

	_label_0 = NULL;
	_filter_string_0 = NULL;
	_background_ptr = NULL;

	_nbr_disp_filenames = NBR_DISP_FILENAMES;
	_sort_type = Directory::SORT_BY_NAME;
	_winpos_in_dir = 0;
	_dir_ptr = NULL;
	_display_flag = false;
	_filter_nbr = -1;
	_label_0 = STRDUP (label_0);
	_filter_string_0 = STRDUP (filter_string_0);

	/* Recuperation du masque par defaut */
	strcpy (_current_mask_0, FNAM_get_full_filename (path_in_0).c_str ());
	temp_filter = get_filter_nbr (_current_mask_0);
	if (temp_filter >= 0)
	{
		_filter_nbr = temp_filter;
	}
	else
	{
		_filter_nbr = 0;
	}

	/* Chemin complet */
	strcpy (_current_path_0, complete_path (FNAM_get_full_path (path_in_0).c_str ()).c_str ());

	/* Nom du fichier */
	strcpy (_current_filename_0, file_in_0);
	adapt_fileext_to_filter ();

	/* Lit le repertoire */
	if (read_directory (_current_path_0, _current_mask_0))
	{
		LOG_printf ("FileSelector::FileSelector: Error: couldn't read directory.\n");
		return;	/* Erreur d'allocation memoire */
	}

	/* Initialise le slider */
	_slider.sbar_object = RSC_OBJ_FS_BACK_WINDOW_SBAR;
	_slider.slider_object = RSC_OBJ_FS_BACK_WINDOW_SBAR_SLIDER;
	_slider.virtual_len = 1;
	_slider.virtual_pos = 0;
	_slider.virtual_win = _nbr_disp_filenames;
	_slider.glisse_pos = -1;
	_slider.direction = SLID_DIRECTION_VERTICAL;
	_slider.no_graphic_limit_flag = false;

	/* Calcule la taille du selecteur */
	_win_car_width =   2	/* Bord du la fenetre */
	                 + 2	/* Flag repertoire + espace */
	                 + FILENAME_DISPLEN	/* Le nom */
	                 + 13	/* Longueur fichier: 1 234 567 890 */
	                 + 1	/* Espace */
	                 + 8	/* Date JJ.MM.AA */
	                 + 2;	/* Bord de la fenetre */
	_win_pix_height = _nbr_disp_filenames * (RSC_CHAR_H + RSC_V_SPACE) + RSC_V_SPACE;
	_form_pix_width = (_win_car_width + 4) * RSC_CHAR_W;
	_form_pix_height =   RSC_object_ptr [RSC_OBJ_FS_BACK_WINDOW]->y
	                   + _win_pix_height + RSC_BOXTEXT_H * 2 + RSC_V_SPACE * 2
	                   + RSC_BOXTEXT_H + RSC_V_SPACE * 2;
	_form_pix_x_pos = (  INTR_graph_ptr->get_width ()
	                   - _form_pix_width
	                   - INTR_SHADOW_CARXDEC * RSC_CHAR_W) / 2;
	_form_pix_y_pos = (  INTR_graph_ptr->get_height ()
	                   - _form_pix_height
	                   - INTR_SHADOW_PIXYDEC) / 2;

	/* Capture le decor */
	_background_ptr = INTR_graph_ptr->save_background (_form_pix_x_pos, _form_pix_y_pos,
	                                                  _form_pix_width + INTR_SHADOW_CARXDEC * RSC_CHAR_W,
	                                                  _form_pix_height + INTR_SHADOW_PIXYDEC);
	if (_background_ptr == NULL)
	{
		LOG_printf ("FileSelector::FileSelector: Error: couldn't save background.\n");
		return;		/* Probleme pour sauver le fond */
	}

	_display_flag = true;
	FileSelector::displayed_file_selector_ptr = this;
	redraw ();
	KEYB_flush_keyboard_buffer ();
	RSC_flush_resource ();
}



/*==========================================================================*/
/*      Nom: (destructeur)                                                  */
/*      Description: Detruit le selecteur de fichiers.                      */
/*==========================================================================*/

FileSelector::~FileSelector ()
{
	FileSelector::displayed_file_selector_ptr = NULL;
	_display_flag = false;

	/* Restore le decor */
	if (_background_ptr != NULL)
	{
		INTR_graph_ptr->restore_background (_background_ptr);
		_background_ptr = NULL;
	}

	free_directory ();

	if (_filter_string_0 != NULL)
	{
		FREE (_filter_string_0);
		_filter_string_0 = NULL;
	}

	if (_label_0 != NULL)
	{
		FREE (_label_0);
		_label_0 = NULL;
	}
}



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

signed int	FileSelector::check_ok (void) const
{

	/*** A faire ***/

	return (0);
}



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

void	FileSelector::self_display (void) const
{

	/*** A faire ***/

}



signed int	FileSelector::manage (void)
{
	int		sel_object;
	int		sel_aine;
	signed int	temp_filter;
	signed int	line;
	SLWORD	result;
	char		*mask_0;
	char		*filename_0;
	Popup		*sort_popup_ptr;
	LWORD		key;
	int		scan_code;
	int		index;
	bool		exit_flag;
	const FILE_DIR_ENTRY	*file_info_ptr;

/*______________________________________________
 *
 * Gestion de la souris
 *______________________________________________
 */

	INTR_graph_ptr->show_mouse ();
	INTR_graph_ptr->get_mouse (&INTR_mouse);
	RSC_gere_resource (RSC_OBJ_FS_BACK, &sel_object, &sel_aine);
	switch (sel_object)
	{

	/* Nom du fichier */
	case	RSC_OBJ_FS_BACK_FNAME:
		filename_0 = (char *) MALLOC (FNAM_FILENAME_MAXLEN+1);
		BASE_copy_string (_current_filename_0, filename_0, strlen (_current_filename_0), FNAM_FILENAME_MAXLEN);
		filename_0 [FNAM_FILENAME_MAXLEN] = 0;
		EDIT_edit_string (filename_0, RSC_OBJ_FS_BACK_FNAME, FNAM_FILENAME_MAXLEN, EditString_TYPE_ALPHA);
		BASE_trim_string (filename_0);
		strcpy (_current_filename_0, filename_0);
		FREE (filename_0);
		autoselection ();
		refresh ();
		break;

	/* Lecteur */
	case	RSC_OBJ_FS_BACK_DRIVE:
		select_drive ();
		break;

	/* Masque */
	case	RSC_OBJ_FS_BACK_FILTER:
		/* Selection du filtre */
		if (RSC_mouse_key & 2)
		{
			mask_0 = (char *) MALLOC (FNAM_FILENAME_MAXLEN+1);
			BASE_copy_string (_current_mask_0, mask_0, strlen (_current_mask_0), FNAM_FILENAME_MAXLEN);
			mask_0 [FNAM_FILENAME_MAXLEN] = 0;
			EDIT_edit_string (mask_0, RSC_OBJ_FS_BACK_FILTER, int (strlen (mask_0)), EditString_TYPE_ALPHA);
			BASE_trim_string (mask_0);
			mask_0 = (char *) REALLOC (mask_0, strlen (mask_0) + 1);
			strcpy (_current_mask_0, mask_0);
			FREE (mask_0);
			temp_filter = get_filter_nbr (_current_mask_0);
			if (temp_filter >= 0)
			{
				_filter_nbr = temp_filter;
				adapt_fileext_to_filter ();
			}
		}

		else
		{
			select_filter ();
		}

		read_directory (_current_path_0, _current_mask_0);
		refresh ();
		break;

	/* Type de tri */
	case	RSC_OBJ_FS_BACK_SORT:
		sort_popup_ptr = new Popup;
		if (sort_popup_ptr == NULL)
		{
			LOG_printf ("FileSelector::manage: file sort type pop-up menu creation failed.\n");
			INTR_wait_mouse (true);
			RSC_flush_resource ();
			return (-1);
		}

		sort_popup_ptr->add_line ("No sort", Directory::SORT_NONE);
		sort_popup_ptr->add_line ("By name", Directory::SORT_BY_NAME);
		sort_popup_ptr->add_line ("By ext.", Directory::SORT_BY_EXTENSION);
		sort_popup_ptr->add_line ("By date", Directory::SORT_BY_DATE);
		sort_popup_ptr->add_line ("By size", Directory::SORT_BY_SIZE);
		sort_popup_ptr->add_line ("\n",	0, false, true);
		sort_popup_ptr->add_line ("Reverse", Directory::SORT_INVERTED);

		line = sort_popup_ptr->select_radio_by_code (_sort_type & Directory::SORT_MASK);
		sort_popup_ptr->select_by_line (6, (_sort_type & Directory::SORT_INVERTED) != 0);
		result = sort_popup_ptr->manage (line);
		delete sort_popup_ptr;

		if (result >= 0)
		{
			if ((result & ~Directory::SORT_MASK) == 0)
			{
				_sort_type = (_sort_type & ~Directory::SORT_MASK) | result;
			}
			else if (result == Directory::SORT_INVERTED)
			{
				_sort_type ^= Directory::SORT_INVERTED;
			}
			if ((_sort_type & Directory::SORT_MASK) == Directory::SORT_NONE)
			{
				read_directory (_current_path_0, _current_mask_0);
			}
			_dir_ptr->sort (_sort_type);
			refresh ();
		}
		break;

	/* Repertoire superieur */
	case	RSC_OBJ_FS_BACK_WINDOW_UPDIR:
		close_directory ();
		break;

	/* Chemin */
	case	RSC_OBJ_FS_BACK_WINDOW_PATH:
		INTR_dialog_box ("", "Not implemented.", "Cancel", 0, 0);

		/*** A faire ***/

		break;

	/* Slider */
	case	RSC_OBJ_FS_BACK_WINDOW_SBAR:
	case	RSC_OBJ_FS_BACK_WINDOW_SBAR_SLIDER:
		_slider.virtual_len = _dir_ptr->get_length ();
		_slider.virtual_pos = _winpos_in_dir;
		SLID_gere_slider (&_slider, sel_object);
		if (_slider.changed_pos_flag)
		{
			_winpos_in_dir = _slider.virtual_pos;
			refresh_window ();
		}
		break;

	/* Fleche vers le haut du slider */
	case	RSC_OBJ_FS_BACK_WINDOW_UP:
		_winpos_in_dir --;
		_winpos_in_dir = MIN (_winpos_in_dir, _dir_ptr->get_length () - NBR_DISP_FILENAMES);
		_winpos_in_dir = MAX (_winpos_in_dir, 0);
		refresh_window ();
		break;

	/* Fleche vers le bas du slider */
	case	RSC_OBJ_FS_BACK_WINDOW_DOWN:
		_winpos_in_dir ++;
		_winpos_in_dir = MIN (_winpos_in_dir, _dir_ptr->get_length () - NBR_DISP_FILENAMES);
		_winpos_in_dir = MAX (_winpos_in_dir, 0);
		refresh_window ();
		break;

	/* Fenetre des fichiers */
	case	RSC_OBJ_FS_BACK_WINDOW_FILES:
		if (select_files ())
		{
			RSC_flush_resource ();
			INTR_wait_mouse (true);
			return (BUTTON_OK);
		}
		refresh ();
		break;

	/* Options */
	case	RSC_OBJ_FS_BACK_OPTIONS:
		INTR_dialog_box ("", "Not implemented.", "Cancel", 0, 0);

		/*** A faire ***/

		break;

	/* OK */
	case	RSC_OBJ_FS_BACK_OK:
		RSC_flush_resource ();
		INTR_wait_mouse (true);
		return (BUTTON_OK);
		break;

	/* Cancel */
	case	RSC_OBJ_FS_BACK_CANCEL:
		RSC_flush_resource ();
		INTR_wait_mouse (true);
		return (BUTTON_CANCEL);
		break;
	}

/*______________________________________________
 *
 * Gestion du clavier
 *______________________________________________
 */

	/* Touches */
	if (OS_key_pressed ())
	{
		key = OS_get_key ();
		scan_code = (key >> 16) & 0xFF;

		switch (scan_code)
		{
		case	KEYB_SCANCODE_RETURN:
			exit_flag = true;
			index = get_selected_file_index ();
			if (index >= 0)
			{
				file_info_ptr = _dir_ptr->get_file_info (index);
				if (file_info_ptr->attrib & 0x10)
				{
					enter_directory (file_info_ptr->name_0);
					refresh_window ();
					exit_flag = false;
				}
			}
			if (exit_flag)
			{
				RSC_flush_resource ();
				return (BUTTON_OK);
			}
			break;

		case	KEYB_SCANCODE_ESC:
			RSC_flush_resource ();
			return (BUTTON_CANCEL);
			break;

		case	KEYB_SCANCODE_BACKSPACE:
			close_directory ();
			refresh_window ();
			break;

		case	KEYB_SCANCODE_UP:
			index = get_selected_file_index ();
			select_one_file (index - 1);
			refresh_window ();
			break;

		case	KEYB_SCANCODE_DOWN:
			index = get_selected_file_index ();
			select_one_file (index + 1);
			refresh_window ();
			break;

		case	KEYB_SCANCODE_PAGEUP:
			index = get_selected_file_index ();
			select_one_file (index - NBR_DISP_FILENAMES);
			refresh_window ();
			break;

		case	KEYB_SCANCODE_PAGEDOWN:
			index = get_selected_file_index ();
			select_one_file (index + NBR_DISP_FILENAMES);
			refresh_window ();
			break;

		}
	}

	return (BUTTON_NONE);
}



void		FileSelector::redraw (void)
{
	int		button_pix_y_pos;
	char		bidon_0 [255+1];

	if (! _display_flag)
	{
		return;
	}

	/* Dimension de la fenetre des fichiers */
	RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_FILES, _win_car_width * RSC_CHAR_W);
	RSC_set_height (RSC_OBJ_FS_BACK_WINDOW_FILES, _win_pix_height);

	/* Placement des boutons du haut */
	memset (bidon_0, ' ', 255);
	bidon_0 [_win_car_width - 8] = '\0';
	RSC_set_width (RSC_OBJ_FS_BACK_FNAME, (_win_car_width - 8) * RSC_CHAR_W);
	RSC_set_string (RSC_OBJ_FS_BACK_FNAME, bidon_0);
	memset (bidon_0, ' ', 255);
	bidon_0 [_win_car_width - 16] = '\0';
	RSC_set_width (RSC_OBJ_FS_BACK_FILTER, (_win_car_width - 16) * RSC_CHAR_W);
	RSC_set_string (RSC_OBJ_FS_BACK_FILTER, bidon_0);

	/* Placement des trucs autour de la fenetre */
	memset (bidon_0, ' ', 255);
	bidon_0 [_win_car_width - 4] = '\0';
	RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_PATH, (_win_car_width - 2) * RSC_CHAR_W);
	RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_PATH, bidon_0);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_UP, _win_car_width * RSC_CHAR_W, 0);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_SBAR, _win_car_width * RSC_CHAR_W, RSC_BOXTEXT_H);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_DOWN, _win_car_width * RSC_CHAR_W, _win_pix_height);
	RSC_set_height (RSC_OBJ_FS_BACK_WINDOW_SBAR, _win_pix_height - RSC_BOXTEXT_H);
	memset (bidon_0, ' ', 255);
	bidon_0 [_win_car_width] = '\0';
	RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_STATUS, (_win_car_width + 2) * RSC_CHAR_W);
	RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_STATUS, bidon_0);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_STATUS, 0, RSC_BOXTEXT_H + _win_pix_height);

	/* Placement des boutons du bas */
	button_pix_y_pos =   RSC_object_ptr [RSC_OBJ_FS_BACK_WINDOW]->y
	                   + _win_pix_height + RSC_BOXTEXT_H * 2 + RSC_V_SPACE * 2;
	RSC_set_width (RSC_OBJ_FS_BACK, _form_pix_width);
	RSC_set_height (RSC_OBJ_FS_BACK, _form_pix_height);
	RSC_set_width (RSC_OBJ_FS_SHADOW, _form_pix_width);
	RSC_set_height (RSC_OBJ_FS_SHADOW, _form_pix_height);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_OPTIONS, RSC_CHAR_W, button_pix_y_pos);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_OK, _form_pix_width - 19 * RSC_CHAR_W, button_pix_y_pos);
	RSC_set_relative_object_position (RSC_OBJ_FS_BACK_CANCEL, _form_pix_width - 9 * RSC_CHAR_W, button_pix_y_pos);
	RSC_set_absolute_object_position (RSC_OBJ_FS, _form_pix_x_pos, _form_pix_y_pos);
	RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE, "");
	RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_FILES_INV, 0);

	/* Affichage du formulaire */
	RSC_set_string (RSC_OBJ_FS_BACK_TITLE, _label_0);
	RSC_display_object (RSC_OBJ_FS);

	refresh ();
}



void		FileSelector::refresh (void)
{
	if (_display_flag)
	{
		refresh_misc ();
		refresh_window ();
		refresh_info_bar ();
	}
}



String	FileSelector::get_filename (void) const
{
	return (add_fileext ());
}



String	FileSelector::get_pathname (void) const
{
	return (_current_path_0);
}



String	FileSelector::get_mask (void) const
{
	return (_current_mask_0);
}



signed int	FileSelector::get_filter_nbr (const char *mask_0) const
{
	const char		*current_0;
	char		cmp_mask_0 [FNAM_FILENAME_MAXLEN+1];
	const char		*line_end_0;
	size_t	length;
	int		filter_nbr;

	/* Regarde deja pour le masque courant */
	if (_filter_nbr >= 0)
	{
		get_filter_mask_0 (cmp_mask_0, _filter_nbr);
		if (BASE_search_in_string_nocase (mask_0, cmp_mask_0) >= 0)
		{
			return (_filter_nbr);
		}
	}

	current_0 = _filter_string_0;
	filter_nbr = 0;
	while (*current_0 != '\0')
	{
		/* Saute la chaine descriptive */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			break;
		}
		current_0 = line_end_0 + 1;

		/* Prend le masque */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			strcpy (cmp_mask_0, current_0);
			current_0 += strlen (current_0);
		}
		else
		{
			length = line_end_0 - current_0;
			memcpy (cmp_mask_0, current_0, length);
			cmp_mask_0 [length] = '\0';
			current_0 += length + 1;
		}

		/* Compare avec notre masque */
		if (BASE_search_in_string_nocase (mask_0, cmp_mask_0) >= 0)
		{
			return (filter_nbr);
		}

		filter_nbr ++;
	}

	return (-1);	/* Filtre non trouve */
}



signed int	FileSelector::get_last_valid_filter_nbr (void) const
{
	return (_filter_nbr);
}



void	FileSelector::set_filter_nbr (int filter_nbr)
{
	char		temp_mask_0 [FNAM_FILENAME_MAXLEN+1];

	if (get_filter_mask_0 (temp_mask_0, filter_nbr))
	{
		_filter_nbr = filter_nbr;
	}

	strcpy (_current_mask_0, temp_mask_0);
	read_directory (_current_path_0, _current_mask_0);
	refresh ();
}



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



/*==========================================================================*/
/*      Nom: complete_path                                                  */
/*      Description: Complete un chemin quelconque pour que soient presents */
/*                   le lecteur et le chemin.                               */
/*      Parametres en entree:                                               */
/*        - path_in_0: pointeur sur le chemin a completer, termine par 0.   */
/*      Retour: Chemin complet.                                             */
/*==========================================================================*/

String	FileSelector::complete_path (const char *path_in_0) const
{
	String	path;

	path = FNAM_get_full_path (path_in_0);

	/* Si on n'a pas de chemin, on en prend un par defaut */
	if (path.get_len () == 0)
	{
		/* Le chemin par defaut: la racine du disque C: */
		return ("C:\\");
	}

	return (path);
}



const char	*FileSelector::get_filter_name_0 (int filter_nbr) const
{
	int		filter_cnt;
	const char		*current_0;
	const char		*line_end_0;

	if (filter_nbr < 0)
	{
		return (NULL);
	}

	current_0 = _filter_string_0;
	for (filter_cnt = 0; filter_cnt < filter_nbr; filter_cnt ++)
	{
		/* Saute la chaine descriptive */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			return (NULL);
		}
		current_0 = line_end_0 + 1;

		/* Saute le masque */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			return (NULL);
		}
		else
		{
			current_0 += line_end_0 - current_0 + 1;
		}
	}

	return (current_0);
}



bool	FileSelector::get_filter_mask_0 (char *dest_mask_0, int filter_nbr) const
{
	const char		*current_0;
	const char		*line_end_0;

	/* Par defaut */
	strcpy (dest_mask_0, "*.*");

	/* On se positionne sur le debut du nom du bon filtre */
	current_0 = get_filter_name_0 (filter_nbr);
	if (current_0 == NULL)
	{
		return (false);
	}

	/* Saute le nom */
	line_end_0 = strchr (current_0, '\n');
	if (line_end_0 == NULL)
	{
		return (false);
	}

	current_0 = line_end_0 + 1;
	line_end_0 = strchr (current_0, '\n');

	if (line_end_0 == NULL)
	{
		strcpy (dest_mask_0, current_0);
	}

	else
	{
		memcpy (dest_mask_0, current_0, line_end_0 - current_0);
		dest_mask_0 [line_end_0 - current_0] = '\0';
	}

	return (true);
}



signed int	FileSelector::refresh_misc (void)
{
	size_t	string_len;
	char		path_0 [79+1];
	String	drive;

	/* Nom du fichier */
	RSC_set_string (RSC_OBJ_FS_BACK_FNAME, _current_filename_0);
	RSC_display_object (RSC_OBJ_FS_BACK_FNAME);

	/* Masque de fichier */
	RSC_set_string (RSC_OBJ_FS_BACK_FILTER, _current_mask_0);
	RSC_display_object (RSC_OBJ_FS_BACK_FILTER);

	/* Lecteur courant */
	drive = FNAM_get_drive (_current_path_0);
	RSC_set_string (RSC_OBJ_FS_BACK_DRIVE, drive.c_str ());
	RSC_display_object (RSC_OBJ_FS_BACK_DRIVE);

	/* Nom du chemin */
	string_len = RSC_get_width (RSC_OBJ_FS_BACK_WINDOW_PATH) / RSC_CHAR_W - 1;
	if (strlen (_current_path_0) <= string_len)
	{
		/* On peut l'afficher en entier */
		RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_PATH, _current_path_0);
	}
	else
	{
		/* On doit couper le chemin et mettre "..." au milieu */
		memcpy (path_0, _current_path_0, string_len/2);
		memcpy (path_0 + string_len/2,
				  _current_path_0 + strlen (_current_path_0) - string_len + string_len/2,
				  string_len - string_len/2);
		path_0 [string_len/2 - 1] = '.';
		path_0 [string_len/2] = '.';
		path_0 [string_len/2 + 1] = '.';
		path_0 [string_len] = '\0';
		RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_PATH, path_0);
	}
	RSC_display_object (RSC_OBJ_FS_BACK_WINDOW_PATH);

	/* Type du tri effectue */
	RSC_set_string (RSC_OBJ_FS_BACK_SORT, SORT_TYPES_0 [_sort_type & Directory::SORT_MASK]);
	RSC_display_object (RSC_OBJ_FS_BACK_SORT);

	return (0);
}



signed int	FileSelector::refresh_window (void)
{
	int		nbr_files;
	int		file_count;
	int		file_nbr;
	int		length;
	int		pixypos;
	char		*line_0;
	char		*source;
	char		*dest;
	const FILE_DIR_ENTRY	*dir_entry_ptr;
	char		file_len_0 [10+1];

	if (_dir_ptr == NULL)
	{
		if (read_directory (_current_path_0, _current_mask_0))
		{
			LOG_printf ("FileSelector::refresh_window: Error: couldn't read directory.\n");
			return (-1);
		}
	}

	nbr_files = _dir_ptr->get_length ();

/*______________________________________________
 *
 * La fenetre en elle-meme
 *______________________________________________
 */

	line_0 = (char *) MALLOC (_win_car_width - 2 + 1);	/* Tronque les bords de 1 caractere et ajoute le 0 final */
	if (line_0 == NULL)
	{
		LOG_printf ("FileSelector::refresh_window: Error: couldn't reseve memory for file line.\n");
		return (-1);	/* Erreur d'allocation memoire */
	}

	file_nbr = _winpos_in_dir;
	pixypos = RSC_V_SPACE;

	for (file_count = 0; file_count < NBR_DISP_FILENAMES; file_count ++)
	{
		memset (line_0, ' ', _win_car_width - 2);
		line_0 [_win_car_width - 2] = 0;

		if (file_nbr >= nbr_files)
		{
			RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE,
			                                  RSC_CHAR_W, pixypos);
			RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE, line_0);
			RSC_display_object (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE);
			pixypos += RSC_CHAR_H + RSC_V_SPACE;
			continue;
		}

		dir_entry_ptr = _dir_ptr->get_file_info (file_nbr);

		/* Repertoire */
		if (dir_entry_ptr->attrib & 0x10)
		{
			line_0 [1] = (char) 0xFE;
		}

		/* Nom du fichier */
		strncpy (line_0 + 1 + 2, dir_entry_ptr->name_0, FILENAME_DISPLEN);
		length = (int) strlen (dir_entry_ptr->name_0);
		if (length < FILENAME_DISPLEN)
		{
			memset (line_0 + 1 + 2 + length, ' ', FILENAME_DISPLEN - length);
		}

		/* Taille du fichier */
		if ((dir_entry_ptr->attrib & 0x10) == 0)
		{
			sprintf (file_len_0, "%10ld", (long) dir_entry_ptr->length);
			source = file_len_0;
			dest = line_0 + 1 + 2 + FILENAME_DISPLEN;
			*dest++ = *source++;
			dest ++;
			*dest++ = *source++;
			*dest++ = *source++;
			*dest++ = *source++;
			dest ++;
			*dest++ = *source++;
			*dest++ = *source++;
			*dest++ = *source++;
			dest ++;
			*dest++ = *source++;
			*dest++ = *source++;
			*dest++ = *source++;
			dest ++;
		}
		else
		{
			dest = line_0 + 1 + 2 + FILENAME_DISPLEN + 13 + 1;
		}

		/* La date */
		sprintf (dest, "%02u.%02u.%02u ", dir_entry_ptr->day,
													dir_entry_ptr->month,
													dir_entry_ptr->year % 100);

		/* Affichage */
		RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE,
		                                  RSC_CHAR_W, pixypos);
		RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE, line_0);
		RSC_display_object (RSC_OBJ_FS_BACK_WINDOW_FILES_LINE);

		if (dir_entry_ptr->selected)
		{
			RSC_set_relative_object_position (RSC_OBJ_FS_BACK_WINDOW_FILES_INV,
			                                  RSC_CHAR_W, pixypos);
			RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_FILES_INV, int (strlen (line_0)) * RSC_CHAR_W);
			RSC_display_object (RSC_OBJ_FS_BACK_WINDOW_FILES_INV);
			RSC_set_width (RSC_OBJ_FS_BACK_WINDOW_FILES_INV, 0);
		}

		file_nbr ++;
		pixypos += RSC_CHAR_H + RSC_V_SPACE;
	}
	FREE (line_0);

/*______________________________________________
 *
 * Le slider vertical
 *______________________________________________
 */

	_slider.virtual_len = nbr_files;
	_slider.virtual_pos = _winpos_in_dir;
	_slider.virtual_win = _nbr_disp_filenames;
	SLID_display_slider (&_slider);

	return (0);
}



signed int	FileSelector::refresh_info_bar (void)
{
	char		src_0 [15+1];
	char		dest_0 [47+1];

	/* On cherche le numero du lecteur */
	String         drive_name = FNAM_get_drive (_current_path_0);
	int            drive      = toupper ((drive_name.c_str ()) [0]) - 'A';

	int64_t        free_disk_space;
	int64_t        total_disk_space;
	if (FILE_get_free_disk_space (drive, &free_disk_space, &total_disk_space))
	{
		/* Impossible de connaitre l'espace disk disponible */
		RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_STATUS, "Couldn't get free disk space.");
	}

	else
	{
		static const char * const	unit_0_arr [] =
		{ "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", 0 };

		int				unit = 0;
		while (unit_0_arr [unit + 1] != 0 && free_disk_space > 99999)
		{
			free_disk_space /= 1024;
			++ unit;
		}

		sprintf (dest_0, "%s%s", "Free: 000 000 ", unit_0_arr [unit]);
		sprintf (src_0, "%9ld", long (free_disk_space));
		dest_0 [6]  = src_0 [3];
		dest_0 [7]  = src_0 [4];
		dest_0 [8]  = src_0 [5];
		dest_0 [10] = src_0 [6];
		dest_0 [11] = src_0 [7];
		dest_0 [12] = src_0 [8];
		RSC_set_string (RSC_OBJ_FS_BACK_WINDOW_STATUS, dest_0);
	}

	RSC_display_object (RSC_OBJ_FS_BACK_WINDOW_STATUS);

	return (0);
}



signed int	FileSelector::read_directory (const char *path_in_0, const char *mask_0)
{
	/* S'il n'y avait pas de repertoire en memoire, on en cree un. */
	if (_dir_ptr == NULL)
	{
		_dir_ptr = new Directory;
		if (_dir_ptr == NULL)
		{
			LOG_printf ("FileSelector::read_directory: directory creation failed.\n");
			return (-1);
		}
	}

	INTR_graph_ptr->mouse_bee ();
	
	if (_dir_ptr->build (path_in_0, mask_0))
	{
		LOG_printf ("FileSelector::read_directory: Error: couldn't read directory.\n");
		return (-1);
	}
	
	_dir_ptr->sort (_sort_type);

	INTR_graph_ptr->mouse_arrow ();

	_winpos_in_dir = 0;
	
	return (0);
}



void	FileSelector::free_directory (void)
{
	if (_dir_ptr != NULL)
	{
		delete _dir_ptr;
		_dir_ptr = NULL;
	}
}



/*==========================================================================*/
/*      Nom: select_files                                                   */
/*      Description: Gere la selection d'un fichier a la souris.            */
/*                   Distingue les differents type de clic.                 */
/*      Retour: true si on doit sortir du selecteur de fichier (validation  */
/*              par double-clic), false sinon.                              */
/*==========================================================================*/

bool	FileSelector::select_files (void)
{
	int		click_type;
	int		file_nbr;
	bool		exit_flag;
	const FILE_DIR_ENTRY	*file_info_ptr;

	exit_flag = false;
	file_nbr =   (  INTR_mouse.y
	              - RSC_absolute_object_pixypos [RSC_OBJ_FS_BACK_WINDOW_FILES]
	              - RSC_V_SPACE)
	           / (RSC_CHAR_H+RSC_V_SPACE);
	if (file_nbr >= 0 && file_nbr < NBR_DISP_FILENAMES)
	{
		file_nbr += _winpos_in_dir;
		if (file_nbr < _dir_ptr->get_length ())
		{
			file_info_ptr = _dir_ptr->get_file_info (file_nbr);

			/* C'est un repertoire ? On rentre dedans et c'est tout */
			if (file_info_ptr->attrib & 0x10)
			{
				enter_directory (file_info_ptr->name_0);
				return (false);
			}

			click_type = INTR_test_mouse_click ();
			switch (click_type & 0xFF)
			{

			/* Simple clic */
			case	INTR_CLICK_SINGLE:
			case	INTR_CLICK_HELD:
				/* En cliquant avec le bouton gauche, on annule les
				   precedentes selections */
				if (((click_type >> 8) & 1) != 0)
				{
					deselect_all ();
				}
				_dir_ptr->set_selected_flag (file_nbr, true);
				strcpy (_current_filename_0, file_info_ptr->name_0);
				break;

			/* Double-clic */
			case	INTR_CLICK_DOUBLE:
				_dir_ptr->set_selected_flag (file_nbr, true);
				strcpy (_current_filename_0, file_info_ptr->name_0);
				exit_flag = true;
				break;
			}
		}
	}

	return (exit_flag);
}



void	FileSelector::select_one_file (int index)
{
	int		length;
	const FILE_DIR_ENTRY	*file_info_ptr;

	length = _dir_ptr->get_length ();
	index = MAX (index, 0);
	index = MIN (index, length - 1);
	if (index >= 0)
	{
		deselect_all ();
		_dir_ptr->set_selected_flag (index, true);
		file_info_ptr = _dir_ptr->get_file_info (index);
		if ((file_info_ptr->attrib & 0x10) == 0)
		{
			strcpy (_current_filename_0, file_info_ptr->name_0);
		}
		_winpos_in_dir = MIN (_winpos_in_dir, index);
		_winpos_in_dir = MAX (_winpos_in_dir, index - NBR_DISP_FILENAMES + 1);
		_winpos_in_dir = MAX (_winpos_in_dir, 0);
	}
}



/* Renvoie -1 si pas de selection */
int	FileSelector::get_selected_file_index (void) const
{
	int		index;
	int		length;
	int		cnt;
	const FILE_DIR_ENTRY	*file_info_ptr;

	length = _dir_ptr->get_length ();
	index = -1;
	for (cnt = 0; cnt < length; cnt ++)
	{
		file_info_ptr = _dir_ptr->get_file_info (cnt);
		if (file_info_ptr->selected)
		{
			index = cnt;
			break;
		}
	}

	return (index);
}



/*==========================================================================*/
/*      Nom: autoselection                                                  */
/*      Description: Selectionne ou deselectionne automatiquement suivant   */
/*                   le fichier courant.                                    */
/*==========================================================================*/

void	FileSelector::autoselection (void)
{
	int		nbr_files;
	int		cpt_file;
	bool		selected_flag;

	nbr_files = _dir_ptr->get_length ();

	for (cpt_file = 0; cpt_file < nbr_files; cpt_file ++)
	{
		selected_flag = (BASE_compare_string (_current_filename_0,
			_dir_ptr->get_file_info (cpt_file)->name_0) == 0);
		_dir_ptr->set_selected_flag (cpt_file, selected_flag);
		if (selected_flag)
		{
			_winpos_in_dir = MIN (_winpos_in_dir, cpt_file);
			_winpos_in_dir = MAX (_winpos_in_dir,
			                      cpt_file - NBR_DISP_FILENAMES + 1);
		}
	}
}



/*==========================================================================*/
/*      Nom: select_all                                                     */
/*      Description: Selectionne tous les fichiers du repertoire.           */
/*==========================================================================*/

void	FileSelector::select_all (void)
{
	int		cpt_file;
	int		nbr_files;

	nbr_files = _dir_ptr->get_length ();

	for (cpt_file = 0; cpt_file < nbr_files; cpt_file ++)
	{
		_dir_ptr->set_selected_flag (cpt_file, true);
	}
}



/*==========================================================================*/
/*      Nom: deselect_all                                                   */
/*      Description: Deselectionne tous les fichiers du repertoire.         */
/*==========================================================================*/

void	FileSelector::deselect_all (void)
{
	int		cpt_file;
	int		nbr_files;

	nbr_files = _dir_ptr->get_length ();

	for (cpt_file = 0; cpt_file < nbr_files; cpt_file ++)
	{
		_dir_ptr->set_selected_flag (cpt_file, false);
	}
}



/*==========================================================================*/
/*      Nom: enter_directory                                                */
/*      Description: Rentre dans le repertoire specifie. La directory est   */
/*                   relue et triee.                                        */
/*      Parametres en entree:                                               */
/*        - dir_name_0: pointeur sur le nom du repertoire, se terminant par */
/*                      un 0.                                               */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FileSelector::enter_directory (const char *dir_name_0)
{
	char		*new_path_0;

	new_path_0 = (char *) MALLOC (strlen (_current_path_0) + strlen (dir_name_0) + 1 + 1);
	if (new_path_0 == NULL)
	{
		LOG_printf ("FileSelector::enter_directory: Error: couldn't reseve memory for new path.\n");
		return (-1);	/* Erreur d'allocation memoire */
	}
	strcpy (new_path_0, _current_path_0);
	strcat (new_path_0, dir_name_0);
	strcat (new_path_0, "\\");
	strcpy (_current_path_0, new_path_0);
	FREE (new_path_0);

	read_directory (_current_path_0, _current_mask_0);
	refresh ();
	INTR_wait_mouse (true);

	return (0);
}



/*==========================================================================*/
/*      Nom: close_directory                                                */
/*      Description: Recule d'un repertoire. La directory est relue et      */
/*                   triee.                                                 */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FileSelector::close_directory (void)
{
	char		*new_path_0;
	char		*first_antislash_ptr;
	char		*last_antislash_ptr;

	new_path_0 = STRDUP (_current_path_0);
	if (new_path_0 == NULL)
	{
		LOG_printf ("FileSelector::close_directory: Error: couldn't reseve memory for new path.\n");
		return (-1);	/* Erreur d'allocation memoire */
	}

	first_antislash_ptr = strchr (new_path_0, '\\');
	if (first_antislash_ptr == NULL)
	{
		LOG_printf ("FileSelector::close_directory: Error: incomplete path.\n");
		return (-1);	/* Erreur: le chemin n'est pas complet et absolu */
	}

	last_antislash_ptr = strrchr (new_path_0, '\\');

	if (last_antislash_ptr != first_antislash_ptr)
	{
		*last_antislash_ptr = 0;
		last_antislash_ptr = strrchr (new_path_0, '\\');
		*(last_antislash_ptr+1) = 0;
		new_path_0 = (char *) REALLOC (new_path_0, strlen (new_path_0) + 1);

		strcpy (_current_path_0, new_path_0);
		FREE (new_path_0);
	}

	else
	{
		FREE (new_path_0);
	}

	read_directory (_current_path_0, _current_mask_0);
	refresh ();
	INTR_wait_mouse (true);

	return (0);
}



/*==========================================================================*/
/*      Nom: select_drive                                                   */
/*      Description: Selectionne le lecteur courant via un pop-up.          */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FileSelector::select_drive (void)
{
	int		current_drive;
	int		drive;
	signed int	line;
	LWORD		sel_drive;
	String	drive_name;
	char		line_0 [2+1];
	Popup		drive_popup;

	/* Recherche du lecteur courant */
	drive_name = FNAM_get_drive (_current_path_0);
	current_drive = toupper ((drive_name.c_str ()) [0]) - 'A';

	INTR_graph_ptr->mouse_bee ();
	for (drive = 0; drive < 'Z'-'A'+1; drive ++)
	{
		if (FILE_drive_connected (drive))
		{
			line_0 [0] = 'A' + drive;
			line_0 [1] = ':';
			line_0 [2] = '\0';
			drive_popup.add_line (line_0, drive);
		}
	}
	INTR_graph_ptr->mouse_arrow ();

	/* Selection du lecteur */
	line = drive_popup.select_radio_by_code (current_drive);
	sel_drive = drive_popup.manage (line);

	if (sel_drive >= 0)
	{
		strcpy (_current_path_0, "c:\\");
		_current_path_0 [0] = 'A' + sel_drive;
		read_directory (_current_path_0, _current_mask_0);
		refresh ();
	}
	INTR_wait_mouse (true);

	return (0);
}



signed int	FileSelector::select_filter (void)
{
	char		text_0 [255+1];
	const char		*current_0;
	const char		*line_end_0;
	int		filter_nbr;
	Popup		popup_menu;
	signed int	line;
	signed long	result;

	current_0 = _filter_string_0;
	filter_nbr = 0;
	while (*current_0 != '\0')
	{
		/* La chaine descriptive */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			break;
		}
		memcpy (text_0, current_0, line_end_0 - current_0);
		text_0 [line_end_0 - current_0] = '\0';
		current_0 = line_end_0 + 1;

		/* Saute le masque */
		line_end_0 = strchr (current_0, '\n');
		if (line_end_0 == NULL)
		{
			break;
		}
		else
		{
			current_0 += line_end_0 - current_0 + 1;
		}

		if (popup_menu.add_line (text_0, filter_nbr))
		{
			LOG_printf ("FileSelector::select_filter: Error: couldn't add line to pop-up menu.\n");
			return (-1);
		}

		filter_nbr ++;
	}

	/* Pas de choix dans le menu... */
	if (filter_nbr < 1)
	{
		return (0);
	}

	line = popup_menu.select_radio_by_code (_filter_nbr);
	result = popup_menu.manage (line);
	if (result >= 0)
	{
		set_filter_nbr ((signed int) result);
		adapt_fileext_to_filter ();
	}

	return (0);
}



void	FileSelector::adapt_fileext_to_filter (void)
{
	long		point;
	long		comma;
	char		mask_0 [FNAM_FILENAME_MAXLEN+1];
	String	filter;

	String	filename (_current_filename_0);
	point = filename.find ('.');
	if (point >= 0)
	{
		filename = filename.left (point);

		get_filter_mask_0 (mask_0, _filter_nbr);
		filter = mask_0;
		comma = filter.find (',');
		if (comma >= 0)
		{
			filter = filter.left (comma);
		}
		filter = filter.after (filter.rfind ('.'));
		if (   filter.find ('\?')
		    && filter.find ('*'))
		{
			filename += String (".") + filter;
			strcpy (_current_filename_0, filename.c_str ());
		}
	}
}



String	FileSelector::add_fileext (void) const
{
	long		point;
	long		comma;
	char		mask_0 [FNAM_FILENAME_MAXLEN+1];
	String	filter;

	String	filename (_current_filename_0);
	point = filename.find ('.');
	if (point < 0)
	{
		get_filter_mask_0 (mask_0, _filter_nbr);
		filter = mask_0;
		comma = filter.find (',');
		if (comma >= 0)
		{
			filter = filter.left (comma);
		}
		filter = filter.after (filter.rfind ('.'));
		if (   filter.find ('\?')
		    && filter.find ('*'))
		{
			filename += String (".") + filter;
		}
	}

	return (filename);
}



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



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