Blob Blame History Raw
//
// "$Id: Fl_File_Chooser2.cxx 11813 2016-07-15 20:01:08Z AlbrechtS $"
//
// More Fl_File_Chooser routines.
//
// Copyright 1999-2011 by Michael Sweet.
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems on the following page:
//
//     http://www.fltk.org/str.php
//

// fabien: ATTENTION: Only Out Of Source Gen. because cxx/H files are autogenerated by fluid.
/** \defgroup group_comdlg Common Dialogs classes and functions
    @{
*/
/** \class Fl_File_Chooser
  The Fl_File_Chooser widget displays a standard file selection
  dialog that supports various selection modes.
  
  \image html Fl_File_Chooser.jpg 
  \image latex  Fl_File_Chooser.jpg "Fl_File_Chooser" width=12cm
 
 The Fl_File_Chooser widget transmits UTF-8 encoded filenames to its user. It is
 recommended to open files that may have non-ASCII names with the fl_fopen() or
 fl_open() utility functions that handle these names in a cross-platform way 
 (whereas the standard fopen()/open() functions fail on the MSWindows platform 
 to open files with a non-ASCII name).
 
  The Fl_File_Chooser class also exports several static values
  that may be used to localize or customize the appearance of all file chooser
  dialogs:
  
  <CENTER><TABLE BORDER="1">
  <TR>
  	<TH>Member</TH>
  	<TH>Default value</TH>
  </TR>
  <TR>
  	<TD>add_favorites_label</TD>
  	<TD>"Add to Favorites"</TD>
  </TR>
  <TR>
  	<TD>all_files_label</TD>
  	<TD>"All Files (*)"</TD>
  </TR>
  <TR>
  	<TD>custom_filter_label</TD>
  	<TD>"Custom Filter"</TD>
  </TR>
  <TR>
  	<TD>existing_file_label</TD>
  	<TD>"Please choose an existing file!"</TD>
  </TR>
  <TR>
  	<TD>favorites_label</TD>
  	<TD>"Favorites"</TD>
  </TR>
  <TR>
  	<TD>filename_label</TD>
  	<TD>"Filename:"</TD>
  </TR>
  <TR>
  	<TD>filesystems_label</TD>
  	<TD>"My Computer" (WIN32)<BR>
  	"File Systems" (all others)</TD>
  </TR>
 <TR>
	<TD>hidden_label</TD>
	<TD>"Show hidden files:"</TD>
 </TR>
 <TR>
  	<TD>manage_favorites_label</TD>
  	<TD>"Manage Favorites"</TD>
  </TR>
  <TR>
  	<TD>new_directory_label</TD>
  	<TD>"New Directory?"</TD>
  </TR>
  <TR>
  	<TD>new_directory_tooltip</TD>
  	<TD>"Create a new directory."</TD>
  </TR>
  <TR>
  	<TD>preview_label</TD>
  	<TD>"Preview"</TD>
  </TR>
  <TR>
  	<TD>save_label</TD>
  	<TD>"Save"</TD>
  </TR>
  <TR>
  	<TD>show_label</TD>
  	<TD>"Show:"</TD>
  </TR>
 <TR>
  	<TD>sort</TD>
  	<TD>fl_numericsort</TD>
  </TR>
  </TABLE></CENTER>

  The Fl_File_Chooser::sort member specifies the sort function that is
  used when loading the contents of a directory and can be customized
  at run-time.

  The Fl_File_Chooser class also exports the Fl_File_Chooser::newButton
  and Fl_File_Chooser::previewButton widgets so that application developers
  can control their appearance and use.  For more complex customization,
  consider copying the FLTK file chooser code and changing it accordingly.
*/
/** @} */

/** \fn Fl_File_Chooser::Fl_File_Chooser(const char *pathname, const char *pattern, int type, const char *title)
  The constructor creates the Fl_File_Chooser dialog shown.
  The pathname argument can be a directory name or a
  complete file name (in which case the corresponding file is highlighted
  in the list and in the filename input field.)
  
  The pattern argument can be a NULL
  string or "*" to list all files, or it can be a
  series of descriptions and filter strings separated by tab
  characters (\\t). The format of filters is either
  "Description text (patterns)" or just "patterns". A file chooser
  that provides filters for HTML and image files might look like:
  
  \code
  "HTML Files (*.html)\tImage Files (*.{bmp,gif,jpg,png})"
  \endcode
  
  The file chooser will automatically add the "All Files (*)"
  pattern to the end of the string you pass if you do not provide
  one. The first filter in the string is the default filter.
  
  See the FLTK documentation on fl_filename_match()
  for the kinds of pattern strings that are supported.
  
  The type argument can be one of the following:
  
  \li \c SINGLE - allows the user to select a single, existing file.
  \li \c MULTI - allows the user to select one or more existing files.
  \li \c CREATE - allows the user to select a single, existing file or
         specify a new filename.
  \li \c DIRECTORY - allows the user to select a single, existing directory.
  
  The title argument is used to set the title bar text for the
  Fl_File_Chooser window.
*/

/** \var Fl_File_Chooser::newButton
  The "new directory" button is exported so that application developers
  can control the appearance and use.
*/

/** \var Fl_File_Chooser::previewButton
  The "preview" button is exported so that application developers can
  control the appearance and use.
*/

/** \var Fl_File_Chooser::showHiddenButton
 When checked, hidden files (i.e., filename begins with dot) are displayed.
 
 The "showHiddenButton" button is exported so that application developers can
 control its appearance.
 */

/** \fn Fl_File_Chooser::~Fl_File_Chooser()
  Destroys the widget and frees all memory used by it.*/

/** \fn void Fl_File_Chooser::color(Fl_Color c)
  Sets the background color of the Fl_File_Browser list.*/

/** \fn Fl_Color Fl_File_Chooser::color()
  Gets the background color of the Fl_File_Browser list.*/

/** \fn int Fl_File_Chooser::count()
  Returns the number of selected files.*/

/** \fn void Fl_File_Chooser::directory(const char *pathname)
  Sets the current directory.*/

/** \fn const char *Fl_File_Chooser::directory()
  Gets the current directory.*/

/** \fn void Fl_File_Chooser::filter(const char *pattern)
  Sets or gets the current filename filter patterns. The filter
  patterns use fl_filename_match().
  Multiple patterns can be used by separating them with tabs, like
  <tt>"*.jpg\t*.png\t*.gif\t*"</tt>. In addition, you can provide
  human-readable labels with the patterns inside parenthesis, like
  <tt>"JPEG Files (*.jpg)\tPNG Files (*.png)\tGIF Files (*.gif)\tAll Files (*)"
  </tt>.

  Use filter(NULL) to show all files.
*/

/** \fn const char *Fl_File_Chooser::filter()
  See void filter(const char *pattern)*/

/** \fn void Fl_File_Chooser::filter_value(int f)
  Sets the current filename filter selection.*/

/** \fn int Fl_File_Chooser::filter_value()
  Gets the current filename filter selection.*/

/** \fn void Fl_File_Chooser::hide()
  Hides the Fl_File_Chooser window.*/

/** \fn void Fl_File_Chooser::iconsize(uchar s)
  Sets the size of the icons in the Fl_File_Browser.  By
  default the icon size is set to 1.5 times the textsize().
*/

/** \fn uchar Fl_File_Chooser::iconsize()
  Gets the size of the icons in the Fl_File_Browser.  By
  default the icon size is set to 1.5 times the textsize().
*/

/** \fn void Fl_File_Chooser::label(const char *l)
  Sets the title bar text for the Fl_File_Chooser.*/

/** \fn const char *Fl_File_Chooser::label()
  Gets the title bar text for the Fl_File_Chooser.*/

/** \fn void Fl_File_Chooser::ok_label(const char *l)
  Sets the label for the "ok" button in the Fl_File_Chooser.
*/

/** \fn const char *Fl_File_Chooser::ok_label()
  Gets the label for the "ok" button in the Fl_File_Chooser.
*/

/** \fn int Fl_File_Chooser::preview() const
   Returns the current state of the preview box. */

/** \fn void Fl_File_Chooser::rescan()
  Reloads the current directory in the Fl_File_Browser.*/

/** \fn void Fl_File_Chooser::show()
  Shows the Fl_File_Chooser window.*/

/** \fn void Fl_File_Chooser::textcolor(Fl_Color c)
  Sets the current Fl_File_Browser text color.*/

/** \fn Fl_Color Fl_File_Chooser::textcolor()
  Gets the current Fl_File_Browser text color.*/

/** \fn void Fl_File_Chooser::textfont(Fl_Font f)
  Sets the current Fl_File_Browser text font.*/

/** \fn Fl_Font Fl_File_Chooser::textfont()
  Gets the current Fl_File_Browser text font.*/

/** \fn void Fl_File_Chooser::textsize(Fl_Fontsize s)
  Sets the current Fl_File_Browser text size.*/

/** \fn Fl_Fontsize Fl_File_Chooser::textsize()
  Gets the current Fl_File_Browser text size.*/

/** \fn void Fl_File_Chooser::type(int t)
  Sets the current type of Fl_File_Chooser.*/

/** \fn int Fl_File_Chooser::type()
  Gets the current type of Fl_File_Chooser.*/

/** \fn void Fl_File_Chooser::value(const char *pathname)
  Sets the current value of the selected file.
*/

/** \fn const char *Fl_File_Chooser::value(int f)
 Gets the current value of the selected file(s).
 \p f is a \c 1-based index into a list of
 file names. The number of selected files is returned by
 Fl_File_Chooser::count().
 
 This sample code loops through all selected files:
 \code
 // Get list of filenames user selected from a MULTI chooser
 for ( int t=1; t<=chooser->count(); t++ ) {
 const char *filename = chooser->value(t);
 ...
 }
 \endcode
 */

/** \fn int Fl_File_Chooser::visible()
  Returns 1 if the Fl_File_Chooser window is visible.*/

/** \fn Fl_Widget* Fl_File_Chooser::add_extra(Fl_Widget*)
  Adds extra widget at the bottom of Fl_File_Chooser window.
  Returns pointer for previous extra widget or NULL if not set previously.
  If argument is NULL only remove previous extra widget.

  \note Fl_File_Chooser does \b not delete extra widget in destructor!
     To prevent memory leakage, don't forget to delete unused extra widgets
*/
  /** \fn int Fl_File_Chooser::shown()
    Returns non-zero if the file chooser main window show() has been called (but not hide()
    see Fl_Window::shown()
  */

  /** \fn void Fl_File_Chooser::callback(void (*cb)(Fl_File_Chooser *, void *), void *d = 0)
    Sets the file chooser callback cb and associated data d */

  /** \fn void Fl_File_Chooser::user_data(void *d)
    Sets the file chooser user data d */

  /** \fn void * Fl_File_Chooser::user_data() const
    Gets the file chooser user data */
// *** END OF OUT OF SOURCE DOC ***

// Contents:
//
//   Fl_File_Chooser::count()             - Return the number of selected files.
//   Fl_File_Chooser::directory()         - Set the directory in the file chooser.
//   Fl_File_Chooser::filter()            - Set the filter(s) for the chooser.
//   Fl_File_Chooser::newdir()            - Make a new directory.
//   Fl_File_Chooser::value()             - Return a selected filename.
//   Fl_File_Chooser::rescan()            - Rescan the current directory.
//   Fl_File_Chooser::favoritesButtonCB() - Handle favorites selections.
//   Fl_File_Chooser::fileListCB()        - Handle clicks (and double-clicks)
//                                          in the Fl_File_Browser.
//   Fl_File_Chooser::fileNameCB()        - Handle text entry in the FileBrowser.
//   Fl_File_Chooser::showChoiceCB()      - Handle show selections.
//   compare_dirnames()                   - Compare two directory names.
//   quote_pathname()                     - Quote a pathname for a menu.
//   unquote_pathname()                   - Unquote a pathname from a menu.
//
//   Fl_File_Chooser::add_extra()         - add extra widget at the bottom, return pointer 
//                                          to previous extra widget or NULL if none,
//                                          If argument is NULL extra widget removed.
//                                          NOTE! file chooser does't delete extra widget in 
//                                          destructor! To prevent memory leakage don't forget
//					    delete unused extra widgets by yourself.
//

//
// Include necessary headers.
//

#include <FL/Fl_File_Chooser.H>
#include <FL/filename.H>
#include <FL/fl_ask.H>
#include <FL/x.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/fl_draw.H>

#include <stdio.h>
#include <stdlib.h>
#include "flstring.h"
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>

#if defined(WIN32) && ! defined (__CYGWIN__)
#  include <direct.h>
#  include <io.h>
// Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
// on Windows, which is supposed to be POSIX compliant...
#  define access _access
#  define mkdir _mkdir
// Apparently Borland C++ defines DIRECTORY in <direct.h>, which
// interfers with the Fl_File_Icon enumeration of the same name.
#  ifdef DIRECTORY
#    undef DIRECTORY
#  endif // DIRECTORY
#else
#  include <unistd.h>
#  include <pwd.h>
#endif /* WIN32 */


//
// File chooser label strings and sort function...
//

Fl_Preferences*	Fl_File_Chooser::prefs_ = NULL;

const char	*Fl_File_Chooser::add_favorites_label = "Add to Favorites";
const char	*Fl_File_Chooser::all_files_label = "All Files (*)";
const char	*Fl_File_Chooser::custom_filter_label = "Custom Filter";
const char	*Fl_File_Chooser::existing_file_label = "Please choose an existing file!";
const char	*Fl_File_Chooser::favorites_label = "Favorites";
const char	*Fl_File_Chooser::filename_label = "Filename:";
#ifdef WIN32
const char	*Fl_File_Chooser::filesystems_label = "My Computer";
#else
const char	*Fl_File_Chooser::filesystems_label = "File Systems";
#endif // WIN32
const char	*Fl_File_Chooser::manage_favorites_label = "Manage Favorites";
const char	*Fl_File_Chooser::new_directory_label = "New Directory?";
const char	*Fl_File_Chooser::new_directory_tooltip = "Create a new directory.";
const char	*Fl_File_Chooser::preview_label = "Preview";
const char	*Fl_File_Chooser::save_label = "Save";
const char	*Fl_File_Chooser::show_label = "Show:";
const char      *Fl_File_Chooser::hidden_label = "Show hidden files";
Fl_File_Sort_F	*Fl_File_Chooser::sort = fl_numericsort;


//
// Local functions...
//

static int	compare_dirnames(const char *a, const char *b);
static void	quote_pathname(char *, const char *, int);
static void	unquote_pathname(char *, const char *, int);

// use platform dependent getenv() to get the home directory (STR #3166)
static const char* get_homedir();

//
// 'Fl_File_Chooser::count()' - Return the number of selected files.
//

int				// O - Number of selected files
Fl_File_Chooser::count() {
  int		i;		// Looping var
  int		fcount;		// Number of selected files
  const char	*filename;	// Filename in input field or list


  filename = fileName->value();

  if (!(type_ & MULTI)) {
    // Check to see if the file name input field is blank...
    if (!filename || !filename[0]) return 0;
    else return 1;
  }

  for (i = 1, fcount = 0; i <= fileList->size(); i ++)
    if (fileList->selected(i)) {
      // See if this file is a directory...
      // matt: why would we do that? It is perfectly legal to select multiple
      // directories in a DIR chooser. They are visually selected and value(i)
      // returns all of them as expected
      //filename = (char *)fileList->text(i);

      //if (filename[strlen(filename) - 1] != '/')
	fcount ++;
    }

  if (fcount) return fcount;
  else if (!filename || !filename[0]) return 0;
  else return 1;
}


//
// 'Fl_File_Chooser::directory()' - Set the directory in the file chooser.
//

void
Fl_File_Chooser::directory(const char *d)// I - Directory to change to
{
  char	*dirptr;			// Pointer into directory


//  printf("Fl_File_Chooser::directory(\"%s\")\n", d == NULL ? "(null)" : d);

  // NULL == current directory
  if (d == NULL)
    d = ".";

#ifdef WIN32
  // See if the filename contains backslashes...
  char	*slash;				// Pointer to slashes
  char	fixpath[FL_PATH_MAX];			// Path with slashes converted
  if (strchr(d, '\\')) {
    // Convert backslashes to slashes...
    strlcpy(fixpath, d, sizeof(fixpath));

    for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
      *slash = '/';

    d = fixpath;
  }
#endif // WIN32

  if (d[0] != '\0')
  {
    // Make the directory absolute...
#if (defined(WIN32) && ! defined(__CYGWIN__))|| defined(__EMX__)
    if (d[0] != '/' && d[0] != '\\' && d[1] != ':')
#else
    if (d[0] != '/' && d[0] != '\\')
#endif /* WIN32 || __EMX__ */
      fl_filename_absolute(directory_, d);
    else
      strlcpy(directory_, d, sizeof(directory_));

    // Strip any trailing slash...
    dirptr = directory_ + strlen(directory_) - 1;
    if ((*dirptr == '/' || *dirptr == '\\') && dirptr > directory_)
      *dirptr = '\0';

    // See if we have a trailing .. or . in the filename...
    dirptr = directory_ + strlen(directory_) - 3;
    if (dirptr >= directory_ && strcmp(dirptr, "/..") == 0) {
      // Yes, we have "..", so strip the trailing path...
      *dirptr = '\0';
      while (dirptr > directory_) {
        if (*dirptr == '/') break;
	dirptr --;
      }

      if (dirptr >= directory_ && *dirptr == '/')
        *dirptr = '\0';
    } else if ((dirptr + 1) >= directory_ && strcmp(dirptr + 1, "/.") == 0) {
      // Strip trailing "."...
      dirptr[1] = '\0';
    }
  }
  else
    directory_[0] = '\0';

  if (shown()) {
    // Rescan the directory...
    rescan();
  }
}


//
// 'Fl_File_Chooser::favoritesButtonCB()' - Handle favorites selections.
//

void
Fl_File_Chooser::favoritesButtonCB()
{
  int		v;			// Current selection
  char		pathname[FL_PATH_MAX],		// Pathname
		menuname[FL_PATH_MAX];		// Menu name


  v = favoritesButton->value();

  if (!v) {
    // Add current directory to favorites...
    if (get_homedir()) v = favoritesButton->size() - 5;
    else v = favoritesButton->size() - 4;

    sprintf(menuname, "favorite%02d", v);

    prefs_->set(menuname, directory_);
    prefs_->flush();

    quote_pathname(menuname, directory_, sizeof(menuname));
    favoritesButton->add(menuname);

    if (favoritesButton->size() > 104) {
      ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
    }
  } else if (v == 1) {
    // Manage favorites...
    favoritesCB(0);
  } else if (v == 2) {
    // Filesystems/My Computer
    directory("");
  } else {
    unquote_pathname(pathname, favoritesButton->text(v), sizeof(pathname));
    directory(pathname);
  }
}


//
// 'Fl_File_Chooser::favoritesCB()' - Handle favorites dialog.
//

void
Fl_File_Chooser::favoritesCB(Fl_Widget *w)
					// I - Widget
{
  int		i;			// Looping var
  char		name[32],		// Preference name
		pathname[1024];		// Directory in list


  if (!w) {
    // Load the favorites list...
    favList->clear();
    favList->deselect();

    for (i = 0; i < 100; i ++) {
      // Get favorite directory 0 to 99...
      sprintf(name, "favorite%02d", i);

      prefs_->get(name, pathname, "", sizeof(pathname));

      // Stop on the first empty favorite...
      if (!pathname[0]) break;

      // Add the favorite to the list...
      favList->add(pathname,
                   Fl_File_Icon::find(pathname, Fl_File_Icon::DIRECTORY));
    }

    favUpButton->deactivate();
    favDeleteButton->deactivate();
    favDownButton->deactivate();
    favOkButton->deactivate();

    favWindow->hotspot(favList);
    favWindow->show();
  } else if (w == favList) {
    i = favList->value();
    if (i) {
      if (i > 1) favUpButton->activate();
      else favUpButton->deactivate();

      favDeleteButton->activate();

      if (i < favList->size()) favDownButton->activate();
      else favDownButton->deactivate();
    } else {
      favUpButton->deactivate();
      favDeleteButton->deactivate();
      favDownButton->deactivate();
    }
  } else if (w == favUpButton) {
    i = favList->value();

    favList->insert(i - 1, favList->text(i), favList->data(i));
    favList->remove(i + 1);
    favList->select(i - 1);

    if (i == 2) favUpButton->deactivate();

    favDownButton->activate();

    favOkButton->activate();
  } else if (w == favDeleteButton) {
    i = favList->value();

    favList->remove(i);

    if (i > favList->size()) i --;
    favList->select(i);

    if (i < favList->size()) favDownButton->activate();
    else favDownButton->deactivate();

    if (i > 1) favUpButton->activate();
    else favUpButton->deactivate();

    if (!i) favDeleteButton->deactivate();

    favOkButton->activate();
  } else if (w == favDownButton) {
    i = favList->value();

    favList->insert(i + 2, favList->text(i), favList->data(i));
    favList->remove(i);
    favList->select(i + 1);

    if ((i + 1) == favList->size()) favDownButton->deactivate();

    favUpButton->activate();

    favOkButton->activate();
  } else if (w == favOkButton) {
    // Copy the new list over...
    for (i = 0; i < favList->size(); i ++) {
      // Set favorite directory 0 to 99...
      sprintf(name, "favorite%02d", i);

      prefs_->set(name, favList->text(i + 1));
    }

    // Clear old entries as necessary...
    for (; i < 100; i ++) {
      // Clear favorite directory 0 to 99...
      sprintf(name, "favorite%02d", i);

      prefs_->get(name, pathname, "", sizeof(pathname));

      if (pathname[0]) prefs_->set(name, "");
      else break;
    }

    update_favorites();
    prefs_->flush();

    favWindow->hide();
  }
}


//
// 'Fl_File_Chooser::fileListCB()' - Handle clicks (and double-clicks) in the
//                                   Fl_File_Browser.
//

void
Fl_File_Chooser::fileListCB()
{
  char	*filename,			// New filename
	pathname[FL_PATH_MAX];			// Full pathname to file


  filename = (char *)fileList->text(fileList->value());
  if (!filename)
    return;

  if (!directory_[0]) {
    strlcpy(pathname, filename, sizeof(pathname));
  } else if (strcmp(directory_, "/") == 0) {
    snprintf(pathname, sizeof(pathname), "/%s", filename);
  } else {
    snprintf(pathname, sizeof(pathname), "%s/%s", directory_, filename);
  }

  if (Fl::event_clicks()) {
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
    if ((strlen(pathname) == 2 && pathname[1] == ':') ||
        _fl_filename_isdir_quick(pathname))
#else
    if (_fl_filename_isdir_quick(pathname))
#endif /* WIN32 || __EMX__ */
    {
      // Change directories...
      directory(pathname);

      // Reset the click count so that a click in the same spot won't
      // be treated as a triple-click.  We use a value of -1 because
      // the next click will increment click count to 0, which is what
      // we really want...
      Fl::event_clicks(-1);
    }
    else
    {
      // Hide the window - picked the file...
      window->hide();
      if (callback_) (*callback_)(this, data_);
    }
  }
  else
  {
    // Check if the user clicks on a directory when picking files;
    // if so, make sure only that item is selected...
    filename = pathname + strlen(pathname) - 1;

    if ((type_ & MULTI) && !(type_ & DIRECTORY)) {
      if (*filename == '/') {
	// Clicked on a directory, deselect everything else...
	int i = fileList->value();
	fileList->deselect();
	fileList->select(i);
      } else {
        // Clicked on a file - see if there are other directories selected...
        int i;
	const char *temp;
	for (i = 1; i <= fileList->size(); i ++) {
	  if (i != fileList->value() && fileList->selected(i)) {
	    temp = fileList->text(i);
	    temp += strlen(temp) - 1;
	    if (*temp == '/') break;	// Yes, selected directory
	  }
	}

        if (i <= fileList->size()) {
	  i = fileList->value();
	  fileList->deselect();
	  fileList->select(i);
	}
      }
    }
    // Strip any trailing slash from the directory name...
    if (*filename == '/') *filename = '\0';

//    puts("Setting fileName from fileListCB...");
    fileName->value(pathname);

    // Update the preview box...
    Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this);
    Fl::add_timeout(1.0, (Fl_Timeout_Handler)previewCB, this);

    // Do any callback that is registered...
    if (callback_) (*callback_)(this, data_);

    // Activate the OK button as needed...
    if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY))
      okButton->activate();
    else
      okButton->deactivate();
  }
}


//
// 'Fl_File_Chooser::fileNameCB()' - Handle text entry in the FileBrowser.
//

void
Fl_File_Chooser::fileNameCB()
{
  char		*filename,	// New filename
		*slash,		// Pointer to trailing slash
		pathname[FL_PATH_MAX],	// Full pathname to file
		matchname[FL_PATH_MAX];	// Matching filename
  int		i,		// Looping var
		min_match,	// Minimum number of matching chars
		max_match,	// Maximum number of matching chars
		num_files,	// Number of files in directory
		first_line;	// First matching line
  const char	*file;		// File from directory

//  puts("fileNameCB()");
//  printf("Event: %s\n", fl_eventnames[Fl::event()]);

  // Get the filename from the text field...
  filename = (char *)fileName->value();

  if (!filename || !filename[0]) {
    okButton->deactivate();
    return;
  }

  // Expand ~ and $ variables as needed...
  if (strchr(filename, '~') || strchr(filename, '$')) {
    fl_filename_expand(pathname, sizeof(pathname), filename);
    filename = pathname;
    value(pathname);
  }

  // Make sure we have an absolute path...
#if (defined(WIN32) && !defined(__CYGWIN__)) || defined(__EMX__)
  if (directory_[0] != '\0' && filename[0] != '/' &&
      filename[0] != '\\' &&
      !(isalpha(filename[0] & 255) && (!filename[1] || filename[1] == ':'))) {
#else
  if (directory_[0] != '\0' && filename[0] != '/') {
#endif /* WIN32 || __EMX__ */
    fl_filename_absolute(pathname, sizeof(pathname), filename);
    value(pathname);
    fileName->mark(fileName->position()); // no selection after expansion
  } else if (filename != pathname) {
    // Finally, make sure that we have a writable copy...
    strlcpy(pathname, filename, sizeof(pathname));
  }

  filename = pathname;

  // Now process things according to the key pressed...
  if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) {
    // Enter pressed - select or change directory...
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
    if ((isalpha(pathname[0] & 255) && pathname[1] == ':' && !pathname[2]) ||
        (_fl_filename_isdir_quick(pathname) &&
	 compare_dirnames(pathname, directory_))) {
#else
    if (_fl_filename_isdir_quick(pathname) &&
	compare_dirnames(pathname, directory_)) {
#endif /* WIN32 || __EMX__ */
      directory(pathname);
    } else if ((type_ & CREATE) || access(pathname, 0) == 0) {
      if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY)) {
	// Update the preview box...
	update_preview();

	// Do any callback that is registered...
	if (callback_) (*callback_)(this, data_);

	// Hide the window to signal things are done...
	window->hide();
      }
    } else {
      // File doesn't exist, so beep at and alert the user...
      fl_alert("%s",existing_file_label);
    }
  }
  else if (Fl::event_key() != FL_Delete &&
           Fl::event_key() != FL_BackSpace) {
    // Check to see if the user has entered a directory...
    if ((slash = strrchr(pathname, '/')) == NULL)
      slash = strrchr(pathname, '\\');

    if (!slash) return;

    // Yes, change directories if necessary...
    *slash++ = '\0';
    filename = slash;

#if defined(WIN32) || defined(__EMX__)
    if (strcasecmp(pathname, directory_) &&
        (pathname[0] || strcmp("/", directory_))) {
#else
    if (strcmp(pathname, directory_) &&
        (pathname[0] || strcmp("/", directory_))) {
#endif // WIN32 || __EMX__
      int p = fileName->position();
      int m = fileName->mark();

      directory(pathname);

      if (filename[0]) {
	char tempname[FL_PATH_MAX];

	snprintf(tempname, sizeof(tempname), "%s/%s", directory_, filename);
	fileName->value(tempname);
	strlcpy(pathname, tempname, sizeof(pathname));
      }

      fileName->position(p, m);
    }

    // Other key pressed - do filename completion as possible...
    num_files  = fileList->size();
    min_match  = (int) strlen(filename);
    max_match  = min_match + 1;
    first_line = 0;

    for (i = 1; i <= num_files && max_match > min_match; i ++) {
      file = fileList->text(i);

#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
      if (strncasecmp(filename, file, min_match) == 0) {
#else
      if (strncmp(filename, file, min_match) == 0) {
#endif // WIN32 || __EMX__
        // OK, this one matches; check against the previous match
	if (!first_line) {
	  // First match; copy stuff over...
	  strlcpy(matchname, file, sizeof(matchname));
	  max_match = (int) strlen(matchname);

          // Strip trailing /, if any...
	  if (matchname[max_match - 1] == '/') {
	    max_match --;
	    matchname[max_match] = '\0';
	  }

	  // And then make sure that the item is visible
          fileList->topline(i);
	  first_line = i;
	} else {
	  // Succeeding match; compare to find maximum string match...
	  while (max_match > min_match)
#if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
	    if (strncasecmp(file, matchname, max_match) == 0)
#else
	    if (strncmp(file, matchname, max_match) == 0)
#endif // WIN32 || __EMX__
	      break;
	    else
	      max_match --;

          // Truncate the string as needed...
          matchname[max_match] = '\0';
	}
      }
    }

    // If we have any matches, add them to the input field...
    if (first_line > 0 && min_match == max_match &&
        max_match == (int)strlen(fileList->text(first_line))) {
      // This is the only possible match...
      fileList->deselect(0);
      fileList->select(first_line);
      fileList->redraw();
    } else if (max_match > min_match && first_line) {
      // Add the matching portion...
      fileName->replace( 
			(int) (filename - pathname), 
			(int) (filename - pathname + min_match),
			matchname);

      // Highlight it with the cursor at the end of the selection so
      // s/he can press the right arrow to accept the selection
      // (Tab and End also do this for both cases.)
      fileName->position(
			 (int) (filename - pathname + max_match),
			 (int) (filename - pathname + min_match));
    } else if (max_match == 0) {
      fileList->deselect(0);
      fileList->redraw();
    }

    // See if we need to enable the OK button...
    if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
        (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
      okButton->activate();
    } else {
      okButton->deactivate();
    }
  } else {
    // FL_Delete or FL_BackSpace
    fileList->deselect(0);
    fileList->redraw();
    if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
        (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
      okButton->activate();
    } else {
      okButton->deactivate();
    }
  }
}


//
// 'Fl_File_Chooser::filter()' - Set the filter(s) for the chooser.
//

void
Fl_File_Chooser::filter(const char *p)		// I - Pattern(s)
{
  char		*copyp,				// Copy of pattern
		*start,				// Start of pattern
		*end;				// End of pattern
  int		allfiles;			// Do we have a "*" pattern?
  char		temp[FL_PATH_MAX];			// Temporary pattern string


  // Make sure we have a pattern...
  if (!p || !*p) p = "*";

  // Copy the pattern string...
  copyp = strdup(p);

  // Separate the pattern string as necessary...
  showChoice->clear();

  for (start = copyp, allfiles = 0; start && *start; start = end) {
    end = strchr(start, '\t');
    if (end) *end++ = '\0';

    if (strcmp(start, "*") == 0) {
      showChoice->add(all_files_label);
      allfiles = 1;
    } else {
      quote_pathname(temp, start, sizeof(temp));
      showChoice->add(temp);
      if (strstr(start, "(*)") != NULL) allfiles = 1;
    }
  }

  free(copyp);

  if (!allfiles) showChoice->add(all_files_label);

  showChoice->add(custom_filter_label);
  
  showChoice->value(0);
  showChoiceCB();
}


//
// 'Fl_File_Chooser::newdir()' - Make a new directory.
//

void
Fl_File_Chooser::newdir()
{
  const char	*dir;		// New directory name
  char		pathname[FL_PATH_MAX];	// Full path of directory


  // Get a directory name from the user
  if ((dir = fl_input("%s", NULL, new_directory_label)) == NULL)
    return;

  // Make it relative to the current directory as needed...
#if (defined(WIN32) && ! defined (__CYGWIN__)) || defined(__EMX__)
  if (dir[0] != '/' && dir[0] != '\\' && dir[1] != ':')
#else
  if (dir[0] != '/' && dir[0] != '\\')
#endif /* WIN32 || __EMX__ */
    snprintf(pathname, sizeof(pathname), "%s/%s", directory_, dir);
  else
    strlcpy(pathname, dir, sizeof(pathname));

  // Create the directory; ignore EEXIST errors...
#if defined(WIN32) && ! defined (__CYGWIN__)
  if (mkdir(pathname))
#else
  if (mkdir(pathname, 0777))
#endif /* WIN32 */
    if (errno != EEXIST)
    {
      fl_alert("%s", strerror(errno));
      return;
    }

  // Show the new directory...
  directory(pathname);
}



/** Enable or disable the preview tile. 1 = enable preview, 0 = disable preview. */
void Fl_File_Chooser::preview(int e)
{
  previewButton->value(e);
  prefs_->set("preview", e);
  prefs_->flush();

  Fl_Group *p = previewBox->parent();
  if (e) {
    int w = p->w() * 2 / 3;
    fileList->resize(fileList->x(), fileList->y(),
                     w, fileList->h());
    previewBox->resize(fileList->x()+w, previewBox->y(),
                       p->w()-w, previewBox->h());
    previewBox->show();
    update_preview();
  } else {
    fileList->resize(fileList->x(), fileList->y(),
                     p->w(), fileList->h());
    previewBox->resize(p->x()+p->w(), previewBox->y(),
                       0, previewBox->h());
    previewBox->hide();
  }
  p->init_sizes();

  fileList->parent()->redraw();
}


//
// 'Fl_File_Chooser::previewCB()' - Timeout handler for the preview box.
//

void
Fl_File_Chooser::previewCB(Fl_File_Chooser *fc) {	// I - File chooser
  fc->update_preview();
}


//
// 'Fl_File_Chooser::rescan()' - Rescan the current directory.
//

void
Fl_File_Chooser::rescan()
{
  char	pathname[FL_PATH_MAX];		// New pathname for filename field


  // Clear the current filename
  strlcpy(pathname, directory_, sizeof(pathname));
  if (pathname[0] && pathname[strlen(pathname) - 1] != '/') {
    strlcat(pathname, "/", sizeof(pathname));
  }
//  puts("Setting fileName in rescan()");
  fileName->value(pathname);

  if (type_ & DIRECTORY)
    okButton->activate();
  else
    okButton->deactivate();

  // Build the file list...
  fileList->load(directory_, sort);
#ifndef WIN32	
  if (!showHiddenButton->value()) remove_hidden_files();
#endif
  // Update the preview box...
  update_preview();
}

//
/**  
  Rescan the current directory  without clearing the filename, 
  then select the file if it is in the list
*/
void Fl_File_Chooser::rescan_keep_filename()
{
  // if no filename was set, this is likely a diretory browser
  const char *fn = fileName->value();
  if (!fn || !*fn || fn[strlen(fn) - 1]=='/') {
    rescan();
    return;
  }

  int   i;
  char	pathname[FL_PATH_MAX];		// New pathname for filename field
  strlcpy(pathname, fn, sizeof(pathname));

  // Build the file list...
  fileList->load(directory_, sort);
#ifndef WIN32	
  if (!showHiddenButton->value()) remove_hidden_files();
#endif
  // Update the preview box...
  update_preview();

  // and select the chosen file
  char found = 0;
  char *slash = strrchr(pathname, '/');
  if (slash) 
    slash++;
  else
    slash = pathname;
  for (i = 1; i <= fileList->size(); i ++)
#if defined(WIN32) || defined(__EMX__)
    if (strcasecmp(fileList->text(i), slash) == 0) {
#else
    if (strcmp(fileList->text(i), slash) == 0) {
#endif // WIN32 || __EMX__
      fileList->topline(i);
      fileList->select(i);
      found = 1;
      break;
    }

  // update OK button activity
  if (found || type_ & CREATE)
    okButton->activate();
  else
    okButton->deactivate();
}


//
// 'Fl_File_Chooser::showChoiceCB()' - Handle show selections.
//

void
Fl_File_Chooser::showChoiceCB()
{
  const char	*item,			// Selected item
		*patstart;		// Start of pattern
  char		*patend;		// End of pattern
  char		temp[FL_PATH_MAX];		// Temporary string for pattern


  item = showChoice->text(showChoice->value());

  if (strcmp(item, custom_filter_label) == 0) {
    if ((item = fl_input("%s", pattern_, custom_filter_label)) != NULL) {
      strlcpy(pattern_, item, sizeof(pattern_));

      quote_pathname(temp, item, sizeof(temp));
      showChoice->add(temp);
      showChoice->value(showChoice->size() - 2);
    }
  } else if ((patstart = strchr(item, '(')) == NULL) {
    strlcpy(pattern_, item, sizeof(pattern_));
  } else {
    strlcpy(pattern_, patstart + 1, sizeof(pattern_));
    if ((patend = strrchr(pattern_, ')')) != NULL) *patend = '\0';
  }

  fileList->filter(pattern_);

  if (shown()) {
    // Rescan the directory...
    rescan_keep_filename();
  }
}


//
// 'Fl_File_Chooser::update_favorites()' - Update the favorites menu.
//

void
Fl_File_Chooser::update_favorites()
{
  int		i;			// Looping var
  char		pathname[FL_PATH_MAX],		// Pathname
		menuname[2048];		// Menu name
  const char	*home;			// Home directory


  favoritesButton->clear();
  favoritesButton->add("bla");
  favoritesButton->clear();
  favoritesButton->add(add_favorites_label, FL_ALT + 'a', 0);
  favoritesButton->add(manage_favorites_label, FL_ALT + 'm', 0, 0, FL_MENU_DIVIDER);
  favoritesButton->add(filesystems_label, FL_ALT + 'f', 0);

  if ((home = get_homedir()) != NULL) {
    quote_pathname(menuname, home, sizeof(menuname));
    favoritesButton->add(menuname, FL_ALT + 'h', 0);
  }

  for (i = 0; i < 100; i ++) {
    sprintf(menuname, "favorite%02d", i);
    prefs_->get(menuname, pathname, "", sizeof(pathname));
    if (!pathname[0]) break;

    quote_pathname(menuname, pathname, sizeof(menuname));

    if (i < 10) favoritesButton->add(menuname, FL_ALT + '0' + i, 0);
    else favoritesButton->add(menuname);
  }

  if (i == 100) ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
}


//
// 'Fl_File_Chooser::update_preview()' - Update the preview box...
//

void
Fl_File_Chooser::update_preview()
{
  const char		*filename;	// Current filename
  const char            *newlabel = 0;  // New label text
  Fl_Shared_Image	*image = 0,     // New image
			*oldimage;	// Old image
  int			pbw, pbh;	// Width and height of preview box
  int			w, h;		// Width and height of preview image
  int                   set = 0;        // Set this flag as soon as a decent preview is found

  if (!previewButton->value()) return;

  filename = value();
  if (filename == NULL) {
    // no file name at all, so we have an empty preview
    set = 1;
  } else if (fl_filename_isdir(filename)) {
    // filename is a directory, show a folder icon
    newlabel = "@fileopen";
    set = 1;
  } else {
    struct stat s;
    if (fl_stat(filename, &s)==0) {
      if ((s.st_mode&S_IFMT)!=S_IFREG) {
        // this is no regular file, probably some kind of device
        newlabel = "@-3refresh"; // a cross
        set = 1;
      } else if (s.st_size==0) {
        // this file is emty
        newlabel = "<empty file>";
        set = 1;
      } else {
        // if this file is an image, try to load it
        window->cursor(FL_CURSOR_WAIT);
        Fl::check();
        
        image = Fl_Shared_Image::get(filename);
        
        if (image) {
          window->cursor(FL_CURSOR_DEFAULT);
          Fl::check();
          set = 1;
        }
      }
    }
  }

  oldimage = (Fl_Shared_Image *)previewBox->image();

  if (oldimage) oldimage->release();

  previewBox->image(0);

  if (!set) {
    FILE	*fp;
    int		bytes;
    char	*ptr;

    if (filename) fp = fl_fopen(filename, "rb");
    else fp = NULL;

    if (fp != NULL) {
      // Try reading the first 1k of data for a label...
      bytes = (int) fread(preview_text_, 1, sizeof(preview_text_) - 1, fp);
      preview_text_[bytes] = '\0';
      fclose(fp);
    } else {
      // Assume we can't read any data...
      preview_text_[0] = '\0';
    }

    window->cursor(FL_CURSOR_DEFAULT);
    Fl::check();

    // Scan the buffer for printable UTF8 chars...
    for (ptr = preview_text_; *ptr; ptr++) {
      uchar c = uchar(*ptr);
      if ( (c&0x80)==0 ) {
        if (!isprint(c&255) && !isspace(c&255)) break;
      } else if ( (c&0xe0)==0xc0 ) {
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
      } else if ( (c&0xf0)==0xe0 ) {
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
      } else if ( (c&0xf8)==0xf0 ) {
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
        if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
        ptr++;
      }
    } 
//         *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
//	 ptr ++);

    // Scan the buffer for printable characters in 8 bit
    if (*ptr || ptr == preview_text_) {
      for (ptr = preview_text_;
         *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
	 ptr ++) {/*empty*/}
    }

    if (*ptr || ptr == preview_text_) {
      // Non-printable file, just show a big ?...
      previewBox->label(filename ? "?" : 0);
      previewBox->align(FL_ALIGN_CLIP);
      previewBox->labelsize(75);
      previewBox->labelfont(FL_HELVETICA);
    } else {
      // Show the first 1k of text...
      int size = previewBox->h() / 20;
      if (size < 6) size = 6;
      else if (size > FL_NORMAL_SIZE) size = FL_NORMAL_SIZE;

      previewBox->label(preview_text_);
      previewBox->align((Fl_Align)(FL_ALIGN_CLIP | FL_ALIGN_INSIDE |
                                   FL_ALIGN_LEFT | FL_ALIGN_TOP));
      previewBox->labelsize(size);
      previewBox->labelfont(FL_COURIER);
    }
  } else if (image && ( (image->w() <= 0) ||
                        (image->h() <= 0) ||
                        (image->d() < 0)  ||
                        (image->count() <= 0))) {
    // Image has errors? Show big 'X'
    previewBox->label("X");
    previewBox->align(FL_ALIGN_CLIP);
    previewBox->labelsize(70);
    previewBox->labelfont(FL_HELVETICA);
    previewBox->redraw();
  } else if (image) {
    pbw = previewBox->w() - 20;
    pbh = previewBox->h() - 20;

    if (image->w() > pbw || image->h() > pbh) {
      w   = pbw;
      h   = w * image->h() / image->w();

      if (h > pbh) {
	h = pbh;
	w = h * image->w() / image->h();
      }

      oldimage = (Fl_Shared_Image *)image->copy(w, h);
      previewBox->image((Fl_Image *)oldimage);

      image->release();
    } else {
      previewBox->image((Fl_Image *)image);
    }

    previewBox->align(FL_ALIGN_CLIP);
    previewBox->label(0);
  } else if (newlabel) {
    previewBox->label(newlabel);
    previewBox->align(FL_ALIGN_CLIP);
    previewBox->labelsize(newlabel[0]=='@'?75:12);
    previewBox->labelfont(FL_HELVETICA);
  }

  previewBox->redraw();
}


//
// 'Fl_File_Chooser::value()' - Return a selected filename.
//

const char *			// O - Filename or NULL
Fl_File_Chooser::value(int f)	// I - File number
{
  int		i;		// Looping var
  int		fcount;		// Number of selected files
  const char	*name;		// Current filename
  static char	pathname[FL_PATH_MAX];	// Filename + directory


  name = fileName->value();

  if (!(type_ & MULTI)) {
    // Return the filename in the filename field...
    if (!name || !name[0]) return NULL;
    else return name;
  }

  // Return a filename from the list...
  for (i = 1, fcount = 0; i <= fileList->size(); i ++)
    if (fileList->selected(i)) {
      // See if this file is a selected file/directory...
      name = fileList->text(i);

      fcount ++;

      if (fcount == f) {
	if (directory_[0]) {
	  snprintf(pathname, sizeof(pathname), "%s/%s", directory_, name);
	} else {
	  strlcpy(pathname, name, sizeof(pathname));
	}

	return pathname;
      }
    }

  // If nothing is selected, use the filename field...
  if (!name || !name[0]) return NULL;
  else return name;
}


//
// 'Fl_File_Chooser::value()' - Set the current filename.
//

void
Fl_File_Chooser::value(const char *filename)
					// I - Filename + directory
{
  int	i,				// Looping var
  	fcount;				// Number of items in list
  char	*slash;				// Directory separator
  char	pathname[FL_PATH_MAX];		// Local copy of filename


//  printf("Fl_File_Chooser::value(\"%s\")\n", filename == NULL ? "(null)" : filename);

  // See if the filename is the "My System" directory...
  if (filename == NULL || !filename[0]) {
    // Yes, just change the current directory...
    directory(filename);
    fileName->value("");
    okButton->deactivate();
    return;
  }

#ifdef WIN32
  // See if the filename contains backslashes...
  char	fixpath[FL_PATH_MAX];			// Path with slashes converted
  if (strchr(filename, '\\')) {
    // Convert backslashes to slashes...
    strlcpy(fixpath, filename, sizeof(fixpath));

    for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
      *slash = '/';

    filename = fixpath;
  }
#endif // WIN32

  // See if there is a directory in there...
  fl_filename_absolute(pathname, sizeof(pathname), filename);

  if ((slash = strrchr(pathname, '/')) != NULL) {
    // Yes, change the display to the directory... 
    if (!fl_filename_isdir(pathname)) *slash++ = '\0';

    directory(pathname);
    if (*slash == '/') slash = pathname;
  } else {
    directory(".");
    slash = pathname;
  }

  // Set the input field to the absolute path...
  if (slash > pathname) slash[-1] = '/';

  fileName->value(pathname);
  fileName->position(0, (int) strlen(pathname));
  okButton->activate();

  // Then find the file in the file list and select it...
  fcount = fileList->size();

  fileList->deselect(0);
  fileList->redraw();

  for (i = 1; i <= fcount; i ++)
#if defined(WIN32) || defined(__EMX__)
    if (strcasecmp(fileList->text(i), slash) == 0) {
#else
    if (strcmp(fileList->text(i), slash) == 0) {
#endif // WIN32 || __EMX__
//      printf("Selecting line %d...\n", i);
      fileList->topline(i);
      fileList->select(i);
      break;
    }
}
  
void Fl_File_Chooser::show()
{
  window->hotspot(fileList);
  window->show();
  Fl::flush();
  fl_cursor(FL_CURSOR_WAIT);
  rescan_keep_filename();
  fl_cursor(FL_CURSOR_DEFAULT);
  fileName->take_focus();
#ifdef WIN32
  showHiddenButton->hide();
#endif
}

void Fl_File_Chooser::showHidden(int value)
{
  if (value) {
    fileList->load(directory());
  } else {
    remove_hidden_files();
    fileList->redraw();
  }
}
  
void Fl_File_Chooser::remove_hidden_files()
{
  int count = fileList->size();
  for(int num = count; num >= 1; num--) {
    const char *p = fileList->text(num);
    if (*p == '.' && strcmp(p, "../") != 0) fileList->remove(num);
  }
  fileList->topline(1);
}

  
//
// 'compare_dirnames()' - Compare two directory names.
//

static int
compare_dirnames(const char *a, const char *b) {
  int alen, blen;

  // Get length of each string...
  alen = (int) (strlen(a) - 1);
  blen = (int) (strlen(b) - 1);

  if (alen < 0 || blen < 0) return alen - blen;

  // Check for trailing slashes...
  if (a[alen] != '/') alen ++;
  if (b[blen] != '/') blen ++;

  // If the lengths aren't the same, then return the difference...
  if (alen != blen) return alen - blen;

  // Do a comparison of the first N chars (alen == blen at this point)...
#ifdef WIN32
  return strncasecmp(a, b, alen);
#else
  return strncmp(a, b, alen);
#endif // WIN32
}


//
// 'quote_pathname()' - Quote a pathname for a menu.
//

static void
quote_pathname(char       *dst,		// O - Destination string
               const char *src,		// I - Source string
	       int        dstsize)	// I - Size of destination string
{
  dstsize--; // prepare for trailing zero

  while (*src && dstsize > 1) {
    if (*src == '\\') {
      // Convert backslash to forward slash...
      *dst++ = '\\';
      *dst++ = '/';
      dstsize -= 2;
      src ++;
    } else {
      if (*src == '/') {
	*dst++ = '\\';
	dstsize--;
      }
      *dst++ = *src++;
      dstsize--;
    }
  }

  *dst = '\0';
}


//
// 'unquote_pathname()' - Unquote a pathname from a menu.
//

static void
unquote_pathname(char       *dst,	// O - Destination string
                 const char *src,	// I - Source string
	         int        dstsize)	// I - Size of destination string
{
  dstsize--; // prepare for trailing zero

  while (*src && dstsize > 0) {
    if (*src == '\\') src++;
    *dst++ = *src++;
    dstsize--;
  }

  *dst = '\0';
}

//
// 'get_homedir()' - Try to find the home directory (platform dependent).

static const char*
get_homedir() {

  const char *home = fl_getenv("HOME");

#ifdef WIN32

  if (!home) home = fl_getenv("UserProfile");

#endif // WIN32

  return home;
}


//
// End of "$Id: Fl_File_Chooser2.cxx 11813 2016-07-15 20:01:08Z AlbrechtS $".
//