diff --git a/CHANGES b/CHANGES index 360c49da6..2e1dba7ed 100644 --- a/CHANGES +++ b/CHANGES @@ -1,7 +1,10 @@ CHANGES IN FLTK 1.3.3 RELEASED: MMM DD YYYY + - on Linux/Unix, class Fl_Native_File_Chooser uses file dialogs of the Gnome + environment (provided by the libgtk dynamic library), when this is available, + and falls back to FLTK's Fl_File_Chooser, when it's not (STR #3088). - added class Fl_Copy_Surface allowing to copy graphical data to the clipboard - in a cross-platform way. + in a cross-platform way (STR #3058). - added support for pasting graphical data from the clipboard to an FLTK widget. - added class Fl_Image_Surface allowing to draw into an Fl_Image object. - removed constraint that lines are limited to 1024 chars in widget labels and browser lines (STR #2990). diff --git a/FL/Fl.H b/FL/Fl.H index 020f38cce..e0bcd3194 100644 --- a/FL/Fl.H +++ b/FL/Fl.H @@ -173,7 +173,7 @@ public: /// a text in a text widget will change focus to the next text widget. /// (This is considered 'old' behavior) /// - /// When switched off, the cursor will stop at the end of the text. + /// When switched off (default), the cursor will stop at the end of the text. /// Pressing Tab or Ctrl-Tab will advance the keyboard focus. /// /// See also: Fl_Input_::tab_nav() @@ -189,19 +189,23 @@ public: // decides to choose the file. // \todo implement me //OPTION_FILECHOOSER_PREVIEW, - /// If visible focus is switched on, FLTK will draw a dotted rectangle + /// If visible focus is switched on (default), FLTK will draw a dotted rectangle /// inside the widget that will receive the next keystroke. If switched /// off, no such indicator will be drawn and keyboard navigation /// is disabled. OPTION_VISIBLE_FOCUS, - /// If text drag-and-drop is enabled, the user can select and drag text + /// If text drag-and-drop is enabled (default), the user can select and drag text /// from any text widget. If disabled, no dragging is possible, however /// dropping text from other applications still works. OPTION_DND_TEXT, - /// If tooltips are enabled, hovering the mouse over a widget with a + /// If tooltips are enabled (default), hovering the mouse over a widget with a /// tooltip text will open a little tooltip window until the mouse leaves /// the widget. If disabled, no tooltip is shown. OPTION_SHOW_TOOLTIPS, + /// When switched on (default), Fl_Native_File_Chooser runs GTK file dialogs + /// if the GTK library is available on the platform (linux/unix only). + /// When switched off, GTK file dialogs aren't used even if the GTK library is available. + OPTION_FNFC_USES_GTK, // don't change this, leave it always as the last element /// For internal use only. OPTION_LAST diff --git a/FL/Fl_Native_File_Chooser.H b/FL/Fl_Native_File_Chooser.H index ae115b765..a33985c17 100644 --- a/FL/Fl_Native_File_Chooser.H +++ b/FL/Fl_Native_File_Chooser.H @@ -3,7 +3,7 @@ // // FLTK native OS file chooser widget // -// Copyright 1998-2010 by Bill Spitzak and others. +// Copyright 1998-2014 by Bill Spitzak and others. // Copyright 2004 Greg Ercolano. // // This library is free software. Distribution and use rights are outlined in @@ -23,47 +23,46 @@ #ifndef FL_NATIVE_FILE_CHOOSER_H #define FL_NATIVE_FILE_CHOOSER_H -/* \file - Fl_Native_File_Chooser widget. */ - // Use Windows' chooser #ifdef WIN32 // #define _WIN32_WINNT 0x0501 // needed for OPENFILENAME's 'FlagsEx' -#include -#include // malloc -#include -#include // OPENFILENAME, GetOpenFileName() -#include // BROWSEINFO, SHBrowseForFolder() +# include +# include // malloc +# include +# include // OPENFILENAME, GetOpenFileName() +# include // BROWSEINFO, SHBrowseForFolder() #endif // Use Apple's chooser #ifdef __APPLE__ -#define MAXFILTERS 80 +# define MAXFILTERS 80 #endif // All else falls back to FLTK's own chooser #if ! defined(__APPLE__) && !defined(WIN32) -#include -#include // _POSIX_NAME_MAX +# include +# include // _POSIX_NAME_MAX #else -#include // FL_EXPORT +# include // FL_EXPORT #endif +class Fl_FLTK_File_Chooser; +class Fl_GTK_File_Chooser; /** - This class lets an FLTK application easily and consistently access - the operating system's native file chooser. Some operating systems - have very complex and specific file choosers that many users want - access to specifically, instead of FLTK's default file chooser(s). - + This class lets an FLTK application easily and consistently access + the operating system's native file chooser. Some operating systems + have very complex and specific file choosers that many users want + access to specifically, instead of FLTK's default file chooser(s). + In cases where there is no native file browser, FLTK's own file browser is used instead. - + To use this widget, use the following include in your code: \code #include \endcode - + The following example shows how to pick a single file: \code // Create and post the local native file chooser @@ -82,25 +81,28 @@ default: printf("PICKED: %s\n", fnfc.filename()); break; // FILE CHOSEN } \endcode - + The Fl_Native_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 + 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). - + Platform Specific Caveats - - - Under X windows, it's best if you call Fl_File_Icon::load_system_icons() + + - Under X windows, and if Fl::OPTION_FNFC_USES_GTK has not been switched off, + the widget attempts to use standard GTK file chooser dialogs if they are + available at run-time on the platform, and falls back to use FLTK's Fl_File_Chooser if they are not. + In the latter case, it's best if you call Fl_File_Icon::load_system_icons() at the start of main(), to enable the nicer looking file browser widgets. Use the static public attributes of class Fl_File_Chooser to localize the browser. - - Some operating systems support certain OS specific options; see + - Some operating systems support certain OS specific options; see Fl_Native_File_Chooser::options() for a list. - + \image html Fl_Native_File_Chooser.png "The Fl_Native_File_Chooser on different platforms." \image latex Fl_Native_File_Chooser.png "The Fl_Native_File_Chooser on different platforms" width=14cm - + */ class FL_EXPORT Fl_Native_File_Chooser { public: @@ -121,39 +123,39 @@ public: }; /** Localizable message */ static const char *file_exists_message; - + public: Fl_Native_File_Chooser(int val=BROWSE_FILE); ~Fl_Native_File_Chooser(); - + // Public methods - void type(int); - int type() const; - void options(int); + void type(int t); + int type() const ; + void options(int o); int options() const; int count() const; - const char *filename() const; - const char *filename(int i) const; - void directory(const char *val); + const char *filename() const ; + const char *filename(int i) const ; + void directory(const char *val) ; const char *directory() const; - void title(const char *); + void title(const char *t); const char* title() const; - const char *filter() const; - void filter(const char *); - int filters() const; - void filter_value(int i); - int filter_value() const; - void preset_file(const char*); + const char *filter() const ; + void filter(const char *f); + int filters() const ; + void filter_value(int i) ; + int filter_value() const ; + void preset_file(const char*f) ; const char* preset_file() const; - const char *errmsg() const; - int show(); - + const char *errmsg() const ; + int show() ; + #ifdef WIN32 private: int _btype; // kind-of browser to show() int _options; // general options OPENFILENAMEW _ofn; // GetOpenFileName() & GetSaveFileName() struct - BROWSEINFOW _binf; // SHBrowseForFolder() struct + BROWSEINFOW _binf; // SHBrowseForFolder() struct char **_pathnames; // array of pathnames int _tpathnames; // total pathnames char *_directory; // default pathname to use @@ -163,14 +165,14 @@ private: int _nfilters; // number of filters parse_filter counted char *_preset_file; // the file to preselect char *_errmsg; // error message - + // Private methods void errmsg(const char *msg); - + void clear_pathnames(); void set_single_pathname(const char *s); void add_pathname(const char *s); - + void FreePIDL(LPITEMIDLIST pidl); void ClearOFN(); void ClearBINF(); @@ -179,7 +181,7 @@ private: int showfile(); static int CALLBACK Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data); int showdir(); - + void parse_filter(const char *); void clear_filters(); void add_filter(const char *, const char *); @@ -195,22 +197,22 @@ private: char *_directory; // default pathname to use char *_title; // title for window char *_preset_file; // the 'save as' filename - + char *_filter; // user-side search filter, eg: - // C Files\t*.[ch]\nText Files\t*.txt" - + // C Files\t*.[ch]\nText Files\t*.txt" + char *_filt_names; // filter names (tab delimited) - // eg. "C Files\tText Files" - + // eg. "C Files\tText Files" + char *_filt_patt[MAXFILTERS]; // array of filter patterns, eg: // _filt_patt[0]="*.{cxx,h}" // _filt_patt[1]="*.txt" - + int _filt_total; // parse_filter() # of filters loaded int _filt_value; // index of the selected filter char *_errmsg; // error message - + // Private methods void errmsg(const char *msg); void clear_pathnames(); @@ -225,6 +227,7 @@ private: #if ! defined(__APPLE__) && !defined(WIN32) private: +#if FLTK_ABI_VERSION <= 10302 int _btype; // kind-of browser to show() int _options; // general options int _nfilters; @@ -235,17 +238,100 @@ private: char *_prevvalue; // Returned filename char *_directory; char *_errmsg; // error message - Fl_File_Chooser *_file_chooser; - - // Private methods - void errmsg(const char *msg); - int type_fl_file(int); - void parse_filter(); - void keeplocation(); - int exist_dialog(); +#endif + static int have_looked_for_GTK_libs; + union { + Fl_FLTK_File_Chooser *_x11_file_chooser; + Fl_GTK_File_Chooser *_gtk_file_chooser; + }; #endif }; +#if !defined(__APPLE__) && !defined(WIN32) +class Fl_FLTK_File_Chooser { + friend class Fl_Native_File_Chooser; +protected: + int _btype; // kind-of browser to show() + int _options; // general options + int _nfilters; + char *_filter; // user supplied filter + char *_parsedfilt; // parsed filter + int _filtvalue; // selected filter + char *_preset_file; + char *_prevvalue; // Returned filename + char *_directory; + char *_errmsg; // error message + Fl_FLTK_File_Chooser(int val); + virtual ~Fl_FLTK_File_Chooser(); + void errmsg(const char *msg); + int type_fl_file(int); + void parse_filter(); + int exist_dialog(); + Fl_File_Chooser *_file_chooser; + virtual void type(int); + int type() const; + void options(int); + int options() const; + virtual int count() const; + virtual const char *filename() const; + virtual const char *filename(int i) const; + void directory(const char *val); + const char *directory() const; + virtual void title(const char *); + virtual const char* title() const; + const char *filter() const; + void filter(const char *); + int filters() const; + void filter_value(int i); + int filter_value() const; + void preset_file(const char*); + const char* preset_file() const; + const char *errmsg() const; + virtual int show(); +}; + + +class Fl_GTK_File_Chooser : public Fl_FLTK_File_Chooser { + friend class Fl_Native_File_Chooser; +private: + typedef struct _GtkWidget GtkWidget; + typedef struct _GtkFileFilterInfo GtkFileFilterInfo; + struct pair { + Fl_GTK_File_Chooser* running; // the running Fl_GTK_File_Chooser + const char *filter; // a filter string of the chooser + pair(Fl_GTK_File_Chooser* c, const char *f) { + running = c; + filter = strdup(f); + }; + ~pair() { + free((char*)filter); + }; + }; + 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 + const char *previous_filter; + + int fl_gtk_chooser_wrapper(); // method that wraps the GTK widget + Fl_GTK_File_Chooser(int val); + virtual ~Fl_GTK_File_Chooser(); + static int did_find_GTK_libs; + static void probe_for_GTK_libs(void); + virtual void type(int); + virtual int count() const; + virtual const char *filename() const; + virtual const char *filename(int i) const; + virtual void title(const char *); + virtual const char* title() const; + virtual int show(); + void changed_output_type(const char *filter); + + static int custom_gtk_filter_function(const GtkFileFilterInfo*, Fl_GTK_File_Chooser::pair*); + static void free_pair(pair *p); +}; +#endif // !defined(__APPLE__) && !defined(WIN32) #endif /*FL_NATIVE_FILE_CHOOSER_H*/ diff --git a/documentation/src/Fl_Native_File_Chooser.png b/documentation/src/Fl_Native_File_Chooser.png index 1f1e6686f..ea92d92cc 100644 Binary files a/documentation/src/Fl_Native_File_Chooser.png and b/documentation/src/Fl_Native_File_Chooser.png differ diff --git a/src/Fl.cxx b/src/Fl.cxx index 9d64b95fb..c3b935987 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -2069,6 +2069,8 @@ bool Fl::option(Fl_Option opt) options_[OPTION_DND_TEXT] = tmp; opt_prefs.get("ShowTooltips", tmp, 1); // default: on options_[OPTION_SHOW_TOOLTIPS] = tmp; + opt_prefs.get("FNFCUsesGTK", tmp, 1); // default: on + options_[OPTION_FNFC_USES_GTK] = tmp; } { // next, check the user preferences // override system options only, if the option is set ( >= 0 ) @@ -2086,6 +2088,8 @@ bool Fl::option(Fl_Option opt) if (tmp >= 0) options_[OPTION_DND_TEXT] = tmp; opt_prefs.get("ShowTooltips", tmp, -1); if (tmp >= 0) options_[OPTION_SHOW_TOOLTIPS] = tmp; + opt_prefs.get("FNFCUsesGTK", tmp, -1); + if (tmp >= 0) options_[OPTION_FNFC_USES_GTK] = tmp; } { // now, if the developer has registered this app, we could as for per-application preferences } diff --git a/src/Fl_Native_File_Chooser_FLTK.cxx b/src/Fl_Native_File_Chooser_FLTK.cxx index 6c5f359b5..3fb62ad10 100644 --- a/src/Fl_Native_File_Chooser_FLTK.cxx +++ b/src/Fl_Native_File_Chooser_FLTK.cxx @@ -1,10 +1,9 @@ // "$Id$" // -// FLTK native OS file chooser widget +// FLTK native file chooser widget wrapper for GTK's GtkFileChooserDialog // -// Copyright 1998-2010 by Bill Spitzak and others. -// Copyright 2004 Greg Ercolano. -// API changes + filter improvements by Nathan Vander Wilt 2005 +// Copyright 1998-2014 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 @@ -25,24 +24,20 @@ #define FLTK_CHOOSER_CREATE Fl_File_Chooser::CREATE #include "Fl_Native_File_Chooser_common.cxx" +#include "Fl_Native_File_Chooser_GTK.cxx" + #include #include +int Fl_Native_File_Chooser::have_looked_for_GTK_libs = 0; + /** - 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(). -*/ + 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) { - //// CANT USE THIS -- MESSES UP LINKING/CREATES DEPENDENCY ON fltk_images. - //// Have app call this from main() instead. - //// - //// static int init = 0; // 'first time' initialize flag - //// if ( init == 0 ) { - //// // Initialize when instanced for first time - //// Fl_File_Icon::load_system_icons(); - //// init = 1; - //// } +#if FLTK_ABI_VERSION <= 10302 _btype = val; _options = NO_OPTIONS; _filter = NULL; @@ -52,17 +47,201 @@ Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) { _prevvalue = NULL; _directory = NULL; _errmsg = NULL; - _file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL); - type(val); // do this after _file_chooser created +#endif // FLTK_ABI_VERSION + if (have_looked_for_GTK_libs == 0) { + // First Time here, try to find the GTK libs if they are installed + if (Fl::option(Fl::OPTION_FNFC_USES_GTK)) { + Fl_GTK_File_Chooser::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 (Fl_GTK_File_Chooser::did_find_GTK_libs) _gtk_file_chooser = new Fl_GTK_File_Chooser(val); + else _x11_file_chooser = new Fl_FLTK_File_Chooser(val); +} + +/** + Destructor. + Deallocates any resources allocated to this widget. + */ +Fl_Native_File_Chooser::~Fl_Native_File_Chooser() { + delete _x11_file_chooser; +} + +/** + Sets the current Fl_Native_File_Chooser::Type of browser. + */ +void Fl_Native_File_Chooser::type(int t) { return _x11_file_chooser->type(t); } +/** + Gets the current Fl_Native_File_Chooser::Type of browser. + */ +int Fl_Native_File_Chooser::type() const { return _x11_file_chooser->type(); } +/** + 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. +

+ \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 + USE_FILTER_EXT Chooser filter pilots the output file extension. Ignored Used Ignored +\endcode + */ +void Fl_Native_File_Chooser::options(int o) { _x11_file_chooser->options(o); } +/** + Gets the platform specific Fl_Native_File_Chooser::Option flags. + */ +int Fl_Native_File_Chooser::options() const { return _x11_file_chooser->options(); } + +/** + Returns the number of filenames (or directory names) the user selected. +

+ \b Example: + \code + if ( fnfc->show() == 0 ) { + // Print all filenames user selected + for (int n=0; ncount(); n++ ) { + printf("%d) '%s'\n", n, fnfc->filename(n)); + } + } + \endcode + */ +int Fl_Native_File_Chooser::count() const { return _x11_file_chooser->count(); } +/** + 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 { return _x11_file_chooser->filename(); } +/** + Return one of the filenames the user selected. + Use count() to determine how many filenames the user selected. +

+ \b Example: + \code + if ( fnfc->show() == 0 ) { + // Print all filenames user selected + for (int n=0; ncount(); n++ ) { + printf("%d) '%s'\n", n, fnfc->filename(n)); + } + } + \endcode + */ +const char *Fl_Native_File_Chooser::filename(int i) const { return _x11_file_chooser->filename(i); } +/** + 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) { _x11_file_chooser->directory(val); } +/** + Returns the current preset directory() value. + */ +const char *Fl_Native_File_Chooser::directory() const { return _x11_file_chooser->directory(); } +/** + 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 *t) { _x11_file_chooser->title(t); } +/** + 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 { return _x11_file_chooser->title(); } +/** + Returns the filter string last set. + Can be NULL if no filter was set. + */ +const char *Fl_Native_File_Chooser::filter() const { return _x11_file_chooser->filter(); } +/** + Sets the filename filters used for browsing. + The default is NULL, which browses all files. +

+ The filter string can be any of: +

+ - 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") +

+ The format of each filter is a wildcard, or an optional user description + followed by '\\t' and the wildcard. +

+ 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 *f) { _x11_file_chooser->filter(f); } +/** + Gets how many filters were available, not including "All Files" + */ +int Fl_Native_File_Chooser::filters() const { return _x11_file_chooser->filters(); } +/** + 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 i) { _x11_file_chooser->filter_value(i); } +/** + 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 _x11_file_chooser->filter_value(); } +/** + 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* f) { _x11_file_chooser->preset_file(f); } +/** + Get the preset filename. + */ +const char* Fl_Native_File_Chooser::preset_file() const { return _x11_file_chooser->preset_file(); } +/** + 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 _x11_file_chooser->errmsg(); } +/** + 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() { return _x11_file_chooser->show(); } + + +Fl_FLTK_File_Chooser::Fl_FLTK_File_Chooser(int val) { + _btype = 0; + _options = 0; + _filter = NULL; + _filtvalue = 0; + _parsedfilt = NULL; + _preset_file = NULL; + _prevvalue = NULL; + _directory = NULL; + _errmsg = NULL; + _file_chooser= NULL; + if (val >= 0) { + _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() { +Fl_FLTK_File_Chooser::~Fl_FLTK_File_Chooser() { delete _file_chooser; + _file_chooser = NULL; _filter = strfree(_filter); _parsedfilt = strfree(_parsedfilt); _preset_file = strfree(_preset_file); @@ -71,286 +250,169 @@ Fl_Native_File_Chooser::~Fl_Native_File_Chooser() { _errmsg = strfree(_errmsg); } + // PRIVATE: SET ERROR MESSAGE -void Fl_Native_File_Chooser::errmsg(const char *msg) { +void Fl_FLTK_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) { +int Fl_FLTK_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); + case Fl_Native_File_Chooser::BROWSE_FILE: + return(Fl_File_Chooser::SINGLE); + case Fl_Native_File_Chooser::BROWSE_DIRECTORY: + return(Fl_File_Chooser::SINGLE | Fl_File_Chooser::DIRECTORY); + case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: + return(Fl_File_Chooser::MULTI); + case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: + return(Fl_File_Chooser::DIRECTORY | Fl_File_Chooser::MULTI); + case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: + return(Fl_File_Chooser::SINGLE | Fl_File_Chooser::CREATE); + case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: + return(Fl_File_Chooser::DIRECTORY | Fl_File_Chooser::MULTI | Fl_File_Chooser::CREATE); default: - return(FLTK_CHOOSER_SINGLE); + return(Fl_File_Chooser::SINGLE); } } -/** - Sets the current Fl_Native_File_Chooser::Type of browser. - */ -void Fl_Native_File_Chooser::type(int val) { +void Fl_FLTK_File_Chooser::type(int val) { _btype = val; _file_chooser->type(type_fl_file(val)); } -/** - Gets the current Fl_Native_File_Chooser::Type of browser. - */ -int Fl_Native_File_Chooser::type() const { +int Fl_FLTK_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. -

- \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 - USE_FILTER_EXT Chooser filter pilots the output file extension. Ignored Used Ignored - \endcode -*/ -void Fl_Native_File_Chooser::options(int val) { +void Fl_FLTK_File_Chooser::options(int val) { _options = val; } -/** - Gets the platform specific Fl_Native_File_Chooser::Option flags. -*/ -int Fl_Native_File_Chooser::options() const { +int Fl_FLTK_File_Chooser::options() const { return(_options); } -/** - 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() { +int Fl_FLTK_File_Chooser::show() { + // FILTER - if ( _parsedfilt ) { - _file_chooser->filter(_parsedfilt); - } + if ( _parsedfilt ) { + _file_chooser->filter(_parsedfilt); + } - // FILTER VALUE - // Set this /after/ setting the filter - // - _file_chooser->filter_value(_filtvalue); + // 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); - } + // DIRECTORY + if ( _directory && _directory[0] ) { + _file_chooser->directory(_directory); + } else { + _file_chooser->directory(_prevvalue); + } - // PRESET FILE - if ( _preset_file ) { - _file_chooser->value(_preset_file); - } + // PRESET FILE + if ( _preset_file ) { + _file_chooser->value(_preset_file); + } - // OPTIONS: PREVIEW - _file_chooser->preview( (options() & PREVIEW) ? 1 : 0); + // OPTIONS: PREVIEW + _file_chooser->preview( (options() & Fl_Native_File_Chooser::PREVIEW) ? 1 : 0); - // OPTIONS: NEW FOLDER - if ( options() & NEW_FOLDER ) - _file_chooser->type(_file_chooser->type() | FLTK_CHOOSER_CREATE); // on + // OPTIONS: NEW FOLDER + if ( options() & Fl_Native_File_Chooser::NEW_FOLDER ) + _file_chooser->type(_file_chooser->type() | Fl_File_Chooser::CREATE); // on + + // SHOW + _file_chooser->show(); - // SHOW - _file_chooser->show(); + // BLOCK WHILE BROWSER SHOWN + while ( _file_chooser->shown() ) { + Fl::wait(); + } - // 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 - 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); - } - } + // HANDLE SHOWING 'SaveAs' CONFIRM + if ( options() & Fl_Native_File_Chooser::SAVEAS_CONFIRM && type() == Fl_Native_File_Chooser::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); + if ( _file_chooser->count() ) return(0); + else return(1); } -/** - 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 { +const char *Fl_FLTK_File_Chooser::errmsg() const { return(_errmsg ? _errmsg : "No error"); } -/** - Return the filename the user choose. - 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 ( _file_chooser->count() > 0 ) return(_file_chooser->value()); - return(""); -} - -/** - Return one of the filenames the user selected. - Use count() to determine how many filenames the user selected. -

- \b Example: - \code - if ( fnfc->show() == 0 ) { - // Print all filenames user selected - for (int n=0; ncount(); n++ ) { - printf("%d) '%s'\n", n, fnfc->filename(n)); - } +const char* Fl_FLTK_File_Chooser::filename() const { + if ( _file_chooser->count() > 0 ) { + return(_file_chooser->value()); } - \endcode - */ -const char* Fl_Native_File_Chooser::filename(int i) const { - if ( i < _file_chooser->count() ) - return(_file_chooser->value(i+1)); // convert fltk 1 based to our 0 based 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) { +const char* Fl_FLTK_File_Chooser::filename(int i) const { + if ( i < _file_chooser->count() ) + return(_file_chooser->value(i+1)); // convert fltk 1 based to our 0 based + return(""); +} + +void Fl_FLTK_File_Chooser::title(const char *val) { _file_chooser->label(val); } -/** - 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 { - return(_file_chooser->label()); +const char *Fl_FLTK_File_Chooser::title() const { + return(_file_chooser->label()); } -/** - Sets the filename filters used for browsing. - The default is NULL, which browses all files. -

- The filter string can be any of: -

- - 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") -

- The format of each filter is a wildcard, or an optional user description - followed by '\\t' and the wildcard. -

- 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) { +void Fl_FLTK_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 { +const char *Fl_FLTK_File_Chooser::filter() const { return(_filter); } -/** -Gets how many filters were available, not including "All Files" -*/ -int Fl_Native_File_Chooser::filters() const { +int Fl_FLTK_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) { +void Fl_FLTK_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); +int Fl_FLTK_File_Chooser::filter_value() const { + return _filtvalue; } -/** - Returns the number of filenames (or directory names) the user selected. -

- \b Example: - \code - if ( fnfc->show() == 0 ) { - // Print all filenames user selected - for (int n=0; ncount(); n++ ) { - printf("%d) '%s'\n", n, fnfc->filename(n)); - } - } - \endcode -*/ -int Fl_Native_File_Chooser::count() const { - return(_file_chooser->count()); +int Fl_FLTK_File_Chooser::count() const { + return _file_chooser->count(); } -/** - 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) { +void Fl_FLTK_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); +const char *Fl_FLTK_File_Chooser::directory() const { + return _directory; } // PRIVATE: Convert our filter format to fltk's chooser format @@ -363,7 +425,7 @@ const char *Fl_Native_File_Chooser::directory() const { // Returns a modified version of the filter that the caller is responsible // for freeing with strfree(). // -void Fl_Native_File_Chooser::parse_filter() { +void Fl_FLTK_File_Chooser::parse_filter() { _parsedfilt = strfree(_parsedfilt); // clear previous parsed filter (if any) _nfilters = 0; char *in = _filter; @@ -426,27 +488,17 @@ void Fl_Native_File_Chooser::parse_filter() { //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) { +void Fl_FLTK_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); +const char* Fl_FLTK_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)); +int Fl_FLTK_File_Chooser::exist_dialog() { + return fl_choice("%s", fl_cancel, fl_ok, NULL, Fl_Native_File_Chooser::file_exists_message); } // diff --git a/src/Fl_Native_File_Chooser_GTK.cxx b/src/Fl_Native_File_Chooser_GTK.cxx new file mode 100644 index 000000000..6464198ea --- /dev/null +++ b/src/Fl_Native_File_Chooser_GTK.cxx @@ -0,0 +1,700 @@ +// "$Id$" +// +// FLTK native file chooser widget wrapper for GTK's GtkFileChooserDialog +// +// Copyright 1998-2014 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 +#include // for dlopen et al +#include // for setlocale + +/* --------------------- Type definitions from GLIB and GTK --------------------- */ +/* all of this is from the public gnome API, so unlikely to change */ +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif +typedef void* gpointer; +typedef int gint; +typedef unsigned int guint; +typedef unsigned long gulong; +typedef gint gboolean; +typedef char gchar; +typedef struct _GSList GSList; +struct _GSList +{ + gpointer data; + GSList *next; +}; +#define g_slist_next(slist) ((slist) ? (((GSList *)(slist))->next) : NULL) +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkFileChooser GtkFileChooser; +typedef struct _GtkDialog GtkDialog; +typedef struct _GtkWindow GtkWindow; +typedef struct _GdkDrawable GdkWindow; +typedef struct _GtkFileFilter GtkFileFilter; +typedef struct _GtkToggleButton GtkToggleButton; +typedef enum { + GTK_FILE_FILTER_FILENAME = 1 << 0, + GTK_FILE_FILTER_URI = 1 << 1, + GTK_FILE_FILTER_DISPLAY_NAME = 1 << 2, + GTK_FILE_FILTER_MIME_TYPE = 1 << 3 +} GtkFileFilterFlags; +struct _GtkFileFilterInfo +{ + GtkFileFilterFlags contains; + + const gchar *filename; + const gchar *uri; + const gchar *display_name; + const gchar *mime_type; +}; +typedef struct _GtkFileFilterInfo GtkFileFilterInfo; +typedef gboolean (*GtkFileFilterFunc) (const GtkFileFilterInfo *filter_info, gpointer data); +typedef void (*GDestroyNotify)(gpointer data); +typedef enum +{ + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, + GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER +} GtkFileChooserAction; +#define GTK_STOCK_CANCEL "gtk-cancel" +#define GTK_STOCK_SAVE "gtk-save" +#define GTK_STOCK_OPEN "gtk-open" +const int GTK_RESPONSE_NONE = -1; +const int GTK_RESPONSE_ACCEPT = -3; +const int GTK_RESPONSE_DELETE_EVENT = -4; +const int GTK_RESPONSE_CANCEL = -6; +typedef void (*GCallback)(void); +#define G_CALLBACK(f) ((GCallback) (f)) +typedef int GConnectFlags; +typedef struct _GClosure GClosure; +typedef void (*GClosureNotify)(gpointer data, GClosure *closure); + +/* --------------------- End of Type definitions from GLIB and GTK --------------------- */ + +int Fl_GTK_File_Chooser::did_find_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; + +// 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_now(GtkWidget *); +typedef void (*XX_gtk_widget_show_now)(GtkWidget *); +static XX_gtk_widget_show_now fl_gtk_widget_show_now = NULL; + +// GdkWindow* gtk_widget_get_window(GtkWidget *); +typedef GdkWindow* (*XX_gtk_widget_get_window)(GtkWidget *); +static XX_gtk_widget_get_window fl_gtk_widget_get_window = NULL; + +// Window gdk_x11_drawable_get_xid(GdkWindow *); +typedef Window (*XX_gdk_x11_drawable_get_xid)(GdkWindow *); +static XX_gdk_x11_drawable_get_xid fl_gdk_x11_drawable_get_xid = 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; + + +Fl_GTK_File_Chooser::Fl_GTK_File_Chooser(int val) : Fl_FLTK_File_Chooser(-1) +{ + gtkw_ptr = NULL; // used to hold a GtkWidget* + 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 + _btype = val; + previous_filter = NULL; +} + +Fl_GTK_File_Chooser::~Fl_GTK_File_Chooser() +{ + // Should free up resources taken for... + if(gtkw_ptr) { + fl_gtk_widget_destroy (gtkw_ptr); + gtkw_ptr = NULL; + } + if(gtkw_filename) { + fl_g_free(gtkw_filename); + gtkw_filename = NULL; + } + if(gtkw_slist) { + GSList *iter = (GSList *)gtkw_slist; + while(iter) { + if(iter->data) fl_g_free(iter->data); + iter = g_slist_next(iter); + } + fl_g_slist_free((GSList *)gtkw_slist); + gtkw_slist = NULL; + } + gtkw_count = 0; // assume we have no files selected now + gtkw_title = strfree(gtkw_title); +} + +void Fl_GTK_File_Chooser::type(int val) { + _btype = val; +} + +int Fl_GTK_File_Chooser::count() const { + return gtkw_count; +} + +const char *Fl_GTK_File_Chooser::filename() const +{ + if(gtkw_ptr) { + if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) { + return gtkw_filename; + } + else { + GSList *iter = (GSList *)gtkw_slist; + char *nm = (char *)iter->data; + return nm; + } + } + return(""); +} + +const char *Fl_GTK_File_Chooser::filename(int i) const +{ + if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) { + return gtkw_filename; + } + else { + if ((unsigned)i < gtkw_count) { + GSList *iter = (GSList *)gtkw_slist; + char *nm = (char *)fl_g_slist_nth_data(iter, i); + return nm; + } + } + return(""); +} + +void Fl_GTK_File_Chooser::title(const char *val) +{ + strfree(gtkw_title); + gtkw_title = strnew(val); +} + +const char* Fl_GTK_File_Chooser::title() const +{ + return gtkw_title; +} + +/* changes the extension of the outfile in the chooser according to newly selected filter */ +void Fl_GTK_File_Chooser::changed_output_type(const char *filter) +{ + if ( !(options()&Fl_Native_File_Chooser::USE_FILTER_EXT) ) return; + if (strchr(filter, '(') || strchr(filter, '{') || strchr(filter+1, '*') || strncmp(filter, "*.", 2)) return; + const char *p = fl_gtk_file_chooser_get_filename((GtkFileChooser*)gtkw_ptr); + if (!p) return; + p = fl_filename_name(p); + const char *q = strrchr(p, '.'); + if (!q) q = p + strlen(p); + char *r = new char[strlen(p) + strlen(filter)]; + strcpy(r, p); + strcpy(r + (q - p), filter + 1); + fl_gtk_file_chooser_set_current_name((GtkFileChooser*)gtkw_ptr, r); + delete[] r; +} + +/* Filters files before display in chooser. + Also used to detect when the filter just changed */ +gboolean Fl_GTK_File_Chooser::custom_gtk_filter_function(const GtkFileFilterInfo *info, Fl_GTK_File_Chooser::pair* p) +{ + if (p->running->previous_filter != p->filter) { + p->running->changed_output_type(p->filter); + p->running->previous_filter = p->filter; + } + return (gboolean)fl_filename_match(info->filename, p->filter); +} + +void Fl_GTK_File_Chooser::free_pair(Fl_GTK_File_Chooser::pair *p) +{ + delete p; +} + +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); +} + +int Fl_GTK_File_Chooser::show() +{ + // The point here is that after running a GTK dialog, the calling program's current locale is modified. + // To avoid that, we memorize the calling program's current locale, and the locale as modified + // by GTK after the first dialog use. We restore the calling program's current locale + // before returning, and we set the locale as modified by GTK before subsequent GTK dialog uses. + static bool first = true; + char *p; + char *before = NULL; + static char *gtk_wants = NULL; + // record in before the calling program's current locale + p = setlocale(LC_ALL, NULL); + if (p) before = strdup(p); + if (gtk_wants) { // set the locale as GTK 'wants it' + setlocale(LC_ALL, gtk_wants); + } + int retval = fl_gtk_chooser_wrapper(); // may change the locale + if (first) { + first = false; + // record in gtk_wants the locale as modified by the GTK dialog + p = setlocale(LC_ALL, NULL); + if (p) gtk_wants = strdup(p); + } + if (before) { + setlocale(LC_ALL, before); // restore calling program's current locale + free(before); + } + return retval; +} + + +static void run_response_handler(GtkDialog *dialog, gint response_id, gpointer data) +{ + gint *ri = (gint *)data; + *ri = response_id; +} + + +int Fl_GTK_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); + } + + if(gtkw_ptr) { // discard the previous dialog widget + fl_gtk_widget_destroy (gtkw_ptr); + gtkw_ptr = NULL; + } + + // set the dialog action type + GtkFileChooserAction gtw_action_type; + switch (_btype) { + case Fl_Native_File_Chooser::BROWSE_DIRECTORY: + case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: + gtw_action_type = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; + break; + + case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: + gtw_action_type = GTK_FILE_CHOOSER_ACTION_SAVE; + break; + + case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: + gtw_action_type = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER; + break; + + case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: + case Fl_Native_File_Chooser::BROWSE_FILE: + default: + gtw_action_type = GTK_FILE_CHOOSER_ACTION_OPEN; + break; + } + // create a new dialog + gtkw_ptr = fl_gtk_file_chooser_dialog_new (gtkw_title, + NULL, /* parent_window */ + gtw_action_type, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + gtw_action_type == GTK_FILE_CHOOSER_ACTION_SAVE || gtw_action_type == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ? + GTK_STOCK_SAVE : GTK_STOCK_OPEN, + GTK_RESPONSE_ACCEPT, + NULL); + // did we create a valid dialog widget? + if(!gtkw_ptr) { + // fail + return -1; + } + + // set the dialog properties + switch (_btype) { + case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY: + case Fl_Native_File_Chooser::BROWSE_MULTI_FILE: + fl_gtk_file_chooser_set_select_multiple((GtkFileChooser *)gtkw_ptr, TRUE); + break; + + case Fl_Native_File_Chooser::BROWSE_SAVE_FILE: + if (_preset_file)fl_gtk_file_chooser_set_current_name ((GtkFileChooser *)gtkw_ptr, fl_filename_name(_preset_file)); + /* FALLTHROUGH */ + case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY: + fl_gtk_file_chooser_set_create_folders((GtkFileChooser *)gtkw_ptr, TRUE); + fl_gtk_file_chooser_set_do_overwrite_confirmation ((GtkFileChooser *)gtkw_ptr, (_options & Fl_Native_File_Chooser::SAVEAS_CONFIRM)?TRUE:FALSE); + break; + + case Fl_Native_File_Chooser::BROWSE_DIRECTORY: + case Fl_Native_File_Chooser::BROWSE_FILE: + default: + break; + } + + if (_directory && _directory[0]) fl_gtk_file_chooser_set_current_folder((GtkFileChooser *)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, + (GtkFileFilterFunc)Fl_GTK_File_Chooser::custom_gtk_filter_function, + new Fl_GTK_File_Chooser::pair(this, p), + (GDestroyNotify)Fl_GTK_File_Chooser::free_pair); + fl_gtk_file_chooser_add_filter((GtkFileChooser *)gtkw_ptr, filter_tab[count]); + p = strtok(NULL, "\t"); + count++; + } + free(filter); + fl_gtk_file_chooser_set_filter((GtkFileChooser *)gtkw_ptr, filter_tab[_filtvalue]); + previous_filter = NULL; + 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 *)gtkw_ptr, gfilter); + } + } + + GtkWidget *toggle = fl_gtk_check_button_new_with_label(Fl_File_Chooser::hidden_label); + fl_gtk_file_chooser_set_extra_widget((GtkFileChooser *)gtkw_ptr, toggle); + fl_g_signal_connect_data(toggle, "toggled", G_CALLBACK(hidden_files_cb), gtkw_ptr, NULL, (GConnectFlags) 0); + Fl_Window* firstw = Fl::first_window(); + fl_gtk_widget_show_now(gtkw_ptr); // map the GTK window on screen + if (firstw) { + GdkWindow* gdkw = fl_gtk_widget_get_window(gtkw_ptr); + Window xw = fl_gdk_x11_drawable_get_xid(gdkw); // get the X11 ref of the GTK window + XSetTransientForHint(fl_display, xw, fl_xid(firstw)); // set the GTK window transient for the last FLTK win + } + gboolean state = fl_gtk_file_chooser_get_show_hidden((GtkFileChooser *)gtkw_ptr); + fl_gtk_toggle_button_set_active((GtkToggleButton *)toggle, state); + + gint response_id = GTK_RESPONSE_NONE; + fl_g_signal_connect_data(gtkw_ptr, "response", G_CALLBACK(run_response_handler), &response_id, NULL, (GConnectFlags) 0); + while (response_id == GTK_RESPONSE_NONE) { // loop that shows the GTK dialog window + fl_gtk_main_iteration(); // one iteration of the GTK event loop + while (XEventsQueued(fl_display, QueuedAfterReading)) { // emulate modal dialog + XEvent xevent; + XNextEvent(fl_display, &xevent); + Window xid = xevent.xany.window; + if (xevent.type == ConfigureNotify) xid = xevent.xmaprequest.window; + if (!fl_find(xid)) continue; // skip events to non-FLTK windows + // process Expose and ConfigureNotify events + if ( xevent.type == Expose || xevent.type == ConfigureNotify ) fl_handle(xevent); + } + Fl::flush(); // do the drawings needed after Expose events + } + + if (response_id == GTK_RESPONSE_ACCEPT) { + if (_parsedfilt) { + GtkFileFilter *gfilter = fl_gtk_file_chooser_get_filter((GtkFileChooser *)gtkw_ptr); + for (_filtvalue = 0; _filtvalue < _nfilters; _filtvalue++) { + if (filter_tab[_filtvalue] == gfilter) break; + } + } + + // discard any filenames or lists from previous calls + if(gtkw_filename) { + fl_g_free(gtkw_filename); + gtkw_filename = NULL; + } + if(gtkw_slist) { + GSList *iter = (GSList *)gtkw_slist; + while(iter) { + if(iter->data) fl_g_free(iter->data); + iter = g_slist_next(iter); + } + fl_g_slist_free((GSList *)gtkw_slist); + gtkw_slist = NULL; + } + gtkw_count = 0; // assume we have no files selected now + + if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) { + gtkw_filename = fl_gtk_file_chooser_get_filename ((GtkFileChooser *)gtkw_ptr); + if (gtkw_filename) { + gtkw_count = 1; + result = 0; + //printf("single: %s\n", gtkw_filename); + } + } + else { + gtkw_slist = fl_gtk_file_chooser_get_filenames((GtkFileChooser *)gtkw_ptr); + gtkw_count = fl_g_slist_length((GSList *)gtkw_slist); + if(gtkw_count) result = 0; + + // puts("multiple"); + // GSList *iter = (GSList *)gtkw_slist; + // printf ("Selected %d files\n", gtkw_count); + // while(iter) { + // char *nm = (char *)iter->data; + // printf("%s\n", nm); + // iter = g_slist_next(iter); + // } + } + } + delete[] filter_tab; + if ( response_id == GTK_RESPONSE_DELETE_EVENT) gtkw_ptr = NULL; + else fl_gtk_widget_hide (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 + +// 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; } + +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. + */ +void Fl_GTK_File_Chooser::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 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 + if (ptr_gtk && ptr_glib) { +#ifdef DEBUG + puts("selected GTK-2\n"); +#endif + } + else {// Try then with GTK3 + ptr_gtk = fl_dlopen("libgtk-3.so", "libgtk-3.so.0"); +#ifdef DEBUG + if (ptr_gtk && ptr_glib) { + puts("selected GTK-3\n"); + } +#endif + } + + if((!ptr_glib) || (!ptr_gtk)) { +#ifdef DEBUG + puts("Failure to load libglib or libgtk"); +#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_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_now, ptr_gtk); + GET_SYM(gtk_widget_get_window, ptr_gtk); + GET_SYM(gdk_x11_drawable_get_xid, 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 = 1; +} // probe_for_GTK_libs + +// +// End of "$Id$". +//