DO NOT REPLY TO THIS MESSAGE.  INSTEAD, POST ANY RESPONSES TO THE LINK BELOW.

[STR New]

Link: http://www.fltk.org/str.php?L2904
Version: 1.3-feature


Attached file "Fl_Native_File_Chooser_GTK_test.cxx"...


Link: http://www.fltk.org/str.php?L2904
Version: 1.3-feature
// "$Id: $"
//
// FLTK native file chooser widget wrapper for GTK's GtkFileChooserDialog 
//
// Copyright 1998-2013 by Bill Spitzak and others.
// Copyright 2012 IMM
//
// 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 to:
//
//     http://www.fltk.org/str.php
//

#include <FL/Fl_Native_File_Chooser.H>
#include <FL/Fl_File_Icon.H>
#define FLTK_CHOOSER_SINGLE    Fl_File_Chooser::SINGLE
#define FLTK_CHOOSER_DIRECTORY Fl_File_Chooser::DIRECTORY
#define FLTK_CHOOSER_MULTI     Fl_File_Chooser::MULTI
#define FLTK_CHOOSER_CREATE    Fl_File_Chooser::CREATE

#include "Fl_Native_File_Chooser_common.cxx"
#include <sys/stat.h>
#include <string.h>

static int did_find_GTK_libs = 0;

#ifdef _TEST_GTK_FILE_CHOOSER
#  warning Test code for GTK file chooser
#  include <dlfcn.h>   // for dlopen et al
#  include <gtk/gtk.h> // for GTK funtions
#  include <locale.h>

static int have_looked_for_GTK_libs = 0;

/* These are the GTK/GLib methods we want to load, but not call by name...! */

// void g_free (gpointer mem);
typedef void (*XX_g_free)(gpointer);
static XX_g_free fl_g_free = NULL;

// gpointer g_slist_nth_data (GSList *list, guint n);
typedef gpointer (*XX_g_slist_nth_data) (GSList *, guint);
static XX_g_slist_nth_data fl_g_slist_nth_data = NULL;

// guint g_slist_length (GSList *list);
typedef guint (*XX_g_slist_length) (GSList *);
static XX_g_slist_length fl_g_slist_length = NULL;

// void g_slist_free (GSList *list);
typedef void (*XX_g_slist_free) (GSList *);
static XX_g_slist_free fl_g_slist_free = NULL;

// gboolean gtk_init_check (int *argc, char ***argv);
typedef gboolean (*XX_gtk_init_check)(int *, char ***);
static XX_gtk_init_check fl_gtk_init_check = NULL;

// void gtk_widget_destroy (GtkWidget *widget);
typedef void (*XX_gtk_widget_destroy) (GtkWidget *);
static XX_gtk_widget_destroy fl_gtk_widget_destroy = NULL;

// void gtk_file_chooser_set_select_multiple(GtkFileChooser *chooser, gboolean 
select_multiple);
typedef void (*XX_gtk_file_chooser_set_select_multiple)(GtkFileChooser *, 
gboolean);
static XX_gtk_file_chooser_set_select_multiple 
fl_gtk_file_chooser_set_select_multiple = NULL;

// void gtk_file_chooser_set_do_overwrite_confirmation(GtkFileChooser *chooser, 
gboolean do_overwrite_confirmation);
typedef void 
(*XX_gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *, 
gboolean);
static XX_gtk_file_chooser_set_do_overwrite_confirmation 
fl_gtk_file_chooser_set_do_overwrite_confirmation = NULL;

// void gtk_file_chooser_set_current_name (GtkFileChooser *chooser, const gchar 
*name);
typedef void (*XX_gtk_file_chooser_set_current_name)(GtkFileChooser *, const 
gchar *);
static XX_gtk_file_chooser_set_current_name 
fl_gtk_file_chooser_set_current_name = NULL;

// void gtk_file_chooser_set_current_folder (GtkFileChooser *chooser, const 
gchar *name);
typedef void (*XX_gtk_file_chooser_set_current_folder)(GtkFileChooser *, const 
gchar *);
static XX_gtk_file_chooser_set_current_folder 
fl_gtk_file_chooser_set_current_folder = NULL;

// void gtk_file_chooser_set_create_folders (GtkFileChooser *chooser, gboolean 
create_folders);
typedef void (*XX_gtk_file_chooser_set_create_folders) (GtkFileChooser *, 
gboolean);
static XX_gtk_file_chooser_set_create_folders 
fl_gtk_file_chooser_set_create_folders = NULL;

// gboolean gtk_file_chooser_get_select_multiple(GtkFileChooser *chooser);
typedef gboolean (*XX_gtk_file_chooser_get_select_multiple)(GtkFileChooser *);
static XX_gtk_file_chooser_get_select_multiple 
fl_gtk_file_chooser_get_select_multiple = NULL;

// void gtk_widget_hide(GtkWidget *widget);
typedef void (*XX_gtk_widget_hide)(GtkWidget *);
static XX_gtk_widget_hide fl_gtk_widget_hide = NULL;

// gint gtk_dialog_run(GtkDialog *dialog);
typedef gint (*XX_gtk_dialog_run)(GtkDialog *dialog);
static XX_gtk_dialog_run fl_gtk_dialog_run = NULL;

// gchar * gtk_file_chooser_get_filename(GtkFileChooser *chooser);
typedef gchar* (*XX_gtk_file_chooser_get_filename)(GtkFileChooser *);
static XX_gtk_file_chooser_get_filename fl_gtk_file_chooser_get_filename = NULL;

// GSList * gtk_file_chooser_get_filenames(GtkFileChooser *chooser);
typedef GSList* (*XX_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
static XX_gtk_file_chooser_get_filenames fl_gtk_file_chooser_get_filenames = 
NULL;

// gboolean gtk_main_iteration(void);
typedef gboolean (*XX_gtk_main_iteration)(void);
static XX_gtk_main_iteration fl_gtk_main_iteration = NULL;

// gboolean gtk_events_pending(void);
typedef gboolean (*XX_gtk_events_pending)(void);
static XX_gtk_events_pending fl_gtk_events_pending = NULL;

// GtkWidget * gtk_file_chooser_dialog_new(const gchar *title, GtkWindow 
*parent, GtkFileChooserAction action, const gchar *first_button_text, ...);
typedef GtkWidget* (*XX_gtk_file_chooser_dialog_new)(const gchar *, GtkWindow 
*, GtkFileChooserAction, const gchar *, ...);
static XX_gtk_file_chooser_dialog_new fl_gtk_file_chooser_dialog_new = NULL;

// void gtk_file_chooser_add_filter(GtkFileChooser*, GtkFileFilter*);
typedef void (*XX_gtk_file_chooser_add_filter)(GtkFileChooser*, GtkFileFilter*);
static XX_gtk_file_chooser_add_filter fl_gtk_file_chooser_add_filter = NULL;

// GtkFileFilter* gtk_file_chooser_get_filter(GtkFileChooser*);
typedef GtkFileFilter* (*XX_gtk_file_chooser_get_filter)(GtkFileChooser*);
static XX_gtk_file_chooser_get_filter fl_gtk_file_chooser_get_filter = NULL;

// void gtk_file_chooser_set_filter(GtkFileChooser*, GtkFileFilter*);
typedef void (*XX_gtk_file_chooser_set_filter)(GtkFileChooser*, GtkFileFilter*);
static XX_gtk_file_chooser_set_filter fl_gtk_file_chooser_set_filter = NULL;

// GtkFileFilter * gtk_file_filter_new();
typedef GtkFileFilter* (*XX_gtk_file_filter_new)(void);
static XX_gtk_file_filter_new fl_gtk_file_filter_new = NULL;

// void gtk_file_filter_add_pattern(GtkFileFilter*, const gchar*);
typedef void (*XX_gtk_file_filter_add_pattern)(GtkFileFilter*, const gchar*);
static XX_gtk_file_filter_add_pattern fl_gtk_file_filter_add_pattern = NULL;

// void gtk_file_filter_add_custom(GtkFileFilter *filter, GtkFileFilterFlags 
needed,
//  GtkFileFilterFunc func, gpointer data, GDestroyNotify notify);
typedef void (*XX_gtk_file_filter_add_custom)(GtkFileFilter *filter, 
GtkFileFilterFlags needed,
                                              GtkFileFilterFunc func, gpointer 
data, 
                                              GDestroyNotify notify);
static XX_gtk_file_filter_add_custom fl_gtk_file_filter_add_custom = NULL;

// void gtk_file_filter_set_name(GtkFileFilter*, const gchar*);
typedef void (*XX_gtk_file_filter_set_name)(GtkFileFilter*, const gchar*);
static XX_gtk_file_filter_set_name fl_gtk_file_filter_set_name = NULL;

// const gchar* gtk_file_filter_get_name(GtkFileFilter*);
typedef const gchar* (*XX_gtk_file_filter_get_name)(GtkFileFilter*);
static XX_gtk_file_filter_get_name fl_gtk_file_filter_get_name = NULL;

// void gtk_file_chooser_set_extra_widget(GtkFileChooser *, GtkWidget *);
typedef void (*XX_gtk_file_chooser_set_extra_widget)(GtkFileChooser *, 
GtkWidget *);
static XX_gtk_file_chooser_set_extra_widget 
fl_gtk_file_chooser_set_extra_widget = NULL;

// void gtk_widget_show(GtkWidget *);
typedef void (*XX_gtk_widget_show)(GtkWidget *);
static XX_gtk_widget_show fl_gtk_widget_show = NULL;

// GtkWidget *gtk_check_button_new_with_label(const gchar *);
typedef GtkWidget* (*XX_gtk_check_button_new_with_label)(const gchar *);
static XX_gtk_check_button_new_with_label fl_gtk_check_button_new_with_label = 
NULL;

// gulong g_signal_connect_data(gpointer, const gchar *, GCallback, gpointer, 
GClosureNotify, GConnectFlags);
typedef gulong (*XX_g_signal_connect_data)(gpointer, const gchar *, GCallback, 
gpointer, GClosureNotify, GConnectFlags);
static XX_g_signal_connect_data fl_g_signal_connect_data = NULL;

// gboolean gtk_toggle_button_get_active(GtkToggleButton *);
typedef gboolean (*XX_gtk_toggle_button_get_active)(GtkToggleButton*);
static XX_gtk_toggle_button_get_active fl_gtk_toggle_button_get_active = NULL;

// void gtk_file_chooser_set_show_hidden(GtkFileChooser *, gboolean);
typedef void (*XX_gtk_file_chooser_set_show_hidden)(GtkFileChooser *, gboolean);
static XX_gtk_file_chooser_set_show_hidden fl_gtk_file_chooser_set_show_hidden 
= NULL;

// gboolean gtk_file_chooser_get_show_hidden(GtkFileChooser *);
typedef gboolean (*XX_gtk_file_chooser_get_show_hidden)(GtkFileChooser *);
static XX_gtk_file_chooser_get_show_hidden fl_gtk_file_chooser_get_show_hidden 
= NULL;

// void gtk_toggle_button_set_active(GtkToggleButton *, gboolean);
typedef void (*XX_gtk_toggle_button_set_active)(GtkToggleButton *, gboolean);
static XX_gtk_toggle_button_set_active fl_gtk_toggle_button_set_active = NULL;


// macro to help with the symbol loading boilerplate...
#define GET_SYM(SSS, LLL) \
  dlerror();    /* Clear any existing error */  \
  fl_##SSS = (XX_##SSS)dlsym(LLL, #SSS);        \
  if ((pc_dl_error = dlerror()) != NULL)  {     \
    fprintf(stderr, "%s\n", pc_dl_error);       \
    did_find_GTK_libs = 0;                      \
    return; }

class Fl_GTK_File_Chooser {
  friend class Fl_Native_File_Chooser;
private:
  GtkWidget *gtkw_ptr; // used to hold a GtkWidget* without pulling GTK into 
everything...
  void *gtkw_slist; // used to hold a GLib GSList...
  unsigned gtkw_count; // number of files read back - if any
  mutable char *gtkw_filename; // last name we read back
  char *gtkw_title; // the title to be applied to the dialog
public:
  Fl_GTK_File_Chooser() {
    gtkw_ptr   = NULL;    // used to hold a GtkWidget* without pulling GTK into 
everything...
    gtkw_slist = NULL;    // will hold the returned file names in a 
multi-selection...
    gtkw_count = 0;       // How many items were selected?
    gtkw_filename = NULL; // holds the last name we read back in a single file 
selection...
    gtkw_title = NULL;    // dialog title
  }
};

static void* fl_dlopen(const char *filename1, const char *filename2)
{
  void *ptr = dlopen(filename1, RTLD_LAZY | RTLD_GLOBAL);
  if (!ptr) ptr = dlopen(filename2, RTLD_LAZY | RTLD_GLOBAL);
  return ptr;
}

/* 
 * Use dlopen to see if we can load the gtk dynamic libraries that
 * will allow us to create a GtkFileChooserDialog() on the fly,
 * without linking to the GTK libs at compile time.
 */
static void probe_for_GTK_libs(void) {
  void *ptr_glib    = NULL;
  void *ptr_gtk     = NULL;

#   ifdef __APPLE_CC__ // allows testing on Darwin + X11
  ptr_glib    = dlopen("/sw/lib/libglib-2.0.dylib", RTLD_LAZY | RTLD_GLOBAL);
#   else
  ptr_glib    = fl_dlopen("libglib-2.0.so", "libglib-2.0.so.0");
#   endif
  // Try first with GTK3
  ptr_gtk     = fl_dlopen("libgtk-3.so", "libgtk-3.so.0");
  if (ptr_gtk && ptr_glib) {
#ifdef DEBUG
    puts("selected GTK-3\n");
#endif
  }
  else {// Try then with GTK2
#   ifdef __APPLE_CC__ // allows testing on Darwin + X11
    ptr_gtk     = dlopen("/sw/lib/libgtk-x11-2.0.dylib", RTLD_LAZY | 
RTLD_GLOBAL);
#else
    ptr_gtk     = fl_dlopen("libgtk-x11-2.0.so", "libgtk-x11-2.0.so.0");
#endif
#ifdef DEBUG
    if (ptr_gtk && ptr_glib) {
      puts("selected GTK-2\n");
    }
#endif
  }
  
  if((!ptr_glib) || (!ptr_gtk)) {
#ifdef DEBUG
    puts("Failure to load");
#endif
    did_find_GTK_libs = 0;
    return;
  }

  char *pc_dl_error; // used to report errors by the GET_SYM macro...
  // items we need from GLib
  GET_SYM(g_free, ptr_glib);
  GET_SYM(g_slist_nth_data, ptr_glib);
  GET_SYM(g_slist_length, ptr_glib);
  GET_SYM(g_slist_free, ptr_glib);
  // items we need from GTK
  GET_SYM(gtk_init_check, ptr_gtk);
  GET_SYM(gtk_widget_destroy, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_select_multiple, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_do_overwrite_confirmation, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_current_name, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_current_folder, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_create_folders, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_select_multiple, ptr_gtk);
  GET_SYM(gtk_widget_hide, ptr_gtk);
  GET_SYM(gtk_dialog_run, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filename, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filenames, ptr_gtk);
  GET_SYM(gtk_main_iteration, ptr_gtk);
  GET_SYM(gtk_events_pending, ptr_gtk);
  GET_SYM(gtk_file_chooser_dialog_new, ptr_gtk);
  GET_SYM(gtk_file_chooser_add_filter, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filter, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_filter, ptr_gtk);
  GET_SYM(gtk_file_filter_new, ptr_gtk);
  GET_SYM(gtk_file_filter_add_pattern, ptr_gtk);
  GET_SYM(gtk_file_filter_add_custom, ptr_gtk);
  GET_SYM(gtk_file_filter_set_name, ptr_gtk);
  GET_SYM(gtk_file_filter_get_name, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_extra_widget, ptr_gtk);
  GET_SYM(gtk_widget_show, ptr_gtk);
  GET_SYM(gtk_check_button_new_with_label, ptr_gtk);
  GET_SYM(g_signal_connect_data, ptr_gtk);
  GET_SYM(gtk_toggle_button_get_active, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_show_hidden, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_show_hidden, ptr_gtk);
  GET_SYM(gtk_toggle_button_set_active, ptr_gtk);

  did_find_GTK_libs = _TEST_GTK_FILE_CHOOSER;
} // probe_for_GTK_libs

#endif /* _TEST_GTK_FILE_CHOOSER */

/**
  The constructor. Internally allocates the native widgets.
  Optional \p val presets the type of browser this will be, 
  which can also be changed with type().
*/
Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
  _btype       = val;
  _options     = NO_OPTIONS;
  _filter      = NULL;
  _filtvalue   = 0;
  _parsedfilt  = NULL;
  _preset_file = NULL;
  _prevvalue   = NULL;
  _directory   = NULL;
  _errmsg      = NULL;
  _file_chooser = NULL;

  // My plan - such as it is - is to allocate _file_chooser ONLY if I can not 
  // make a GtkFileChooserDialog, which I'll deal with... somehow... 
  // Anyway, the idea is to have some global static state in this file, such 
that
  // the first time in, we probe for the GTK libs we need, and if found we use 
them.
  // Otherwise, we just create a stock FLTK file chooser, and from thereon this 
file
  // would behave exactly like Fl_Native_File_Chooser_FLTK.

#ifdef _TEST_GTK_FILE_CHOOSER

  if(have_looked_for_GTK_libs == 0) {
    // First Time here, try to find the GTK libs if they are installed
    probe_for_GTK_libs();
    have_looked_for_GTK_libs = -1;
  }

  // if we found all the GTK functions we need, we will use the 
GtkFileChooserDialog
  if (did_find_GTK_libs) {
    _file_chooser = (Fl_File_Chooser*)new Fl_GTK_File_Chooser();
  }
#endif // _TEST_GTK_FILE_CHOOSER
  if (!_file_chooser) _file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL);

  type(val);                    // do this after _file_chooser created
  _nfilters    = 0;
} 

/**
  Destructor. 
  Deallocates any resources allocated to this widget.
*/
Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
  if (!did_find_GTK_libs) {
    delete _file_chooser;
    _file_chooser = NULL;
  }
  _filter      = strfree(_filter);
  _parsedfilt  = strfree(_parsedfilt);
  _preset_file = strfree(_preset_file);
  _prevvalue   = strfree(_prevvalue);
  _directory   = strfree(_directory);
  _errmsg      = strfree(_errmsg);
#ifdef _TEST_GTK_FILE_CHOOSER
  if (did_find_GTK_libs) {
    // Should free up resources taken for...
    Fl_GTK_File_Chooser *gtk = (Fl_GTK_File_Chooser*)_file_chooser;
    if(gtk->gtkw_ptr) { 
      fl_gtk_widget_destroy (gtk->gtkw_ptr);
      gtk->gtkw_ptr = NULL;
    }
    if(gtk->gtkw_filename) {
      fl_g_free(gtk->gtkw_filename);
      gtk->gtkw_filename = NULL;
    }
    if(gtk->gtkw_slist) {
      GSList *iter = (GSList *)gtk->gtkw_slist;
      while(iter) {
        if(iter->data) fl_g_free(iter->data);
        iter = g_slist_next(iter);
      }
      fl_g_slist_free((GSList *)gtk->gtkw_slist);
      gtk->gtkw_slist = NULL;
    }
    gtk->gtkw_count = 0; // assume we have no files selected now
    gtk->gtkw_title = strfree(gtk->gtkw_title);
  }
#endif // _TEST_GTK_FILE_CHOOSER
}

// PRIVATE: SET ERROR MESSAGE
void Fl_Native_File_Chooser::errmsg(const char *msg) {
  _errmsg = strfree(_errmsg);
  _errmsg = strnew(msg);
}

// PRIVATE: translate Native types to Fl_File_Chooser types
int Fl_Native_File_Chooser::type_fl_file(int val) {
  switch (val) {
    case BROWSE_FILE:
      return(FLTK_CHOOSER_SINGLE);
    case BROWSE_DIRECTORY:
      return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_DIRECTORY);
    case BROWSE_MULTI_FILE:
      return(FLTK_CHOOSER_MULTI);
    case BROWSE_MULTI_DIRECTORY:
      return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI);
    case BROWSE_SAVE_FILE:
      return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_CREATE);
    case BROWSE_SAVE_DIRECTORY:
      return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI | FLTK_CHOOSER_CREATE);
    default:
      return(FLTK_CHOOSER_SINGLE);
  }
}

/**
  Sets the current Fl_Native_File_Chooser::Type of browser.
 */
void Fl_Native_File_Chooser::type(int val) {
  _btype = val;
  if (!did_find_GTK_libs) _file_chooser->type(type_fl_file(val));
}

/**
  Gets the current Fl_Native_File_Chooser::Type of browser.
 */
int Fl_Native_File_Chooser::type() const {
  return(_btype);
}

/**
  Sets the platform specific chooser options to \p val.
  \p val is expected to be one or more Fl_Native_File_Chooser::Option flags 
ORed together.
  Some platforms have OS-specific functions that can be enabled/disabled via 
this method.
  <P>
  \code
  Flag              Description                                       Win       
Mac       Other
  --------------    -----------------------------------------------   -------   
-------   -------
  NEW_FOLDER        Shows the 'New Folder' button.                    Ignored   
Used      Used
  PREVIEW           Enables the 'Preview' mode by default.            Ignored   
Ignored   Used
  SAVEAS_CONFIRM    Confirm dialog if BROWSE_SAVE_FILE file exists.   Used      
Used      Used
  \endcode
*/
void Fl_Native_File_Chooser::options(int val) {
  _options = val;
}

/**
  Gets the platform specific Fl_Native_File_Chooser::Option flags.
*/
int Fl_Native_File_Chooser::options() const {
  return(_options);
}

#ifdef _TEST_GTK_FILE_CHOOSER
static gboolean fl_custom_gtk_filter_function(const GtkFileFilterInfo *info, 
gpointer data)
{
  return (gboolean)fl_filename_match(info->filename, (const char*)data);
}

static void hidden_files_cb(GtkToggleButton *togglebutton, gpointer user_data)
{
  gboolean state = fl_gtk_toggle_button_get_active(togglebutton);
  fl_gtk_file_chooser_set_show_hidden((GtkFileChooser*)user_data, state);
}

// Implements a wrapper for the GtkFileChooserDialog 
int Fl_Native_File_Chooser::fl_gtk_chooser_wrapper()
{
  int result = 1;
  static int have_gtk_init = 0;
  if(!have_gtk_init) {
    have_gtk_init = -1;
    int ac = 0;
    fl_gtk_init_check(&ac, NULL);
  }

  Fl_GTK_File_Chooser *gtk = (Fl_GTK_File_Chooser*)_file_chooser;
  if(gtk->gtkw_ptr) { // discard the previous dialog widget
    fl_gtk_widget_destroy (gtk->gtkw_ptr);
    gtk->gtkw_ptr = NULL;
  }

  // set the dialog action type
  GtkFileChooserAction gtw_action_type;
  switch (_btype) {
    case BROWSE_DIRECTORY:
    case BROWSE_MULTI_DIRECTORY:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
      break;

    case BROWSE_SAVE_FILE:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_SAVE;
      break;

    case BROWSE_SAVE_DIRECTORY:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
      break;

    case BROWSE_MULTI_FILE:
    case BROWSE_FILE:
    default:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_OPEN;
      break;
  }
  // create a new dialog
  gtk->gtkw_ptr = fl_gtk_file_chooser_dialog_new (gtk->gtkw_title,
                                             NULL, /* parent_window */
                                             gtw_action_type,
                                             GTK_STOCK_CANCEL, 
GTK_RESPONSE_CANCEL,
                          gtw_action_type == GTK_FILE_CHOOSER_ACTION_SAVE ? 
GTK_STOCK_SAVE : GTK_STOCK_OPEN, 
                                             GTK_RESPONSE_ACCEPT,
                                             NULL);
  // did we create a valid dialog widget?
  if(!gtk->gtkw_ptr) {
    // fail
    return -1;
  }

  // set the dialog properties
  switch (_btype) {
    case BROWSE_MULTI_DIRECTORY:
    case BROWSE_MULTI_FILE:
      fl_gtk_file_chooser_set_select_multiple((GtkFileChooser *)gtk->gtkw_ptr, 
TRUE);
      break;

    case BROWSE_SAVE_FILE:
      if((_options & SAVEAS_CONFIRM) != 0) 
fl_gtk_file_chooser_set_do_overwrite_confirmation ((GtkFileChooser 
*)gtk->gtkw_ptr, TRUE);
      fl_gtk_file_chooser_set_current_name ((GtkFileChooser *)gtk->gtkw_ptr, 
_preset_file);
      if((_options & NEW_FOLDER) != 0) 
fl_gtk_file_chooser_set_create_folders((GtkFileChooser *)gtk->gtkw_ptr, TRUE);
      break;

    case BROWSE_SAVE_DIRECTORY:
      if((_options & NEW_FOLDER) != 0) 
fl_gtk_file_chooser_set_create_folders((GtkFileChooser *)gtk->gtkw_ptr, TRUE);
      //if((_options & SAVEAS_CONFIRM) != 0) 
      fl_gtk_file_chooser_set_do_overwrite_confirmation ((GtkFileChooser 
*)gtk->gtkw_ptr, TRUE);
      break;

    case BROWSE_DIRECTORY:
    case BROWSE_FILE:
    default:
      break;
  }
  
  if (_directory && _directory[0]) 
fl_gtk_file_chooser_set_current_folder((GtkFileChooser *)gtk->gtkw_ptr, 
_directory);
  
  GtkFileFilter **filter_tab = NULL;
  if (_parsedfilt) {
    filter_tab = new GtkFileFilter*[_nfilters];
    char *filter = strdup(_parsedfilt);
    char *p = strtok(filter, "\t");
    int count = 0;
    while (p) {
      filter_tab[count] = fl_gtk_file_filter_new();
      fl_gtk_file_filter_set_name(filter_tab[count], p);
      p = strchr(p, '(') + 1;
      char *q = strchr(p, ')'); *q = 0;      
      fl_gtk_file_filter_add_custom(filter_tab[count], 
GTK_FILE_FILTER_FILENAME, 
                                    fl_custom_gtk_filter_function, strdup(p), 
free);
      fl_gtk_file_chooser_add_filter((GtkFileChooser *)gtk->gtkw_ptr, 
filter_tab[count]);
      p = strtok(NULL, "\t");
      count++;
      }
    free(filter);
    fl_gtk_file_chooser_set_filter((GtkFileChooser *)gtk->gtkw_ptr, 
filter_tab[_filtvalue]);
    if (gtw_action_type == GTK_FILE_CHOOSER_ACTION_OPEN) {
      GtkFileFilter* gfilter = fl_gtk_file_filter_new();
      fl_gtk_file_filter_set_name(gfilter, Fl_File_Chooser::all_files_label);
      fl_gtk_file_filter_add_pattern(gfilter, "*");
      fl_gtk_file_chooser_add_filter((GtkFileChooser *)gtk->gtkw_ptr, gfilter);
      }
    }

  GtkWidget *toggle = 
fl_gtk_check_button_new_with_label(Fl_File_Chooser::hidden_label);
  fl_gtk_widget_show(toggle);
  fl_gtk_file_chooser_set_extra_widget((GtkFileChooser *)gtk->gtkw_ptr, toggle);
  fl_g_signal_connect_data(toggle, "toggled", G_CALLBACK(hidden_files_cb), 
gtk->gtkw_ptr, NULL, (GConnectFlags) 0);
  fl_gtk_widget_show(gtk->gtkw_ptr);
  gboolean state = fl_gtk_file_chooser_get_show_hidden((GtkFileChooser 
*)gtk->gtkw_ptr);
  fl_gtk_toggle_button_set_active((GtkToggleButton *)toggle, state);
  
  if (fl_gtk_dialog_run ((GtkDialog *)gtk->gtkw_ptr) == GTK_RESPONSE_ACCEPT) {
    if (_parsedfilt) {
      GtkFileFilter *gfilter = fl_gtk_file_chooser_get_filter((GtkFileChooser 
*)gtk->gtkw_ptr);
      for (_filtvalue = 0; _filtvalue < _nfilters; _filtvalue++) {
        if (filter_tab[_filtvalue] == gfilter) break;
        }
    }
    
    // discard any filenames or lists from previous calls
    if(gtk->gtkw_filename) {
      fl_g_free(gtk->gtkw_filename);
      gtk->gtkw_filename = NULL;
    }
    if(gtk->gtkw_slist) {
      GSList *iter = (GSList *)gtk->gtkw_slist;
      while(iter) {
        if(iter->data) fl_g_free(iter->data);
        iter = g_slist_next(iter);
      }
      fl_g_slist_free((GSList *)gtk->gtkw_slist);
      gtk->gtkw_slist = NULL;
    }
    gtk->gtkw_count = 0; // assume we have no files selected now

    if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtk->gtkw_ptr) 
== FALSE) {
      gtk->gtkw_filename = fl_gtk_file_chooser_get_filename ((GtkFileChooser 
*)gtk->gtkw_ptr);
      if (gtk->gtkw_filename) {
        gtk->gtkw_count = 1;
        result = 0;
        //printf("single: %s\n", gtkw_filename);
      }
    }
    else {
      gtk->gtkw_slist = fl_gtk_file_chooser_get_filenames((GtkFileChooser 
*)gtk->gtkw_ptr);
      gtk->gtkw_count = fl_g_slist_length((GSList *)gtk->gtkw_slist);
      if(gtk->gtkw_count) result = 0;

//      puts("multiple");
//      GSList *iter = (GSList *)gtk->gtkw_slist;
//      printf ("Selected %d files\n", gtk->gtkw_count);
//      while(iter) {
//        char *nm = (char *)iter->data;
//        printf("%s\n", nm);
//        iter = g_slist_next(iter);
//      }
    }
  }
  delete[] filter_tab;
  fl_gtk_widget_hide (gtk->gtkw_ptr);

  // I think this is analogus to doing an Fl::check() - we need this here to 
make sure
  // the GtkFileChooserDialog is removed from the display correctly
  while (fl_gtk_events_pending ()) fl_gtk_main_iteration (); 

  return result;
} // fl_gtk_chooser_wrapper
#endif // _TEST_GTK_FILE_CHOOSER

/**
  Post the chooser's dialog. Blocks until dialog has been completed or 
cancelled.
  \returns
     - 0  -- user picked a file
     - 1  -- user cancelled
     - -1 -- failed; errmsg() has reason
*/
int Fl_Native_File_Chooser::show() {

  if(!did_find_GTK_libs) { // use FLTK built-in file chooser
  // FILTER
    if ( _parsedfilt ) {
      _file_chooser->filter(_parsedfilt);
    }

    // FILTER VALUE
    //     Set this /after/ setting the filter
    //
    _file_chooser->filter_value(_filtvalue);

    // DIRECTORY
    if ( _directory && _directory[0] ) {
      _file_chooser->directory(_directory);
    } else {
      _file_chooser->directory(_prevvalue);
    }

    // PRESET FILE
    if ( _preset_file ) {
      _file_chooser->value(_preset_file);
    }

    // OPTIONS: PREVIEW
    _file_chooser->preview( (options() & PREVIEW) ? 1 : 0);

    // OPTIONS: NEW FOLDER
    if ( options() & NEW_FOLDER )
      _file_chooser->type(_file_chooser->type() | FLTK_CHOOSER_CREATE); // on

    // SHOW
    _file_chooser->show();

    // BLOCK WHILE BROWSER SHOWN
    while ( _file_chooser->shown() ) {
      Fl::wait();
    }

    if ( _file_chooser->value() && _file_chooser->value()[0] ) {
      _prevvalue = strfree(_prevvalue);
      _prevvalue = strnew(_file_chooser->value());
      _filtvalue = _file_chooser->filter_value(); // update filter value

      // HANDLE SHOWING 'SaveAs' CONFIRM
      if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) {
        struct stat buf;
        if ( stat(_file_chooser->value(), &buf) != -1 ) {
          if ( buf.st_mode & S_IFREG ) {    // Regular file + exists?
            if ( exist_dialog() == 0 ) {
              return(1);
            }
          }
        }
      }
    }

    if ( _file_chooser->count() ) return(0);
    else return(1);
  }
#ifdef _TEST_GTK_FILE_CHOOSER
  else { // use GtkFileChooserDialog
    char *p, **before = new char*[20];
    int val;
    for (val = 0; val < 20; val++) { // memorize the state of all components of 
the current locale
      if (val == LC_ALL) continue;
      p = setlocale(val, NULL);
      if (!p) break;
      before[val] = strdup(p);
    }
    int retval = fl_gtk_chooser_wrapper(); // may change the locale
    for (int i = 0; i < val; i++) { // set all components of the locale to 
their previous state
      if (i == LC_ALL) continue;
      if (before[i]) {
        setlocale(i, before[i]);
        free(before[i]);
      }
    }
    delete[] before;
    return retval;
  }
#else
  return -1; //error
#endif // _TEST_GTK_FILE_CHOOSER
}

/**
  Returns a system dependent error message for the last method that failed. 
  This message should at least be flagged to the user in a dialog box, or to 
some kind of error log. 
  Contents will be valid only for methods that document errmsg() will have info 
on failures.
 */
const char *Fl_Native_File_Chooser::errmsg() const {
  return(_errmsg ? _errmsg : "No error");
}

/**
  Return the filename the user chose.
  Use this if only expecting a single filename.
  If more than one filename is expected, use filename(int) instead.
  Return value may be "" if no filename was chosen (eg. user cancelled).
 */
const char* Fl_Native_File_Chooser::filename() const {
  if ((!did_find_GTK_libs) && ( _file_chooser->count() > 0 )) {
    return(_file_chooser->value());
  }

#ifdef _TEST_GTK_FILE_CHOOSER
  // else...
  if (did_find_GTK_libs) {
    Fl_GTK_File_Chooser *gtk = (Fl_GTK_File_Chooser*)_file_chooser;
    if(gtk->gtkw_ptr) {
      if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser 
*)gtk->gtkw_ptr) == FALSE) {
        return gtk->gtkw_filename;
      }
      else {
        GSList *iter = (GSList *)gtk->gtkw_slist;
        char *nm = (char *)iter->data;
        return nm;
      }
    }
  }
#endif // _TEST_GTK_FILE_CHOOSER

  // else...
  return("");
}

/**
  Return one of the filenames the user selected.
  Use count() to determine how many filenames the user selected.
  <P>
  \b Example:
  \code
  if ( fnfc->show() == 0 ) {
    // Print all filenames user selected
    for (int n=0; n<fnfc->count(); n++ ) {
      printf("%d) '%s'\n", n, fnfc->filename(n));
    }
  }
  \endcode
 */
const char* Fl_Native_File_Chooser::filename(int i) const {
  if(!did_find_GTK_libs) {
    if ( i < _file_chooser->count() )
        return(_file_chooser->value(i+1));  // convert fltk 1 based to our 0 
based
  }
#ifdef _TEST_GTK_FILE_CHOOSER
  else {
    Fl_GTK_File_Chooser *gtk = (Fl_GTK_File_Chooser*)_file_chooser;
    if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtk->gtkw_ptr) 
== FALSE) {
      return gtk->gtkw_filename;
    }
    else {
      if ((unsigned)i < gtk->gtkw_count) {
        GSList *iter = (GSList *)gtk->gtkw_slist;
        char *nm = (char *)fl_g_slist_nth_data(iter, i);
        return nm;
      }
    }
  }
#endif // _TEST_GTK_FILE_CHOOSER

  // else...
  return("");
}

/**
  Set the title of the file chooser's dialog window.
  Can be NULL if no title desired.
  The default title varies according to the platform, so you are advised to set 
the title explicitly.
*/
void Fl_Native_File_Chooser::title(const char *val) {
  if(!did_find_GTK_libs) {
    _file_chooser->label(val);
  } 
#ifdef _TEST_GTK_FILE_CHOOSER
  else { // gtk version...
    Fl_GTK_File_Chooser *gtk = (Fl_GTK_File_Chooser*)_file_chooser;
    strfree(gtk->gtkw_title);
    gtk->gtkw_title = strnew(val);
  }
#endif // _TEST_GTK_FILE_CHOOSER
}

/**
  Get the title of the file chooser's dialog window.
  Return value may be NULL if no title was set.
*/
const char *Fl_Native_File_Chooser::title() const {
  if(!did_find_GTK_libs) {
    return(_file_chooser->label());
  }
#ifdef _TEST_GTK_FILE_CHOOSER
  else {
    return ((Fl_GTK_File_Chooser*)_file_chooser)->gtkw_title;
  }
#else
  return "";
#endif // _TEST_GTK_FILE_CHOOSER
}

/**
  Sets the filename filters used for browsing. 
  The default is NULL, which browses all files.
  <P>
  The filter string can be any of:
  <P>
    - A single wildcard (eg. "*.txt")
    - Multiple wildcards (eg. "*.{cxx,h,H}")
    - A descriptive name followed by a "\t" and a wildcard (eg. "Text 
Files\t*.txt")
    - A list of separate wildcards with a "\n" between each (eg. 
"*.{cxx,H}\n*.txt")
    - A list of descriptive names and wildcards (eg. "C++ Files\t*.{cxx,H}\nTxt 
Files\t*.txt")
  <P>
  The format of each filter is a wildcard, or an optional user description 
  followed by '\\t' and the wildcard.
  <P>
  On most platforms, each filter is available to the user via a pulldown menu 
  in the file chooser. The 'All Files' option is always available to the user. 
*/
void Fl_Native_File_Chooser::filter(const char *val) {
  _filter = strfree(_filter);
  _filter = strnew(val);
  parse_filter();
}

/**
  Returns the filter string last set.
  Can be NULL if no filter was set.
 */
const char *Fl_Native_File_Chooser::filter() const {
  return(_filter);
}

/**
Gets how many filters were available, not including "All Files" 
*/
int Fl_Native_File_Chooser::filters() const {
  return(_nfilters);
}

/**
  Sets which filter will be initially selected.

  The first filter is indexed as 0. 
  If filter_value()==filters(), then "All Files" was chosen. 
  If filter_value() > filters(), then a custom filter was set.
 */
void Fl_Native_File_Chooser::filter_value(int val) {
  _filtvalue = val;
}

/**
  Returns which filter value was last selected by the user.
  This is only valid if the chooser returns success.
 */
int Fl_Native_File_Chooser::filter_value() const {
  return(_filtvalue);
}

/**
  Returns the number of filenames (or directory names) the user selected.
  <P>
  \b Example:
  \code
  if ( fnfc->show() == 0 ) {
    // Print all filenames user selected
    for (int n=0; n<fnfc->count(); n++ ) {
      printf("%d) '%s'\n", n, fnfc->filename(n));
    }
  }
  \endcode
*/
int Fl_Native_File_Chooser::count() const {
  if(!did_find_GTK_libs) return(_file_chooser->count());

#ifdef _TEST_GTK_FILE_CHOOSER
  if (((Fl_GTK_File_Chooser*)_file_chooser)->gtkw_ptr) { return 
((Fl_GTK_File_Chooser*)_file_chooser)->gtkw_count; }
#endif // _TEST_GTK_FILE_CHOOSER
  
  else return 0;
}

/**
  Preset the directory the browser will show when opened.
  If \p val is NULL, or no directory is specified, the chooser will attempt
  to use the last non-cancelled folder.
*/
void Fl_Native_File_Chooser::directory(const char *val) {
  _directory = strfree(_directory);
  _directory = strnew(val);
}

/**
  Returns the current preset directory() value.
*/
const char *Fl_Native_File_Chooser::directory() const {
  return(_directory);
}

// PRIVATE: Convert our filter format to fltk's chooser format
//     FROM                                     TO (FLTK)
//     -------------------------                --------------------------
//     "*.cxx"                                  "*.cxx Files(*.cxx)"
//     "C Files\t*.{cxx,h}"                     "C Files(*.{cxx,h})"
//     "C Files\t*.{cxx,h}\nText Files\t*.txt"  "C Files(*.{cxx,h})\tText 
Files(*.txt)"
//
//     Returns a modified version of the filter that the caller is responsible
//     for freeing with strfree().
//
void Fl_Native_File_Chooser::parse_filter() {
  _parsedfilt = strfree(_parsedfilt);   // clear previous parsed filter (if any)
  _nfilters = 0;
  char *in = _filter;
  if ( !in ) return;

  int has_name = strchr(in, '\t') ? 1 : 0;

  char mode = has_name ? 'n' : 'w';     // parse mode: n=title, w=wildcard
  char wildcard[1024] = "";             // parsed wildcard
  char name[1024] = "";

  // Parse filter user specified
  for ( ; 1; in++ ) {
    /*** DEBUG
    printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
                        *in, mode,     name,     wildcard);
    ***/

    switch (*in) {
      // FINISHED PARSING NAME?
      case '\t':
        if ( mode != 'n' ) goto regchar;
        mode = 'w';
        break; 
      // ESCAPE NEXT CHAR
      case '\\':
        ++in;
        goto regchar; 
      // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
      case '\r':
      case '\n':
      case '\0':
        // APPEND NEW FILTER TO LIST
        if ( wildcard[0] ) {
          // OUT: "name(wild)\tname(wild)"
          char comp[2048];
          sprintf(comp, "%s%.511s(%.511s)", ((_parsedfilt)?"\t":""),
                                            name, wildcard);
          _parsedfilt = strapp(_parsedfilt, comp);
          _nfilters++;
          //DEBUG printf("DEBUG: PARSED FILT NOW <%s>\n", _parsedfilt);
        }
        // RESET
        wildcard[0] = name[0] = '\0';
        mode = strchr(in, '\t') ? 'n' : 'w';
        // DONE?
        if ( *in == '\0' ) return;      // done
        else continue;                  // not done yet, more filters

      // Parse all other chars
      default:                          // handle all non-special chars
      regchar:                          // handle regular char
        switch ( mode ) {
          case 'n': chrcat(name, *in);     continue;
          case 'w': chrcat(wildcard, *in); continue;
        }
        break;
    }
  }
  //NOTREACHED
}

/**
  Sets the default filename for the chooser.
  Use directory() to set the default directory.
  Mainly used to preset the filename for save dialogs, 
  and on most platforms can be used for opening files as well. 
 */
void Fl_Native_File_Chooser::preset_file(const char* val) {
  _preset_file = strfree(_preset_file);
  _preset_file = strnew(val);
}

/**
  Get the preset filename.
  */
const char* Fl_Native_File_Chooser::preset_file() const {
  return(_preset_file);
}


int Fl_Native_File_Chooser::exist_dialog() {
  return(fl_choice("%s", fl_cancel, fl_ok, NULL, file_exists_message));
}

//
// End of "$Id: $".
//
_______________________________________________
fltk-dev mailing list
fltk-dev@easysw.com
http://lists.easysw.com/mailman/listinfo/fltk-dev

Reply via email to