STR #3088: make Fl_Native_File_Chooser use the standard GTK file dialog when available.

git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@10186 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Manolo Gouy 2014-06-07 12:01:59 +00:00
parent 460544455d
commit baebff2227
7 changed files with 1169 additions and 320 deletions

View File

@ -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).

12
FL/Fl.H
View File

@ -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

View File

@ -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 <stdio.h>
#include <stdlib.h> // malloc
#include <windows.h>
#include <commdlg.h> // OPENFILENAME, GetOpenFileName()
#include <shlobj.h> // BROWSEINFO, SHBrowseForFolder()
# include <stdio.h>
# include <stdlib.h> // malloc
# include <windows.h>
# include <commdlg.h> // OPENFILENAME, GetOpenFileName()
# include <shlobj.h> // 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 <FL/Fl_File_Chooser.H>
#include <unistd.h> // _POSIX_NAME_MAX
# include <FL/Fl_File_Chooser.H>
# include <unistd.h> // _POSIX_NAME_MAX
#else
#include <FL/filename.H> // FL_EXPORT
# include <FL/filename.H> // 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 <FL/Fl_Native_File_Chooser.H>
\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).
<B>Platform Specific Caveats</B>
- 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*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 401 KiB

View File

@ -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
}

View File

@ -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 <sys/stat.h>
#include <string.h>
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.
<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
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.
<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 { 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.
<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 { 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.
<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 *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.
<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
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.
<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));
}
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.
<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) {
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.
<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 {
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);
}
//

View File

@ -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 <FL/x.H>
#include <dlfcn.h> // for dlopen et al
#include <locale.h> // 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$".
//