diff --git a/Fltk/GUI_Extras.cpp b/Fltk/GUI_Extras.cpp index a45f8d227f99cf9b4c3815c42c9390c583fcf28c..3c13af19bb5ca396e36c25480eefbba4f41c43a8 100644 --- a/Fltk/GUI_Extras.cpp +++ b/Fltk/GUI_Extras.cpp @@ -1,4 +1,4 @@ -// $Id: GUI_Extras.cpp,v 1.38 2008-01-07 21:32:58 geuzaine Exp $ +// $Id: GUI_Extras.cpp,v 1.39 2008-01-07 22:59:29 geuzaine Exp $ // // Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle // @@ -76,6 +76,11 @@ int file_chooser(int multi, int create, const char *message, default: if(fc->filename()) ret = fc->count(); break; } thefilterindex = fc->filter_value(); +#if defined(__APPLE__) + // FIXME: hack to clear the KEYDOWN state that remains when calling + // the file chooser on Mac using a keyboard shortcut + Fl::e_state = 0; +#endif return ret; #else Fl_File_Chooser::show_label = "Format:"; diff --git a/contrib/NativeFileChooser/CHANGES b/contrib/NativeFileChooser/CHANGES new file mode 100644 index 0000000000000000000000000000000000000000..2c32501d2cda5a4fca10b4954ddbd008eedc6368 --- /dev/null +++ b/contrib/NativeFileChooser/CHANGES @@ -0,0 +1,129 @@ +# WARNING: Makefile automatically derives version number from the first line +# of this file that starts with x.x +# | +# \|/ +# v + +0.84 +----------------------------------------------------- + o FLTK2 SUPPORT! + Merged in Frederic Hoerni's mods for fltk2 support, + converted to fltk2 style namespace. + + o Makefile fix for IMGLIB from Gonzalo Garramuno (fltk.general 09/30/07) + + o Incorporated Ian MacArthur's Makefile for fltk-config + + o 80 column compliance + + o Build: + > Tar file now extracts with version number in the directory name, + > 'make tar' logic now moved into separate shell script + + o Windows: + > Fixed memory leak with lpstrFile (alloc'ed twice) + > Removed unused strfree() code in ClearOFN() + > Small code formatting fixes + + +0.83d +----------------------------------------------------- + o Linux: + > Added Shane Hill's #include <sys/stat.h> to Fl_Native_File_Chooser_FLTK.cxx + + o Windows: + > Added OFN_NOCHANGEDIR flag to prevent GetOpenFileName() from changing the CWD + > Added Andreas Sch?mann's fix for _nfilters++ to the add_filter() method + > Fixed showfile() GetCurrentDirectory() call + + o Copyright fixes and README.txt brought up to date + + +0.83c -- 02/03/2006 fix release +----------------------------------------------------- + o Windows: + > Fixed behavior of BROWSE_SAVE_FILE + > Fixed SAVEAS_CONFIRM + > Mods to getcwd() macros in test-browser.cxx for MINGW + > Fixes to test-browser.cxx for MinGW compiles (removed #ifdef VISUAL_STUDIO) + + o Linux: + > "Save File" now has working "Show SaveAs Confirm" dialog + > "Save File" under linux wasn't letting user type in a filename + > 'int type()' method wasn't implemented, added _btype + > Removed ifeq() cruft from Makefile.LINUX + + o Applied Ian's fixes to Makefile.fltkconfig + o Makefile was sourcing user's ~/.cshrc, added '-f' to SHELL=/bin/csh -f + +0.83b -- 01/07/2006 erco: small patches from Ian +------------------------------------------------ + o Makefile.fltkconfig WINDOWS -> WIN32 + o Tar forces owner/group to 0/0, fixed perms on images dir + +0.83a -- 01/06/2006 erco: small patches from Ian +------------------------------------------------ + o Makefile.fltkconfig patch + o Enforce correct perms on files in tar file + +0.83 -- 12/06/2005 erco: misc +----------------------------------------------- + o Windows: Added preset_file() support + o Linux: missing filters() method + o All: Added options() to API, removed macosx_*() + o Added options() to the API with flags: + o NEW_FOLDER + o PREVIEW + o SAVEAS_CONFIRM + o Removed macosx_*() stuff -- bad idea + +0.82 -- 12/05/2005 erco: misc fixes after merge +----------------------------------------------- + o win32 filter fixes for Alessandro's 11/29 report + o Rellocated Macos reference links from code to REFERENCE-MAC.txt + o Made sure all #ifdefs use _WIN32 instead of other variants. + o Fixed problem with FLTK chooser not showing its icons (natevw) + o Added macosx_*() platform specific accessors + + TODO: See ./TODO for outstanding items + +0.81 -- 11/28/2005 erco: linux version cleanup +---------------------------------------------- + o Support documented filters + o free/strdup -> new/delete + o added #ifndef at top of FL/Fl_Native_File_Chooser.H to prevent recursion + +0.80 -- 11/27/2005 erco+nate: code merge, cleanup +------------------------------------------------- + o Merge in Nathan's changes + o Updated docs for all changes noted here + o Rewrote mac's filter code to support old "*.a" and new "Desc1\t*.a\nDesc2\t*.b" syntax + o Changed Nathan's set_filter() -> filter_value() (consistent with FLTK's own chooser) + o Renamed test program to test-browser.cxx + o Code cleanup: variable + method naming, 80 column conformance, indents, strdup/malloc -> new + TODO: Linux version needs code cleanup (strdup -> new, etc) + +0.70 -- 08/04/05 erco: general cleanup +-------------------------------------- + + o Implemented Linux. + This was easy -- just use Fl_File_Chooser. + + o Removed references to non-existant size() method from docs; + the correct method name is total_filenames() + +0.62 -- 02/02/2005 +------------------ + + o Added Ian's mods to main.cxx to use getcwd() instead of fixed path + + o Added #include/#define's to support getcwd() under Windows VS + +0.61 -- 01/26/2005 +------------------ + o Applied Ian MacArthur's 01/26/2005 fixes, + and Makefile.fltkconfig, including tab fixes and line 90 .obj -> .o fix. + + o Enabled /Wall on Windows VS7.x, and fixed all problems related to Fl_Native* + (mainly, ctor type value was being ignored) + diff --git a/contrib/NativeFileChooser/CREDITS b/contrib/NativeFileChooser/CREDITS new file mode 100644 index 0000000000000000000000000000000000000000..f565f48dff4be683333dcef5e38f8227c8b5830d --- /dev/null +++ b/contrib/NativeFileChooser/CREDITS @@ -0,0 +1,5 @@ +Greg Ercolano -- initial version for mac + win32, code maintainer +Ian MacArthur -- various fixes, additions, Makefile.fltkconfig +Nathan Vander Wilt -- filter code for WIN32 and Mac, FLTK chooser +Boris Mayer -- various debugging +Frederic Hoerni -- fltk2 port diff --git a/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser.H b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser.H new file mode 100644 index 0000000000000000000000000000000000000000..88c0acb77a469fa1717141bfd32475f2141ce393 --- /dev/null +++ b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser.H @@ -0,0 +1,40 @@ +// +// Fl_Native_File_Chooser.H -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +#ifndef FL_NATIVE_FILE_CHOOSER_H +#define FL_NATIVE_FILE_CHOOSER_H + +// Use Windows' chooser +#ifdef _WIN32 +#include <FL/Fl_Native_File_Chooser_WIN32.H> +#endif + +// Use Apple's chooser +#ifdef __APPLE__ +#include <FL/Fl_Native_File_Chooser_MAC.H> +#endif + +// All else falls back to FLTK's own chooser +#if ! defined(__APPLE__) && !defined(_WIN32) +#include <FL/Fl_Native_File_Chooser_FLTK.H> +#endif + +#endif /*FL_NATIVE_FILE_CHOOSER_H*/ diff --git a/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_FLTK.H b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_FLTK.H new file mode 100644 index 0000000000000000000000000000000000000000..128f36612bb1aad67abac2ec01c60d5af2335bf8 --- /dev/null +++ b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_FLTK.H @@ -0,0 +1,97 @@ +// +// Fl_Native_File_Chooser_DEFAULT.H -- FLTK native OS file chooser widget +// +// Copyright 2005 by Nathan Vander Wilt. +// March 2005 - wrapper around Fl_File_Chooser +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +#include <FL/Fl_File_Chooser.H> +#include <string.h> + +class Fl_Native_File_Chooser { +public: + enum Type { + BROWSE_FILE = 0, + BROWSE_DIRECTORY, + BROWSE_MULTI_FILE, + BROWSE_MULTI_DIRECTORY, + BROWSE_SAVE_FILE, + BROWSE_SAVE_DIRECTORY + }; + enum Option { + NO_OPTIONS = 0x0000, // no options enabled + SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite + // confirm dialog (if supported) + NEW_FOLDER = 0x0002, // Show 'New Folder' icon + // (if supported) + PREVIEW = 0x0004 // enable preview mode + }; +private: + int _btype; // kind-of browser to show() + int _options; // general options + 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_File_Chooser *file_chooser; + + int exist_dialog() { + return(fl_choice("File exists. Are you sure you want to overwrite?", + "Cancel", " OK ", NULL)); + } + void load_system_icons() { + Fl_File_Icon::load_system_icons(); + } + + int _nfilters; + + // Private methods + void errmsg(const char *msg); + int type_fl_file(int); + void parse_filter(); + void keeplocation(); + +public: + Fl_Native_File_Chooser(int val=BROWSE_FILE); + ~Fl_Native_File_Chooser(); + + // Public methods + void type(int); + int type() const; + void options(int); + int options() const; + int count() const; + const char *filename() const; + const char *filename(int i) const; + void directory(const char *val); + const char *directory() const; + void title(const char *); + const char* title() const; + const char *filter() const; + void filter(const char *); + int filters() const { return(_nfilters); } + void filter_value(int i); + int filter_value() const; + void preset_file(const char*); + const char* preset_file() const; + const char *errmsg() const; + int show(); +}; diff --git a/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_MAC.H b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_MAC.H new file mode 100644 index 0000000000000000000000000000000000000000..49577c40748f126b2d2c3547f55616bcdbddaec1 --- /dev/null +++ b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_MAC.H @@ -0,0 +1,138 @@ +// +// Fl_Native_File_Chooser_MAC.H -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// 10 20 30 40 50 60 70 +// | | | | | | | +// 4567890123456789012345678901234567890123456789012345678901234567890123456789 + +// OSX-SPECIFIC NATIVE BROWSER +#ifdef __APPLE_CC__ +#include <Carbon/Carbon.h> +#else +#include <Carbon.h> +#endif + +#include <FL/filename.H> +#define MAXFILTERS 80 + +class Fl_Native_File_Chooser { +public: + enum Type { + BROWSE_FILE = 0, + BROWSE_DIRECTORY, + BROWSE_MULTI_FILE, + BROWSE_MULTI_DIRECTORY, + BROWSE_SAVE_FILE, + BROWSE_SAVE_DIRECTORY + }; + enum Option { + NO_OPTIONS = 0x0000, // no options enabled + SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite + // confirm dialog (if supported) + NEW_FOLDER = 0x0002, // Show 'New Folder' icon + // (if supported) + PREVIEW = 0x0004, // enable preview mode + }; +protected: + NavDialogCreationOptions _opts; // file navigation options +private: + int _btype; // kind-of browser to show() + int _options; // general options + NavDialogRef _ref; // file navigation reference + NavActionState _keepstate; // holds button permissions + NavMenuItemSpec _tempitem; // Popup menu selection + char **_pathnames; // array of pathnames + int _tpathnames; // total pathnames + 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" + + char *_filt_names; // filter names (tab delimited) + // 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 CLASS TO HANDLE NAVIGATION DIALOG REPLY STRUCT + // Class-ified, mainly to ensure proper cleanup. + // + class NavReply { + int _valid_reply; + NavReplyRecord _reply; + public: + NavReply(); + ~NavReply(); + int get_reply(NavDialogRef& ref); + int get_saveas_basename(char *s, int slen); + int get_dirname(char *s, int slen); + int get_pathnames(char **&pathnames, int& tpathnames); + }; + + // Private methods + void errmsg(const char *msg); + void clear_pathnames(); + void set_single_pathname(const char *s); + int get_saveas_basename(NavDialogRef& ref); + int get_pathnames(NavDialogRef& ref); + static void event_handler(NavEventCallbackMessage callBackSelector, + NavCBRecPtr cbparm, void *data); + + void clear_filters(); + void add_filter(const char *, const char *); + void parse_filter(const char *from); + static Boolean filter_proc_cb(AEDesc *, void *, void *, NavFilterModes); + Boolean filter_proc_cb2(AEDesc*, void*, void*, NavFilterModes); + int post(); + +public: + Fl_Native_File_Chooser(int val = BROWSE_FILE); + ~Fl_Native_File_Chooser(); + + // Public methods + void type(int); + int type() const; + void options(int); + int options() const; + int count() const; + const char *filename() const; + const char *filename(int i) const; + void directory(const char *); + const char *directory() const; + void title(const char *); + const char *title() const; + const char *filter() const; + void filter(const char *); + void filter_value(int i) { _filt_value = i; } + int filter_value() { return(_filt_value); } + int filters() { return(_filt_total); } + void preset_file(const char *); + const char *preset_file(); + const char *errmsg() const; + int show(); +}; diff --git a/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_WIN32.H b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_WIN32.H new file mode 100644 index 0000000000000000000000000000000000000000..889b4842433c76c38f4e6dfede8dcbd1f6b2c8dd --- /dev/null +++ b/contrib/NativeFileChooser/FL/Fl_Native_File_Chooser_WIN32.H @@ -0,0 +1,108 @@ +// +// Fl_Native_File_Chooser_WINDOWS.H -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// April 2005 - API changes, improved filter processing by Nathan Vander Wilt +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +// #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() + +class Fl_Native_File_Chooser { +public: + enum Type { + BROWSE_FILE = 0, + BROWSE_DIRECTORY, + BROWSE_MULTI_FILE, + BROWSE_MULTI_DIRECTORY, + BROWSE_SAVE_FILE, + BROWSE_SAVE_DIRECTORY + }; + enum Option { + NO_OPTIONS = 0x0000, // no options enabled + SAVEAS_CONFIRM = 0x0001, // Show native 'Save As' overwrite + // confirm dialog (if supported) + NEW_FOLDER = 0x0002, // Show 'New Folder' icon + // (if supported) + PREVIEW = 0x0004, // enable preview mode + }; +private: + int _btype; // kind-of browser to show() + int _options; // general options + OPENFILENAME _ofn; // GetOpenFileName() & GetSaveFileName() struct + BROWSEINFO _binf; // SHBrowseForFolder() struct + char **_pathnames; // array of pathnames + int _tpathnames; // total pathnames + char *_directory; // default pathname to use + char *_title; // title for window + char *_filter; // user-side search filter + char *_parsedfilt; // filter parsed for Windows dialog + 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(ITEMIDLIST *pidl); + void ClearOFN(); + void ClearBINF(); + void Win2Unix(char *s); + void Unix2Win(char *s); + 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 *); + +public: + Fl_Native_File_Chooser(int val = BROWSE_FILE); + ~Fl_Native_File_Chooser(); + + // Public methods + void type(int val); + int type() const; + void options(int); + int options() const; + int count() const; + const char *filename() const; + const char *filename(int i) const; + void directory(const char *val); + const char *directory() const; + void title(const char *val); + const char *title() const; + const char *filter() const; + void filter(const char *val); + int filters() const { return _nfilters; } + void filter_value(int i); + int filter_value() const; + void preset_file(const char *); + const char *preset_file() const; + const char *errmsg() const; + int show(); +}; diff --git a/contrib/NativeFileChooser/Fl_Native_File_Chooser.cxx b/contrib/NativeFileChooser/Fl_Native_File_Chooser.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5c851cb20118105993a4e96066497ee7141dce6a --- /dev/null +++ b/contrib/NativeFileChooser/Fl_Native_File_Chooser.cxx @@ -0,0 +1,36 @@ +// +// Fl_Native_File_Chooser.cxx -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// + +// Use Windows' chooser +#ifdef _WIN32 +#include "Fl_Native_File_Chooser_WIN32.cxx" +#endif + +// Use Apple's chooser +#ifdef __APPLE__ +#include "Fl_Native_File_Chooser_MAC.cxx" +#endif + +// All else falls back to FLTK's own chooser +#if ! defined(__APPLE__) && !defined(_WIN32) +#include "Fl_Native_File_Chooser_FLTK.cxx" +#endif + diff --git a/contrib/NativeFileChooser/Fl_Native_File_Chooser_FLTK.cxx b/contrib/NativeFileChooser/Fl_Native_File_Chooser_FLTK.cxx new file mode 100644 index 0000000000000000000000000000000000000000..0244c530842ea18bf0f62e2d03bd70c46e866a69 --- /dev/null +++ b/contrib/NativeFileChooser/Fl_Native_File_Chooser_FLTK.cxx @@ -0,0 +1,374 @@ +// +// Fl_Native_File_Chooser_FLTK.cxx -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// API changes + filter improvements by Nathan Vander Wilt 2005 +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please keep code 80 column compliant. +// +// 10 20 30 40 50 60 70 +// | | | | | | | +// 4567890123456789012345678901234567890123456789012345678901234567890123456789 +// + +#ifdef FLTK1 +// +// FLTK1 +// +#include <FL/Fl_Native_File_Chooser.H> +#define FNFC_CLASS Fl_Native_File_Chooser +#define FNFC_CTOR Fl_Native_File_Chooser +#define FLTK_CHOOSER_SINGLE Fl_File_Chooser::SINGLE +#define FLTK_CHOOSER_DIRECTORY Fl_File_Chooser::DIRECTORY +#define FLTK_CHOOSER_MULTI Fl_File_Chooser::MULTI +#define FLTK_CHOOSER_CREATE Fl_File_Chooser::CREATE +#else +// +// FLTK2 +// +#include <fltk/NativeFileChooser.h> +#include <fltk/run.h> +#define FNFC_CTOR NativeFileChooser +#define FNFC_CLASS fltk::FNFC_CTOR +#define FLTK_CHOOSER_SINGLE fltk::FileChooser::SINGLE +#define FLTK_CHOOSER_DIRECTORY fltk::FileChooser::DIRECTORY +#define FLTK_CHOOSER_MULTI fltk::FileChooser::MULTI +#define FLTK_CHOOSER_CREATE fltk::FileChooser::CREATE +#endif + +#include "common.cxx" +#include <sys/stat.h> + +// CTOR +FNFC_CLASS::FNFC_CTOR(int val) { + static int init = 0; // 'first time' initialize flag + if ( init == 0 ) { + // Initialize when instanced for first time + load_system_icons(); + init = 1; + } + _btype = val; + _options = NO_OPTIONS; + _filter = NULL; + _filtvalue = 0; + _parsedfilt = NULL; + _preset_file = NULL; + _prevvalue = NULL; + _directory = NULL; + _errmsg = NULL; +#ifdef FLTK1 + file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL); +#else + file_chooser = new fltk::FileChooser(NULL, NULL, 0, NULL); +#endif + type(val); // do this after file_chooser created + _nfilters = 0; +} + +// DTOR +FNFC_CLASS::~FNFC_CTOR() { + delete file_chooser; + _filter = strfree(_filter); + _parsedfilt = strfree(_parsedfilt); + _preset_file = strfree(_preset_file); + _prevvalue = strfree(_prevvalue); + _directory = strfree(_directory); + _errmsg = strfree(_errmsg); +} + +// PRIVATE: SET ERROR MESSAGE +void FNFC_CLASS::errmsg(const char *msg) { + _errmsg = strfree(_errmsg); + _errmsg = strnew(msg); +} + +// PRIVATE: translate Native types to Fl_File_Chooser types +int FNFC_CLASS::type_fl_file(int val) { + switch (val) { + case BROWSE_FILE: + return(FLTK_CHOOSER_SINGLE); + case BROWSE_DIRECTORY: + return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_DIRECTORY); + case BROWSE_MULTI_FILE: + return(FLTK_CHOOSER_MULTI); + case BROWSE_MULTI_DIRECTORY: + return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI); + case BROWSE_SAVE_FILE: + return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_CREATE); + case BROWSE_SAVE_DIRECTORY: + return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI | FLTK_CHOOSER_CREATE); + default: + return(FLTK_CHOOSER_SINGLE); + } +} + +void FNFC_CLASS::type(int val) { + _btype = val; + file_chooser->type(type_fl_file(val)); +} + +int FNFC_CLASS::type() const { + return(_btype); +} + +// SET OPTIONS +void FNFC_CLASS::options(int val) { + _options = val; +} + +// GET OPTIONS +int FNFC_CLASS::options() const { + return(_options); +} + +// Show chooser, blocks until done. +// RETURNS: +// 0 - user picked a file +// 1 - user cancelled +// -1 - failed; errmsg() has reason +// +int FNFC_CLASS::show() { + // FILTER + if ( _parsedfilt ) { + file_chooser->filter(_parsedfilt); + } + + // FILTER VALUE + // Set this /after/ setting the filter + // + file_chooser->filter_value(_filtvalue); + + // DIRECTORY + if ( _directory && _directory[0] ) { + file_chooser->directory(_directory); + } else { + file_chooser->directory(_prevvalue); + } + + // PRESET FILE + if ( _preset_file ) { + file_chooser->value(_preset_file); + } + + // OPTIONS: PREVIEW + file_chooser->preview( (options() & PREVIEW) ? 1 : 0); + + // OPTIONS: NEW FOLDER + if ( options() & NEW_FOLDER ) + file_chooser->type(file_chooser->type() | + FLTK_CHOOSER_CREATE); // on + + // SHOW + file_chooser->show(); + +#ifdef FLTK1 + // FLTK1: BLOCK WHILE BROWSER SHOWN + while ( file_chooser->shown() ) { + Fl::wait(); + } +#else + // FLTK2: BLOCK WHILE BROWSER SHOWN + while ( file_chooser->visible() ) { + fltk::wait(); + } +#endif + + if ( file_chooser->value() && file_chooser->value()[0] ) { + _prevvalue = strfree(_prevvalue); + _prevvalue = strnew(file_chooser->value()); + _filtvalue = file_chooser->filter_value(); // update filter value + + // HANDLE SHOWING 'SaveAs' CONFIRM + if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { + struct stat buf; + if ( stat(file_chooser->value(), &buf) != -1 ) { + if ( buf.st_mode & S_IFREG ) { // Regular file + exists? + if ( exist_dialog() == 0 ) { + return(1); + } + } + } + } + } + + if ( file_chooser->count() ) return(0); + else return(1); +} + +// RETURN ERROR MESSAGE +const char *FNFC_CLASS::errmsg() const { + return(_errmsg ? _errmsg : "No error"); +} + +// GET FILENAME +const char* FNFC_CLASS::filename() const { + if ( file_chooser->count() > 0 ) return(file_chooser->value()); + return(""); +} + +// GET FILENAME FROM LIST OF FILENAMES +const char* FNFC_CLASS::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 TITLE +// Can be NULL if no title desired. +// +void FNFC_CLASS::title(const char *val) { + file_chooser->label(val); +} + +// GET TITLE +// Can return NULL if none set. +// +const char *FNFC_CLASS::title() const { + return(file_chooser->label()); +} + +// SET FILTER +// Can be NULL if no filter needed +// +void FNFC_CLASS::filter(const char *val) { + _filter = strfree(_filter); + _filter = strnew(val); + parse_filter(); +} + +// GET FILTER +const char *FNFC_CLASS::filter() const { + return(_filter); +} + +// SET SELECTED FILTER +void FNFC_CLASS::filter_value(int val) { + _filtvalue = val; +} + +// RETURN SELECTED FILTER +int FNFC_CLASS::filter_value() const { + return(_filtvalue); +} + +// GET TOTAL FILENAMES CHOSEN +int FNFC_CLASS::count() const { + return(file_chooser->count()); +} + +// PRESET PATHNAME +// Can be NULL if no preset is desired. +// +void FNFC_CLASS::directory(const char *val) { + _directory = strfree(_directory); + _directory = strnew(val); +} + +// GET PRESET PATHNAME +// Can return NULL if none set. +// +const char *FNFC_CLASS::directory() const { + return(_directory); +} + +// Convert our filter format to fltk's chooser format +// FROM TO (FLTK) +// ------------------------- -------------------------- +// "*.cxx" "*.cxx Files(*.cxx)" +// "C Files\t*.{cxx,h}" "C Files(*.{cxx,h})" +// "C Files\t*.{cxx,h}\nText Files\t*.txt" "C Files(*.{cxx,h})\tText Files(*.txt)" +// +// Returns a modified version of the filter that the caller is responsible +// for freeing with strfree(). +// +void FNFC_CLASS::parse_filter() { + _parsedfilt = strfree(_parsedfilt); // clear previous parsed filter (if any) + _nfilters = 0; + char *in = _filter; + if ( !in ) return; + + int has_name = strchr(in, '\t') ? 1 : 0; + + char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard + char wildcard[1024] = ""; // parsed wildcard + char name[1024] = ""; + + // Parse filter user specified + for ( ; 1; in++ ) { + + /*** DEBUG + printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n", + *in, mode, name, wildcard); + ***/ + + switch (*in) { + // FINISHED PARSING NAME? + case '\t': + if ( mode != 'n' ) goto regchar; + mode = 'w'; + break; + + // ESCAPE NEXT CHAR + case '\\': + ++in; + goto regchar; + + // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? + case '\r': + case '\n': + case '\0': + // APPEND NEW FILTER TO LIST + if ( wildcard[0] ) { + // OUT: "name(wild)\tname(wild)" + char comp[2048]; + sprintf(comp, "%s%.511s(%.511s)", ((_parsedfilt)?"\t":""), + name, wildcard); + _parsedfilt = strapp(_parsedfilt, comp); + _nfilters++; + //DEBUG printf("DEBUG: PARSED FILT NOW <%s>\n", _parsedfilt); + } + // RESET + wildcard[0] = name[0] = '\0'; + mode = strchr(in, '\t') ? 'n' : 'w'; + // DONE? + if ( *in == '\0' ) return; // done + else continue; // not done yet, more filters + + // Parse all other chars + default: // handle all non-special chars + regchar: // handle regular char + switch ( mode ) { + case 'n': chrcat(name, *in); continue; + case 'w': chrcat(wildcard, *in); continue; + } + break; + } + } + //NOTREACHED +} + +// SET PRESET FILENAME +void FNFC_CLASS::preset_file(const char* val) { + _preset_file = strfree(_preset_file); + _preset_file = strnew(val); +} + +// GET PRESET FILENAME +const char* FNFC_CLASS::preset_file() const { + return(_preset_file); +} diff --git a/contrib/NativeFileChooser/Fl_Native_File_Chooser_MAC.cxx b/contrib/NativeFileChooser/Fl_Native_File_Chooser_MAC.cxx new file mode 100644 index 0000000000000000000000000000000000000000..5965521acc3d7c5a0adda62ad25567220908651b --- /dev/null +++ b/contrib/NativeFileChooser/Fl_Native_File_Chooser_MAC.cxx @@ -0,0 +1,863 @@ +// +// Fl_Native_File_Chooser_MAC.cxx -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// FLTK2/MAC port by Greg Ercolano 2007. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please keep code 80 column compliant. +// +// 10 20 30 40 50 60 70 +// | | | | | | | +// 4567890123456789012345678901234567890123456789012345678901234567890123456789 +// +// TODO: +// o When doing 'open file', only dir is preset, not filename. +// Possibly 'preset_file' could be used to select the filename. +// +#include "common.cxx" // strnew/strfree/strapp/chrcat + +#ifdef FLTK1 +// +// FLTK1 +// +#include <FL/Fl.H> +#include <FL/Fl_Native_File_Chooser.H> +#include <FL/filename.H> +#define FNFC_CLASS Fl_Native_File_Chooser +#define FNFC_CTOR Fl_Native_File_Chooser +#else +// +// FLTK2 +// +#include <fltk/NativeFileChooser.h> +#include <fltk/run.h> +#include <fltk/filename.h> +#define FNFC_CTOR NativeFileChooser +#define FNFC_CLASS fltk::FNFC_CTOR +#define fl_filename_match filename_match // fltk1 name -> fltk2 name +#define fl_filename_isdir filename_isdir // fltk1 name -> fltk2 name +#endif + +// TRY TO CONVERT AN AEDesc TO AN FSSpec +// As per Apple Technical Q&A QA1274 +// eg: http://developer.apple.com/qa/qa2001/qa1274.html +// Returns 'noErr' if OK, +// or an 'OSX result code' on error. +// +static int AEDescToFSSpec(const AEDesc* desc, FSSpec* fsspec) { + OSStatus err = noErr; + AEDesc coerceDesc; + // If AEDesc isn't already an FSSpec, convert it to one + if ( desc->descriptorType != typeFSS ) { + if ( ( err = AECoerceDesc(desc, typeFSS, &coerceDesc) ) == noErr ) { + // Get FSSpec out of AEDesc + err = AEGetDescData(&coerceDesc, fsspec, sizeof(FSSpec)); + AEDisposeDesc(&coerceDesc); + } + } else { + err = AEGetDescData(desc, fsspec, sizeof(FSSpec)); + } + return( err ); +} + +// CONVERT AN FSSpec TO A PATHNAME +static void FSSpecToPath(const FSSpec &spec, char *buff, int bufflen) { + FSRef fsRef; + FSpMakeFSRef(&spec, &fsRef); + FSRefMakePath(&fsRef, (UInt8*)buff, bufflen); +} + +// CONVERT REGULAR PATH -> FSSpec +// If file does not exist, expect fnfErr. +// Returns 'noErr' if OK, +// or an 'OSX result code' on error. +// +static OSStatus PathToFSSpec(const char *path, FSSpec &spec) { + OSStatus err; + FSRef ref; + if ((err = FSPathMakeRef((const UInt8*)path, &ref, NULL)) != noErr) { + return(err); + } + // FSRef -> FSSpec + if ((err = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, &spec, + NULL)) != noErr) { + return(err); + } + return(noErr); +} + +// NAVREPLY: CTOR +FNFC_CLASS::NavReply::NavReply() { + _valid_reply = 0; +} + +// NAVREPLY: DTOR +FNFC_CLASS::NavReply::~NavReply() { + if ( _valid_reply ) { + NavDisposeReply(&_reply); + } +} + +// GET REPLY FROM THE NAV* DIALOG +int FNFC_CLASS::NavReply::get_reply(NavDialogRef& ref) { + if ( _valid_reply ) { + NavDisposeReply(&_reply); // dispose of previous + _valid_reply = 0; + } + if ( ref == NULL || NavDialogGetReply(ref, &_reply) != noErr ) { + return(-1); + } + _valid_reply = 1; + return(0); +} + +// RETURN THE BASENAME USER WANTS TO 'Save As' +int FNFC_CLASS::NavReply::get_saveas_basename(char *s, int slen) { + if (CFStringGetCString(_reply.saveFileName, s, slen-1, + kCFStringEncodingUTF8) == false) { + s[0] = '\0'; + return(-1); + } + return(0); +} + +// RETURN THE DIRECTORY NAME +// Returns 0 on success, -1 on error. +// +int FNFC_CLASS::NavReply::get_dirname(char *s, int slen) { + FSSpec fsspec; + if ( AEDescToFSSpec(&_reply.selection, &fsspec) != noErr ) { + // Conversion failed? Return empty name + s[0] = 0; + return(-1); + } + FSSpecToPath(fsspec, s, slen); + return(0); +} + +// RETURN MULTIPLE DIRECTORIES +// Returns: 0 on success with pathnames[] containing pathnames selected, +// -1 on error +// +int FNFC_CLASS::NavReply::get_pathnames(char **&pathnames, + int& tpathnames) { + // How many items selected? + long count = 0; + if ( AECountItems(&_reply.selection, &count) != noErr ) + { return(-1); } + + // Allocate space for that many pathnames + pathnames = new char*[count]; + memset((void*)pathnames, 0, count*sizeof(char*)); + tpathnames = count; + + // Walk list of pathnames selected + for (short index=1; index<=count; index++) { + AEKeyword keyWord; + AEDesc desc; + if (AEGetNthDesc(&_reply.selection, index, typeFSS, &keyWord, + &desc) != noErr) { + pathnames[index-1] = strnew(""); + continue; + } + FSSpec fsspec; + if (AEGetDescData(&desc, &fsspec, sizeof(FSSpec)) != noErr ) { + pathnames[index-1] = strnew(""); + continue; + } + char s[4096]; + FSSpecToPath(fsspec, s, sizeof(s)-1); + pathnames[index-1] = strnew(s); + AEDisposeDesc(&desc); + } + return(0); +} + +// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS +void FNFC_CLASS::clear_pathnames() { + if ( _pathnames ) { + while ( --_tpathnames >= 0 ) { + _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); + } + delete [] _pathnames; + _pathnames = NULL; + } + _tpathnames = 0; +} + +// SET A SINGLE PATHNAME +void FNFC_CLASS::set_single_pathname(const char *s) { + clear_pathnames(); + _pathnames = new char*[1]; + _pathnames[0] = strnew(s); + _tpathnames = 1; +} + +// GET THE 'Save As' FILENAME +// Returns -1 on error, errmsg() has reason, filename == "". +// 0 if OK, filename() has filename chosen. +// +int FNFC_CLASS::get_saveas_basename(NavDialogRef& ref) { + if ( ref == NULL ) { + errmsg("get_saveas_basename: ref is NULL"); + return(-1); + } + NavReply reply; + OSStatus err; + if ((err = reply.get_reply(ref)) != noErr ) { + errmsg("NavReply::get_reply() failed"); + clear_pathnames(); + return(-1); + } + + char pathname[4096] = ""; + // Directory name.. + // -2 leaves room to append '/' + // + if ( reply.get_dirname(pathname, sizeof(pathname)-2) < 0 ) { + clear_pathnames(); + errmsg("NavReply::get_dirname() failed"); + return(-1); + } + // Append '/' + int len = strlen(pathname); + pathname[len++] = '/'; + pathname[len] = '\0'; + // Basename.. + if ( reply.get_saveas_basename(pathname+len, sizeof(pathname)-len) < 0 ) { + clear_pathnames(); + errmsg("NavReply::get_saveas_basename() failed"); + return(-1); + } + set_single_pathname(pathname); + return(0); +} + +// GET (POTENTIALLY) MULTIPLE FILENAMES +// Returns: +// -1 -- error, errmsg() has reason, filename == "" +// 0 -- OK, pathnames()/filename() has pathname(s) chosen +// +int FNFC_CLASS::get_pathnames(NavDialogRef& ref) { + if ( ref == NULL ) { + errmsg("get_saveas_basename: ref is NULL"); + return(-1); + } + NavReply reply; + OSStatus err; + if ((err = reply.get_reply(ref)) != noErr ) { + errmsg("NavReply::get_reply() failed"); + clear_pathnames(); + return(-1); + } + // First, clear pathnames array of any previous contents + clear_pathnames(); + if ( reply.get_pathnames(_pathnames, _tpathnames) < 0 ) { + clear_pathnames(); + errmsg("NavReply::get_dirname() failed"); + return(-1); + } + return(0); +} + +// NAV CALLBACK EVENT HANDLER +void FNFC_CLASS::event_handler( + NavEventCallbackMessage callBackSelector, + NavCBRecPtr cbparm, + void *data) { + OSStatus err; + FNFC_CLASS *nfb = (FNFC_CLASS*)data; + switch (callBackSelector) { + case kNavCBStart: + if ( nfb->directory() || nfb->preset_file() ) { + const char *pathname = nfb->directory() + ? nfb->directory() + : nfb->preset_file(); + FSSpec spec; + if ( ( err = PathToFSSpec(pathname, spec) ) != noErr ) { + fprintf(stderr, "PathToFSSpec(%s) failed: err=%d\n", + pathname, (int)err); + break; + } + AEDesc desc; + if ((err = AECreateDesc(typeFSS, &spec, sizeof(FSSpec), + &desc)) != noErr) { + fprintf(stderr, "AECreateDesc() failed: err=%d\n", + (int)err); + } + if ((err = NavCustomControl(cbparm->context, + kNavCtlSetLocation, + &desc)) != noErr) { + fprintf(stderr, "NavCustomControl() failed: err=%d\n", + (int)err); + } + AEDisposeDesc(&desc); + } + if ( nfb->_btype == BROWSE_SAVE_FILE && nfb->preset_file() ) { + CFStringRef namestr = + CFStringCreateWithCString(NULL, + nfb->preset_file(), + kCFStringEncodingASCII); + NavDialogSetSaveFileName(cbparm->context, namestr); + CFRelease(namestr); + } + NavCustomControl(cbparm->context, + kNavCtlSetActionState, + &nfb->_keepstate ); + + // Select the right filter in pop-up menu + if ( nfb->_filt_value == nfb->_filt_total ) { + // Select All Documents + NavPopupMenuItem kAll = kNavAllFiles; + NavCustomControl(cbparm->context, kNavCtlSelectAllType, &kAll); + } else if (nfb->_filt_value < nfb->_filt_total) { + // Select custom filter + nfb->_tempitem.version = kNavMenuItemSpecVersion; + nfb->_tempitem.menuCreator = 'extn'; + nfb->_tempitem.menuType = nfb->_filt_value; + *nfb->_tempitem.menuItemName = '\0'; // needed on 10.3+ + NavCustomControl(cbparm->context, + kNavCtlSelectCustomType, + &(nfb->_tempitem)); + } + break; + + case kNavCBPopupMenuSelect: + NavMenuItemSpecPtr ptr; + // they really buried this one! + ptr = (NavMenuItemSpecPtr)cbparm->eventData.eventDataParms.param; + if ( ptr->menuCreator ) { + // Gets index to filter ( menuCreator = 'extn' ) + nfb->_filt_value = ptr->menuType; + } else { + // All docs filter selected ( menuCreator = '\0\0\0\0' ) + nfb->_filt_value = nfb->_filt_total; + } + break; + + case kNavCBSelectEntry: + NavActionState astate; + switch ( nfb->_btype ) { + // these don't need selection override + case BROWSE_MULTI_FILE: + case BROWSE_MULTI_DIRECTORY: + case BROWSE_SAVE_FILE: + break; + + // These need to allow only one item, so disable + // Open button if user tries to select multiple files + case BROWSE_SAVE_DIRECTORY: + case BROWSE_DIRECTORY: + case BROWSE_FILE: + SInt32 selectcount; + AECountItems((AEDescList*)cbparm-> + eventData.eventDataParms.param, + &selectcount); + if ( selectcount > 1 ) { + NavCustomControl(cbparm->context, + kNavCtlSetSelection, + NULL); + astate = nfb->_keepstate | + kNavDontOpenState | + kNavDontChooseState; + NavCustomControl(cbparm->context, + kNavCtlSetActionState, + &astate ); + } + else { + astate= nfb->_keepstate | kNavNormalState; + NavCustomControl(cbparm->context, + kNavCtlSetActionState, + &astate ); + } + break; + } + break; + } +} + +// CONSTRUCTOR +FNFC_CLASS::FNFC_CTOR(int val) { + _btype = val; + NavGetDefaultDialogCreationOptions(&_opts); + _opts.optionFlags |= kNavDontConfirmReplacement; // no confirms for "save as" + _options = NO_OPTIONS; + _ref = NULL; + memset(&_tempitem, 0, sizeof(_tempitem)); + _pathnames = NULL; + _tpathnames = 0; + _title = NULL; + _filter = NULL; + _filt_names = NULL; + memset(_filt_patt, 0, sizeof(char*) * MAXFILTERS); + _filt_total = 0; + _filt_value = 0; + _directory = NULL; + _preset_file = NULL; + _errmsg = NULL; + _keepstate = kNavNormalState; +} + +// DESTRUCTOR +FNFC_CLASS::~FNFC_CTOR() { + // _opts // nothing to manage + if (_ref) { NavDialogDispose(_ref); _ref = NULL; } + // _options // nothing to manage + // _keepstate // nothing to manage + // _tempitem // nothing to manage + clear_pathnames(); + _directory = strfree(_directory); + _title = strfree(_title); + _preset_file = strfree(_preset_file); + _filter = strfree(_filter); + //_filt_names // managed by clear_filters() + //_filt_patt[i] // managed by clear_filters() + //_filt_total // managed by clear_filters() + clear_filters(); + //_filt_value // nothing to manage + _errmsg = strfree(_errmsg); +} + +// SET THE TYPE OF BROWSER +void FNFC_CLASS::type(int val) { + _btype = val; +} + +// GET TYPE OF BROWSER +int FNFC_CLASS::type() const { + return(_btype); +} + +// SET OPTIONS +void FNFC_CLASS::options(int val) { + _options = val; +} + +// GET OPTIONS +int FNFC_CLASS::options() const { + return(_options); +} + +// SHOW THE BROWSER WINDOW +// Returns: +// 0 - user picked a file +// 1 - user cancelled +// -1 - failed; errmsg() has reason +// +int FNFC_CLASS::show() { + + // Make sure fltk interface updates before posting our dialog +#ifdef FLTK1 + Fl::flush(); +#else + fltk::flush(); +#endif + + // BROWSER TITLE + CFStringRef cfs_title; + cfs_title = CFStringCreateWithCString(NULL, + _title ? _title : "No Title", + kCFStringEncodingASCII); + _opts.windowTitle = cfs_title; + + _keepstate = kNavNormalState; + + // BROWSER FILTERS + CFArrayRef filter_array = NULL; + { + // One or more filters specified? + if ( _filt_total ) { + // NAMES -> CFArrayRef + CFStringRef tab = CFSTR("\t"); + CFStringRef tmp_cfs; + tmp_cfs = CFStringCreateWithCString(NULL, _filt_names, + kCFStringEncodingASCII); + filter_array = CFStringCreateArrayBySeparatingStrings( + NULL, tmp_cfs, tab); + CFRelease(tmp_cfs); + CFRelease(tab); + _opts.popupExtension = filter_array; + _opts.optionFlags |= kNavAllFilesInPopup; + } else { + filter_array = NULL; + _opts.popupExtension = NULL; + _opts.optionFlags |= kNavAllFilesInPopup; + } + } + + // HANDLE OPTIONS WE SUPPORT + if ( _options & SAVEAS_CONFIRM ) { + _opts.optionFlags &= ~kNavDontConfirmReplacement; // enables confirm + } else { + _opts.optionFlags |= kNavDontConfirmReplacement; // disables confirm + } + + // POST BROWSER + int err = post(); + + // RELEASE _FILT_ARR + if ( filter_array ) CFRelease(filter_array); + filter_array = NULL; + _opts.popupExtension = NULL; + _filt_total = 0; + + // RELEASE TITLE + if ( cfs_title ) CFRelease(cfs_title); + cfs_title = NULL; + + return(err); +} + +// POST BROWSER +// Internal use only. +// Assumes '_opts' has been initialized. +// +// Returns: +// 0 - user picked a file +// 1 - user cancelled +// -1 - failed; errmsg() has reason +// +int FNFC_CLASS::post() { + + // INITIALIZE BROWSER + OSStatus err; + if ( _filt_total == 0 ) { // Make sure they match + _filt_value = 0; // TBD: move to someplace more logical? + } + + switch (_btype) { + case BROWSE_FILE: + case BROWSE_MULTI_FILE: + //_keepstate = kNavDontNewFolderState; + // Prompt user for one or more files + if ((err = NavCreateGetFileDialog( + &_opts, // options + 0, // file types + event_handler, // event handler + 0, // preview callback + filter_proc_cb, // filter callback + (void*)this, // callback data + &_ref)) != noErr ) { // dialog ref + errmsg("NavCreateGetFileDialog: failed"); + return(-1); + } + break; + + case BROWSE_DIRECTORY: + case BROWSE_MULTI_DIRECTORY: + _keepstate = kNavDontNewFolderState; + //FALLTHROUGH + + case BROWSE_SAVE_DIRECTORY: + // Prompts user for one or more files or folders + if ((err = NavCreateChooseFolderDialog( + &_opts, // options + event_handler, // event callback + 0, // filter callback + (void*)this, // callback data + &_ref)) != noErr ) { // dialog ref + errmsg("NavCreateChooseFolderDialog: failed"); + return(-1); + } + break; + + case BROWSE_SAVE_FILE: + // Prompt user for filename to 'save as' + if ((err = NavCreatePutFileDialog( + &_opts, // options + 0, // file types + 0, // file creator + event_handler, // event handler + (void*)this, // callback data + &_ref)) != noErr ) { // dialog ref + errmsg("NavCreatePutFileDialog: failed"); + return(-1); + } + break; + } + + // SHOW THE DIALOG + if ( ( err = NavDialogRun(_ref) ) != 0 ) { + char msg[80]; + sprintf(msg, "NavDialogRun: failed (err=%d)", (int)err); + errmsg(msg); + return(-1); + } + + // WHAT ACTION DID USER CHOOSE? + NavUserAction act = NavDialogGetUserAction(_ref); + if ( act == kNavUserActionNone ) { + errmsg("Nothing happened yet (dialog still open)"); + return(-1); + } + else if ( act == kNavUserActionCancel ) { // user chose 'cancel' + return(1); + } + else if ( act == kNavUserActionSaveAs ) { // user chose 'save as' + return(get_saveas_basename(_ref)); + } + + // TOO MANY FILES CHOSEN? + int ret = get_pathnames(_ref); + if ( _btype == BROWSE_FILE && ret == 0 && _tpathnames != 1 ) { + char msg[80]; + sprintf(msg, "Expected only one file to be chosen.. you chose %d.", + (int)_tpathnames); + errmsg(msg); + return(-1); + } + return(err); +} + +// SET ERROR MESSAGE +// Internal use only. +// +void FNFC_CLASS::errmsg(const char *msg) { + _errmsg = strfree(_errmsg); + _errmsg = strnew(msg); +} + +// RETURN ERROR MESSAGE +const char *FNFC_CLASS::errmsg() const { + return(_errmsg ? _errmsg : "No error"); +} + +// GET FILENAME +const char* FNFC_CLASS::filename() const { + if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); + return(""); +} + +// GET FILENAME FROM LIST OF FILENAMES +const char* FNFC_CLASS::filename(int i) const { + if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); + return(""); +} + +// GET TOTAL FILENAMES CHOSEN +int FNFC_CLASS::count() const { + return(_tpathnames); +} + +// PRESET PATHNAME +// Value can be NULL for none. +// +void FNFC_CLASS::directory(const char *val) { + _directory = strfree(_directory); + _directory = strnew(val); +} + +// GET PRESET PATHNAME +// Returned value can be NULL if none set. +// +const char* FNFC_CLASS::directory() const { + return(_directory); +} + +// SET TITLE +// Value can be NULL if no title desired. +// +void FNFC_CLASS::title(const char *val) { + _title = strfree(_title); + _title = strnew(val); +} + +// GET TITLE +// Returned value can be NULL if none set. +// +const char *FNFC_CLASS::title() const { + return(_title); +} + +// SET FILTER +// Can be NULL if no filter needed +// +void FNFC_CLASS::filter(const char *val) { + _filter = strfree(_filter); + _filter = strnew(val); + + // Parse filter user specified + // IN: _filter = "C Files\t*.{cxx,h}\nText Files\t*.txt" + // OUT: _filt_names = "C Files\tText Files" + // _filt_patt[0] = "*.{cxx,h}" + // _filt_patt[1] = "*.txt" + // _filt_total = 2 + // + parse_filter(_filter); +} + +// GET FILTER +// Returned value can be NULL if none set. +// +const char *FNFC_CLASS::filter() const { + return(_filter); +} + +// CLEAR ALL FILTERS +// Internal use only. +// +void FNFC_CLASS::clear_filters() { + _filt_names = strfree(_filt_names); + for (int i=0; i<_filt_total; i++) { + _filt_patt[i] = strfree(_filt_patt[i]); + } + _filt_total = 0; +} + +// PARSE USER'S FILTER SPEC +// Parses user specified filter ('in'), +// breaks out into _filt_patt[], _filt_names, and _filt_total. +// +// Handles: +// IN: OUT:_filt_names OUT: _filt_patt +// ------------------------------------ ------------------ --------------- +// "*.{ma,mb}" "*.{ma,mb} Files" "*.{ma,mb}" +// "*.[abc]" "*.[abc] Files" "*.[abc]" +// "*.txt" "*.txt Files" "*.c" +// "C Files\t*.[ch]" "C Files" "*.[ch]" +// "C Files\t*.[ch]\nText Files\t*.cxx" "C Files" "*.[ch]" +// +// Parsing Mode: +// IN:"C Files\t*.{cxx,h}" +// ||||||| ||||||||| +// mode: nnnnnnn wwwwwwwww +// \_____/ \_______/ +// Name Wildcard +// +void FNFC_CLASS::parse_filter(const char *in) { + clear_filters(); + if ( ! in ) return; + int has_name = strchr(in, '\t') ? 1 : 0; + + char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard + char wildcard[1024] = ""; // parsed wildcard + char name[1024] = ""; + + // Parse filter user specified + for ( ; 1; in++ ) { + + //// DEBUG + //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n", + //// *in, mode, name, wildcard); + + switch (*in) { + // FINISHED PARSING NAME? + case '\t': + if ( mode != 'n' ) goto regchar; + mode = 'w'; + break; + + // ESCAPE NEXT CHAR + case '\\': + ++in; + goto regchar; + + // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? + case '\r': + case '\n': + case '\0': + // TITLE + // If user didn't specify a name, make one + // + if ( name[0] == '\0' ) { + sprintf(name, "%.*s Files", (int)sizeof(name)-10, wildcard); + } + // APPEND NEW FILTER TO LIST + if ( wildcard[0] ) { + // Add to filtername list + // Tab delimit if more than one. We later break + // tab delimited string into CFArray with + // CFStringCreateArrayBySeparatingStrings() + // + if ( _filt_total ) { + _filt_names = strapp(_filt_names, "\t"); + } + _filt_names = strapp(_filt_names, name); + + // Add filter to the pattern array + _filt_patt[_filt_total++] = strnew(wildcard); + } + // RESET + wildcard[0] = name[0] = '\0'; + mode = strchr(in, '\t') ? 'n' : 'w'; + // DONE? + if ( *in == '\0' ) return; // done + else continue; // not done yet, more filters + + // Parse all other chars + default: // handle all non-special chars + regchar: // handle regular char + switch ( mode ) { + case 'n': chrcat(name, *in); continue; + case 'w': chrcat(wildcard, *in); continue; + } + break; + } + } + //NOTREACHED +} + +// STATIC: FILTER CALLBACK +Boolean FNFC_CLASS::filter_proc_cb(AEDesc *theItem, + void *info, + void *callBackUD, + NavFilterModes filterMode) { + return((FNFC_CLASS*)callBackUD)->filter_proc_cb2( + theItem, info, callBackUD, filterMode); +} + +// FILTER CALLBACK +// Return true if match, +// false if no match. +// +Boolean FNFC_CLASS::filter_proc_cb2(AEDesc *theItem, + void *info, + void *callBackUD, + NavFilterModes filterMode) { + // All files chosen or no filters + if ( _filt_value == _filt_total ) return(true); + + FSSpec fsspec; + char pathname[4096]; + + // On fail, filter should return true by default + if ( AEDescToFSSpec(theItem, &fsspec) != noErr ) { + return(true); + } + FSSpecToPath(fsspec, pathname, sizeof(pathname)-1); + + if ( fl_filename_isdir(pathname) ) return(true); + if ( fl_filename_match(pathname, _filt_patt[_filt_value]) ) return(true); + else return(false); +} + +// SET PRESET FILE +// Value can be NULL for none. +// +void FNFC_CLASS::preset_file(const char* val) { + _preset_file = strfree(_preset_file); + _preset_file = strnew(val); +} + +// PRESET FILE +// Returned value can be NULL if none set. +// +const char* FNFC_CLASS::preset_file() { + return(_preset_file); +} + diff --git a/contrib/NativeFileChooser/Fl_Native_File_Chooser_WIN32.cxx b/contrib/NativeFileChooser/Fl_Native_File_Chooser_WIN32.cxx new file mode 100644 index 0000000000000000000000000000000000000000..73f0092d862e762e287a1d2453d6909eaa2afaa7 --- /dev/null +++ b/contrib/NativeFileChooser/Fl_Native_File_Chooser_WIN32.cxx @@ -0,0 +1,801 @@ +// +// Fl_Native_File_Chooser_WIN32.cxx -- FLTK native OS file chooser widget +// +// Copyright 2004 by Greg Ercolano. +// API changes + filter improvements by Nathan Vander Wilt 2005 +// FLTK2/WIN32 port by Greg Ercolano +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please keep code 80 column compliant. +// +// 10 20 30 40 50 60 70 +// | | | | | | | +// 4567890123456789012345678901234567890123456789012345678901234567890123456789 +// + +// Any application to multi-folder implementation: +// http://www.codeproject.com/dialog/selectfolder.asp +// + +#include <stdio.h> // debugging +#include "common.cxx" // strnew/strfree/strapp/chrcat + +#ifdef FLTK1 +// +// FLTK1 +// +#include <FL/Fl_Native_File_Chooser.H> +#define FNFC_CLASS Fl_Native_File_Chooser +#define FNFC_CTOR Fl_Native_File_Chooser +#else +// +// FLTK2 +// +#include <fltk/NativeFileChooser.h> +#include <fltk/run.h> +#define FNFC_CTOR NativeFileChooser +#define FNFC_CLASS fltk::FNFC_CTOR +#endif + +#define LCURLY_CHR '{' +#define RCURLY_CHR '}' +#define LBRACKET_CHR '[' +#define RBRACKET_CHR ']' +#define MAXFILTERS 80 + +// STATIC: PRINT WINDOWS 'DOUBLE NULL' STRING (DEBUG) +static void dnullprint(char *wp) { + if ( ! wp ) return; + for ( int t=0; true; t++ ) { + if ( wp[t] == '\0' && wp[t+1] == '\0' ) { + printf("\\0\\0"); + fflush(stdout); + return; + } else if ( wp[t] == '\0' ) { + printf("\\0"); + } else { + printf("%c",wp[t]); + } + } +} + +// RETURN LENGTH OF DOUBLENULL STRING +// Includes single nulls in count, excludes trailing doublenull. +// +// 1234 567 +// |||/\||| +// IN: "one\0two\0\0" +// OUT: 7 +// +static int dnulllen(const char *wp) { + int len = 0; + while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) + { ++wp; ++len; } + return(len); +} + +// STATIC: Append a string to another, leaving terminated with DOUBLE NULL. +// Automatically handles extending length of string. +// wp can be NULL (a new wp will be allocated and initialized). +// string must be NULL terminated. +// The pointer wp may be modified on return. +// +static void dnullcat(char*&wp, const char *string, int n = -1 ) { + //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n"); + int inlen = ( n < 0 ) ? strlen(string) : n; + if ( ! wp ) { + wp = new char[inlen + 4]; + *(wp+0) = '\0'; + *(wp+1) = '\0'; + } else { + int wplen = dnulllen(wp); + // Make copy of wp into larger buffer + char *tmp = new char[wplen + inlen + 4]; + memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull + delete [] wp; // delete old wp + wp = tmp; // use new copy + //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen); + } + + // Find end of double null string + // *wp2 is left pointing at second null. + // + char *wp2 = wp; + if ( *(wp2+0) != '\0' && *(wp2+1) != '\0' ) { + for ( ; 1; wp2++ ) + if ( *(wp2+0) == '\0' && *(wp2+1) == '\0' ) + { wp2++; break; } + } + + if ( n == -1 ) n = strlen(string); + strncpy(wp2, string, n); + + // Leave string double-null terminated + *(wp2+n+0) = '\0'; + *(wp2+n+1) = '\0'; + //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n"); +} + +// CTOR +FNFC_CLASS::FNFC_CTOR(int val) { + _btype = val; + _options = NO_OPTIONS; + memset((void*)&_ofn, 0, sizeof(OPENFILENAME)); + _ofn.lStructSize = sizeof(OPENFILENAME); + _ofn.hwndOwner = NULL; + memset((void*)&_binf, 0, sizeof(BROWSEINFO)); + _pathnames = NULL; + _tpathnames = 0; + _directory = NULL; + _title = NULL; + _filter = NULL; + _parsedfilt = NULL; + _nfilters = 0; + _preset_file = NULL; + _errmsg = NULL; +} + +// DTOR +FNFC_CLASS::~FNFC_CTOR() { + //_pathnames // managed by clear_pathnames() + //_tpathnames // managed by clear_pathnames() + _directory = strfree(_directory); + _title = strfree(_title); + _filter = strfree(_filter); + //_parsedfilt // managed by clear_filters() + //_nfilters // managed by clear_filters() + _preset_file = strfree(_preset_file); + _errmsg = strfree(_errmsg); + clear_filters(); + clear_pathnames(); + ClearOFN(); + ClearBINF(); +} + +// SET TYPE OF BROWSER +void FNFC_CLASS::type(int val) { + _btype = val; +} + +// GET TYPE OF BROWSER +int FNFC_CLASS::type() const { + return( _btype ); +} + +// SET OPTIONS +void FNFC_CLASS::options(int val) { + _options = val; +} + +// GET OPTIONS +int FNFC_CLASS::options() const { + return(_options); +} + +// PRIVATE: SET ERROR MESSAGE +void FNFC_CLASS::errmsg(const char *val) { + _errmsg = strfree(_errmsg); + _errmsg = strnew(val); +} + +// FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS +void FNFC_CLASS::clear_pathnames() { + if ( _pathnames ) { + while ( --_tpathnames >= 0 ) { + _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); + } + delete [] _pathnames; + _pathnames = NULL; + } + _tpathnames = 0; +} + +// SET A SINGLE PATHNAME +void FNFC_CLASS::set_single_pathname(const char *s) { + clear_pathnames(); + _pathnames = new char*[1]; + _pathnames[0] = strnew(s); + _tpathnames = 1; +} + +// ADD PATHNAME TO EXISTING ARRAY +void FNFC_CLASS::add_pathname(const char *s) { + if ( ! _pathnames ) { + // Create first element in array + ++_tpathnames; + _pathnames = new char*[_tpathnames]; + } else { + // Grow array by 1 + char **tmp = new char*[_tpathnames+1]; // create new buffer + memcpy((void*)tmp, (void*)_pathnames, + sizeof(char*)*_tpathnames); // copy old + delete [] _pathnames; // delete old + _pathnames = tmp; // use new + ++_tpathnames; + } + _pathnames[_tpathnames-1] = strnew(s); +} + +// FREE A PIDL (Pointer to IDentity List) +void FNFC_CLASS::FreePIDL(ITEMIDLIST *pidl) { + IMalloc *imalloc = NULL; + if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) + { imalloc->Free(pidl); imalloc->Release(); imalloc = NULL; } +} + +// CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS +void FNFC_CLASS::ClearOFN() { + // Free any previously allocated lpstrFile before zeroing out _ofn + if ( _ofn.lpstrFile ) { + _ofn.lpstrFile = strfree((char*)_ofn.lpstrFile); + } + if ( _ofn.lpstrInitialDir ) { + _ofn.lpstrInitialDir = (LPCSTR)strfree((char*)_ofn.lpstrInitialDir); + } + _ofn.lpstrFilter = NULL; // (deleted elsewhere) + int temp = _ofn.nFilterIndex; // keep the filter_value + memset((void*)&_ofn, 0, sizeof(_ofn)); + _ofn.lStructSize = sizeof(OPENFILENAME); + _ofn.nFilterIndex = temp; +} + +// CLEAR MICROSOFT BINF (BROWSER INFO) CLASS +void FNFC_CLASS::ClearBINF() { + if ( _binf.pidlRoot ) { + FreePIDL((ITEMIDLIST*)_binf.pidlRoot); + _binf.pidlRoot = NULL; + } + memset((void*)&_binf, 0, sizeof(_binf)); +} + +// CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES +void FNFC_CLASS::Win2Unix(char *s) { + for ( ; *s; s++ ) + if ( *s == '\\' ) *s = '/'; +} + +// CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES +void FNFC_CLASS::Unix2Win(char *s) { + for ( ; *s; s++ ) + if ( *s == '/' ) *s = '\\'; +} + +// SHOW FILE BROWSER +int FNFC_CLASS::showfile() { + ClearOFN(); + clear_pathnames(); + size_t fsize = 2048; + _ofn.Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes + _ofn.Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag + // USE NEW BROWSER + _ofn.Flags |= OFN_EXPLORER; // use newer explorer windows + _ofn.Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?) + + // XXX: The docs for OFN_NOCHANGEDIR says the flag is 'ineffective' on XP/2K/NT! + // But let's set it anyway.. + // + _ofn.Flags |= OFN_NOCHANGEDIR; // prevent dialog for messing up the cwd + + switch ( _btype ) { + case BROWSE_DIRECTORY: + case BROWSE_MULTI_DIRECTORY: + case BROWSE_SAVE_DIRECTORY: + abort(); // never happens: handled by showdir() + case BROWSE_FILE: + fsize = 65536; // XXX: there must be a better way + break; + case BROWSE_MULTI_FILE: + _ofn.Flags |= OFN_ALLOWMULTISELECT; + fsize = 65536; // XXX: there must be a better way + break; + case BROWSE_SAVE_FILE: + if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { + _ofn.Flags |= OFN_OVERWRITEPROMPT; + } + break; + } + // SPACE FOR RETURNED FILENAME + _ofn.lpstrFile = new char[fsize]; + _ofn.nMaxFile = fsize-1; + _ofn.lpstrFile[0] = '\0'; + _ofn.lpstrFile[1] = '\0'; // dnull + // PARENT WINDOW + _ofn.hwndOwner = GetForegroundWindow(); + // DIALOG TITLE + _ofn.lpstrTitle = _title ? _title : NULL; + // FILTER + _ofn.lpstrFilter = _parsedfilt ? _parsedfilt : NULL; + // PRESET FILE + // If set, supercedes _directory. See KB Q86920 for details + // + if ( _preset_file ) { + size_t len = strlen(_preset_file); + if ( len >= _ofn.nMaxFile ) { + char msg[80]; + sprintf(msg, "preset_file() filename is too long: %ld is >=%ld", + (long)len, (long)fsize); + return(-1); + } + strncpy(_ofn.lpstrFile, _preset_file, _ofn.nMaxFile); + Unix2Win(_ofn.lpstrFile); + _ofn.lpstrFile[len+0] = 0; // multiselect needs dnull + _ofn.lpstrFile[len+1] = 0; + } + if ( _directory ) { + // PRESET DIR + // XXX: See KB Q86920 for doc bug: + // http://support.microsoft.com/default.aspx?scid=kb;en-us;86920 + // + _ofn.lpstrInitialDir = strnew(_directory); + Unix2Win((char*)_ofn.lpstrInitialDir); + } + // SAVE THE CURRENT DIRECTORY + // XXX: Save the cwd because GetOpenFileName() is probably going to + // change it, in spite of the OFN_NOCHANGEDIR flag, due to its docs + // saying the flag is 'ineffective'. %^( + // + char oldcwd[MAX_PATH]; + GetCurrentDirectory(MAX_PATH, oldcwd); + oldcwd[MAX_PATH-1] = '\0'; + // OPEN THE DIALOG WINDOW + int err; + if ( _btype == BROWSE_SAVE_FILE ) { + err = GetSaveFileName(&_ofn); + } else { + err = GetOpenFileName(&_ofn); + } + if ( err == 0 ) { + // EXTENDED ERROR CHECK + int err = CommDlgExtendedError(); + // CANCEL? + if ( err == 0 ) + return(1); // user hit 'cancel' + // AN ERROR OCCURRED + char msg[80]; + sprintf(msg, "CommDlgExtendedError() code=%d", err); + errmsg(msg); + // XXX: RESTORE CWD + if ( oldcwd[0] ) SetCurrentDirectory(oldcwd); + return(-1); + } + // XXX: RESTORE CWD + if ( oldcwd[0] ) { + SetCurrentDirectory(oldcwd); + } + // PREPARE PATHNAMES FOR RETURN + switch ( _btype ) { + case BROWSE_FILE: + case BROWSE_SAVE_FILE: + set_single_pathname(_ofn.lpstrFile); + Win2Unix(_pathnames[_tpathnames-1]); + break; + case BROWSE_MULTI_FILE: { + // EXTRACT MULTIPLE FILENAMES + const char *dirname = _ofn.lpstrFile; + int dirlen = strlen(dirname); + if ( dirlen > 0 ) { + // WALK STRING SEARCHING FOR 'DOUBLE-NULL' + // eg. "/dir/name\0foo1\0foo2\0foo3\0\0" + // + char pathname[2048]; + for ( const char *s = _ofn.lpstrFile + dirlen + 1; + *s; s+= (strlen(s)+1)) { + strcpy(pathname, dirname); + strcat(pathname, "\\"); + strcat(pathname, s); + add_pathname(pathname); + Win2Unix(_pathnames[_tpathnames-1]); + } + } + // XXX + // Work around problem where pasted forward-slash pathname + // into the file browser causes new "Explorer" interface + // not to grok forward slashes, passing back as a 'filename'..! + // + if ( _tpathnames == 0 ) { + add_pathname(dirname); + Win2Unix(_pathnames[_tpathnames-1]); + } + break; + } + case BROWSE_DIRECTORY: + case BROWSE_MULTI_DIRECTORY: + case BROWSE_SAVE_DIRECTORY: + abort(); // never happens: handled by showdir() + } + return(0); +} + +// Used by SHBrowseForFolder(), sets initial selected dir. +// Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes +// Subject: How to specify to select an initial folder .." +// +int CALLBACK FNFC_CLASS::Dir_CB(HWND win, UINT msg, + LPARAM param, LPARAM data) { + switch (msg) { + case BFFM_INITIALIZED: + if (data) ::SendMessage(win, BFFM_SETSELECTION, TRUE, data); + break; + case BFFM_SELCHANGED: + TCHAR path[MAX_PATH]; + if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) { + ::SendMessage(win, BFFM_ENABLEOK, 0, 1); + } else { + //disable ok button if not a path + ::SendMessage(win, BFFM_ENABLEOK, 0, 0); + } + break; + case BFFM_VALIDATEFAILED: + // we could pop up an annoying message here. + // also needs set ulFlags |= BIF_VALIDATE + break; + default: + break; + } + return(0); +} + +// SHOW DIRECTORY BROWSER +int FNFC_CLASS::showdir() { + OleInitialize(NULL); // init needed by BIF_USENEWUI + ClearBINF(); + clear_pathnames(); + // PARENT WINDOW + _binf.hwndOwner = GetForegroundWindow(); + // DIALOG TITLE + _binf.lpszTitle = _title ? _title : NULL; + // FLAGS + _binf.ulFlags = 0; // initialize + + // TBD: make sure matches to runtime system, if need be. + //( what if _WIN32_IE doesn't match system? does the program not run? ) + // TBD: match all 3 types of directories + +#if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 + if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON; + _binf.ulFlags |= BIF_USENEWUI | BIF_SHAREABLE | BIF_RETURNONLYFSDIRS; +#elif defined(BIF_USENEWUI) // Version 5.0 + if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_EDITBOX; + else if ( _btype == BROWSE_SAVE_DIRECTORY ) _binf.ulFlags |= BIF_USENEWUI; + _binf.ulFlags |= BIF_SHAREABLE | BIF_RETURNONLYFSDIRS; +#elif defined(BIF_EDITBOX) // Version 4.71 + _binf.ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX; +#else // Version Old + _binf.ulFlags |= BIF_RETURNONLYFSDIRS; +#endif + + // BUFFER + char displayname[MAX_PATH]; + _binf.pszDisplayName = displayname; + // PRESET DIR + char presetname[MAX_PATH]; + if ( _directory ) { + strcpy(presetname, _directory); + Unix2Win(presetname); + _binf.lParam = (LPARAM)presetname; + } + else _binf.lParam = 0; + _binf.lpfn = Dir_CB; + // OPEN BROWSER + ITEMIDLIST *pidl = SHBrowseForFolder(&_binf); + // CANCEL? + if ( pidl == NULL ) return(1); + + // GET THE PATHNAME(S) THE USER SELECTED + // TBD: expand NetHood shortcuts from this PIDL?? + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp + + TCHAR path[MAX_PATH]; + if ( SHGetPathFromIDList(pidl, path) ) { + Win2Unix(path); + add_pathname(path); + } + FreePIDL(pidl); + if ( !strlen(path) ) return(1); // don't return empty pathnames + return(0); +} + +// RETURNS: +// 0 - user picked a file +// 1 - user cancelled +// -1 - failed; errmsg() has reason +// +int FNFC_CLASS::show() { + if ( _btype == BROWSE_DIRECTORY || + _btype == BROWSE_MULTI_DIRECTORY || + _btype == BROWSE_SAVE_DIRECTORY ) { + return(showdir()); + } else { + return(showfile()); + } +} + +// RETURN ERROR MESSAGE +const char *FNFC_CLASS::errmsg() const { + return(_errmsg ? _errmsg : "No error"); +} + +// GET FILENAME +const char* FNFC_CLASS::filename() const { + if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); + return(""); +} + +// GET FILENAME FROM LIST OF FILENAMES +const char* FNFC_CLASS::filename(int i) const { + if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); + return(""); +} + +// GET TOTAL FILENAMES CHOSEN +int FNFC_CLASS::count() const { + return(_tpathnames); +} + +// PRESET PATHNAME +// Can be NULL if no preset is desired. +// +void FNFC_CLASS::directory(const char *val) { + _directory = strfree(_directory); + _directory = strnew(val); +} + +// GET PRESET PATHNAME +// Can return NULL if none set. +// +const char *FNFC_CLASS::directory() const { + return(_directory); +} + +// SET TITLE +// Can be NULL if no title desired. +// +void FNFC_CLASS::title(const char *val) { + _title = strfree(_title); + _title = strnew(val); +} + +// GET TITLE +// Can return NULL if none set. +// +const char *FNFC_CLASS::title() const { + return(_title); +} + +// SET FILTER +// Can be NULL if no filter needed +// +void FNFC_CLASS::filter(const char *val) { + _filter = strfree(_filter); + clear_filters(); + if ( val ) { + _filter = strnew(val); + parse_filter(_filter); + } + add_filter("All Files", "*.*"); // always include 'all files' option + +#ifdef DEBUG + nullprint(_parsedfilt); +#endif /*DEBUG*/ +} + +// GET FILTER +// Can return NULL if none set. +// +const char *FNFC_CLASS::filter() const { + return(_filter); +} + +// CLEAR FILTERS +void FNFC_CLASS::clear_filters() { + _nfilters = 0; + _parsedfilt = strfree(_parsedfilt); +} + +// ADD A FILTER +void FNFC_CLASS::add_filter( + const char *name_in, // name of filter (optional: can be null) + const char *winfilter // windows style filter (eg. "*.cxx;*.h") + ) { + // No name? Make one.. + char name[1024]; + if ( !name_in || name_in[0] == '\0' ) { + sprintf(name, "%.*s Files", sizeof(name)-10, winfilter); + } else { + sprintf(name, "%.*s", sizeof(name)-10, name_in); + } + dnullcat(_parsedfilt, name); + dnullcat(_parsedfilt, winfilter); + _nfilters++; + //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter); +} + +// CONVERT FLTK STYLE PATTERN MATCHES TO WINDOWS 'DOUBLENULL' PATTERN +// Handles: +// IN OUT +// ----------- ----------------------------- +// *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0" +// *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0" +// *.txt "*.txt Files\0*.txt\0\0" +// C Files\t*.[ch] "C Files\0*.c;*.h\0\0" +// +// Example: +// IN: "*.{ma,mb}" +// OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0" +// --------------- --------- --------- --- +// | | | | +// Title Wildcards Title Wildcards +// +// Parsing Mode: +// IN:"C Files\t*.{cxx,h}" +// ||||||| ||||||||| +// mode: nnnnnnn ww{{{{{{{ +// \_____/ \_______/ +// Name Wildcard +// +void FNFC_CLASS::parse_filter(const char *in) { + clear_filters(); + if ( ! in ) return; + + int has_name = strchr(in, '\t') ? 1 : 0; + + char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard + int nwildcards = 0; + char wildcards[MAXFILTERS][1024]; // parsed wildcards (can be several) + char wildprefix[512] = ""; + char name[512] = ""; + + // Init + int t; + for ( t=0; t<MAXFILTERS; t++ ) { + wildcards[t][0] = '\0'; + } + + // Parse + for ( ; 1; in++ ) { + + //// DEBUG + //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n", + //// *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]); + + switch (*in) { + case ',': + case '|': + if ( mode == LCURLY_CHR ) { + // create new wildcard, copy in prefix + strcat(wildcards[nwildcards++], wildprefix); + continue; + } else { + goto regchar; + } + continue; + + // FINISHED PARSING A NAME? + case '\t': + if ( mode != 'n' ) goto regchar; + // finish parsing name? switch to wildcard mode + mode = 'w'; + break; + + // ESCAPE NEXT CHAR + case '\\': + ++in; + goto regchar; + + // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? + case '\r': + case '\n': + case '\0': + { + if ( mode == 'w' ) { // finished parsing wildcard? + if ( nwildcards == 0 ) { + strcpy(wildcards[nwildcards++], wildprefix); + } + // Append wildcards in Microsoft's "*.one;*.two" format + char comp[4096] = ""; + for ( t=0; t<nwildcards; t++ ) { + if ( t != 0 ) strcat(comp, ";"); + strcat(comp, wildcards[t]); + } + // Add if not empty + if ( comp[0] ) { + add_filter(name, comp); + } + } + // RESET + for ( t=0; t<MAXFILTERS; t++ ) { + wildcards[t][0] = '\0'; + } + nwildcards = 0; + wildprefix[0] = name[0] = '\0'; + mode = strchr(in,'\t') ? 'n' : 'w'; + // DONE? + if ( *in == '\0' ) return; // done + continue; // not done yet, more filters + } + + // STARTING A WILDCARD? + case LBRACKET_CHR: + case LCURLY_CHR: + mode = *in; + if ( *in == LCURLY_CHR ) { + // create new wildcard + strcat(wildcards[nwildcards++], wildprefix); + } + continue; + + // ENDING A WILDCARD? + case RBRACKET_CHR: + case RCURLY_CHR: + mode = 'w'; // back to wildcard mode + continue; + + // ALL OTHER NON-SPECIAL CHARACTERS + default: + regchar: // handle regular char + switch ( mode ) { + case LBRACKET_CHR: + // create new wildcard + ++nwildcards; + // copy in prefix + strcpy(wildcards[nwildcards-1], wildprefix); + // append search char + chrcat(wildcards[nwildcards-1], *in); + continue; + + case LCURLY_CHR: + if ( nwildcards > 0 ) { + chrcat(wildcards[nwildcards-1], *in); + } + continue; + + case 'n': + chrcat(name, *in); + continue; + + case 'w': + chrcat(wildprefix, *in); + for ( t=0; t<nwildcards; t++ ) { + chrcat(wildcards[t], *in); + } + continue; + } + break; + } + } +} + +// SET 'CURRENTLY SELECTED FILTER' +void FNFC_CLASS::filter_value(int i) { + _ofn.nFilterIndex = i + 1; +} + +// RETURN VALUE OF 'CURRENTLY SELECTED FILTER' +int FNFC_CLASS::filter_value() const { + return(_ofn.nFilterIndex ? _ofn.nFilterIndex-1 : _nfilters+1); +} + +// PRESET FILENAME FOR 'SAVE AS' CHOOSER +void FNFC_CLASS::preset_file(const char* val) { + _preset_file = strfree(_preset_file); + _preset_file = strnew(val); +} + +// GET PRESET FILENAME FOR 'SAVE AS' CHOOSER +const char* FNFC_CLASS::preset_file() const { + return(_preset_file); +} diff --git a/contrib/NativeFileChooser/Makefile b/contrib/NativeFileChooser/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..e94f12909b868b63bd0efb83d6ec391a4f2eb577 --- /dev/null +++ b/contrib/NativeFileChooser/Makefile @@ -0,0 +1,51 @@ +# $Id: Makefile,v 1.1 2008-01-07 22:59:29 geuzaine Exp $ +# +# Copyright (C) 1997-2007 C. Geuzaine, J.-F. Remacle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA. +# +# Please report all bugs and problems to <gmsh@geuz.org>. + +include ../../variables + +LIB = ../../lib/libGmshNativeFileChooser.a +INCLUDE = -I. +CFLAGS = ${OPTIM} ${FLAGS} ${INCLUDE} -DFLTK1 + +SRC = Fl_Native_File_Chooser.cxx +OBJ = ${SRC:.cxx=.o} + +.SUFFIXES: .o .cxx + +${LIB}: ${OBJ} + ${AR} ${LIB} ${OBJ} + ${RANLIB} ${LIB} + +.cxx.o: + ${CXX} ${CFLAGS} -c $< + +clean: + rm -f *.o + +depend: + (sed '/^# DO NOT DELETE THIS LINE/q' Makefile && \ + ${CXX} -MM ${CFLAGS} ${SRC} \ + ) >Makefile.new + cp Makefile Makefile.bak + cp Makefile.new Makefile + rm -f Makefile.new + +# DO NOT DELETE THIS LINE diff --git a/contrib/NativeFileChooser/README.txt b/contrib/NativeFileChooser/README.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc9da1a370bfa27b116c13028283afd041fa4450 --- /dev/null +++ b/contrib/NativeFileChooser/README.txt @@ -0,0 +1,119 @@ +Fl_Native_File_Chooser -- Access platform's native file choosers in FLTK +------------------------------------------------------------------------ + + +WHAT IS "Fl_Native_File_Chooser"? +================================= + + Fl_Native_File_Chooser is a 'widget' wrapper to access the + different platform's native file choosers. On platforms that + don't have a 'native' file chooser (linux), we use FLTK's own. + + The purpose of this library is to make a consistent interface + for accessing the different native file choosers. + + Tested under Linux, Mac OSX and Windows with fltk 1.1.6. + Tested under Linux with fltk 2.x. + + +LICENSING +========= + + Fl_Native_File_Chooser comes with complete free source code. + Fl_Native_File_Chooser is available under the terms of the + GNU Library General Public License. See COPYING for more info. + + Yes, it can be used in commercial software! Free! Imagine that. + + +BUILD INSTRUCTIONS +================== + + 1. Which version of FLTK? + + Fl_Native_File_Chooser now supports fltk1 and fltk2 as of 0.83e. + + Edit the Makefile and uncomment/modify the FLTKCONFIG and FLTK2CONFIG + variables as needed. + + If both variables are uncommented, both versions of + Fl_Native_File_Chooser will be built. + + For instance, if you have both fltk1 and fltk2 installed on your + system, the top of your Makefile settings might look like: + +FLTKCONFIG=/usr/local/src/fltk-1.1.x-svn/fltk-config +FLTK2CONFIG=/usr/local/src/fltk-2.0-svn/fltk2-config + + Then you can build both with just: + + make + + These test programs are created, depending on if you have + configured the above for fltk1, fltk2, or both: + + ./test-browser -- fltk1 exerciser demo + ./simple-app -- fltk1 simple app + + ./test-browser-fltk2 -- fltk2 exerciser demo + ./simple-app-fltk2 -- fltk2 simple app + + Originally Fl_Native_File_Chooser was designed for FLTK1, + so there may be some left over FLTK1 specific references + in the docs. Please report these as bugs (see below) + + +PLATFORM SPECIFIC NOTES +======================= + + For linux and osx the default compilers are used. + + For Windows, tested with VS Express 8 + make.bat / Makefile.MICROSOFT. + Ian confirmed it compiled OK under Windows with mingw using the default + unix Makefile. + + +WHERE'S THE DOCUMENTATION? +========================== + + ./documentation/index.html + + +HOW DO I LINK Fl_Native_File_Chooser INTO MY OWN APPLICATION? +============================================================= + + ./documentation/how-to-use.html + + +FILE LAYOUT +=========== + + ./Makefile -- main Makefile for unix builds + ./Makefile.MICROSOFT -- main Makefile for native Microsoft builds + + ./documentation/index.html -- public documentation for fltk1 + fltk2 + + ./FL \__ fltk1 include files and lib + ./Fl_Native_File_Chooser.o / (for your app to include and link) + + ./fltk \__ fltk2 include files and lib + ./NativeFileChooser.o / (for your app to include and link) + + ./reference -- project's reference docs (internal use) + + *_FLTK.{cxx,H,h} -- Platforms that don't have native choosers + *_MAC.{cxx,H,h} -- Mac platform specific source code + *_WIN32.{cxx,H,h} -- Windows platform specific source code + + +RELEASE NOTES/VERSION INFORMATION +================================= + + See ./CHANGES. + + +BUGS? FEATURE REQUESTS? +======================= + + Send bugs and RFE's to erco at seriss dot com + diff --git a/contrib/NativeFileChooser/TODO b/contrib/NativeFileChooser/TODO new file mode 100644 index 0000000000000000000000000000000000000000..93558f08603e79f46047295227c5b27a1ef01b50 --- /dev/null +++ b/contrib/NativeFileChooser/TODO @@ -0,0 +1,190 @@ +> Determine why Mac and OSX don't open browser with preset file selected. + WINDOWS: tried to make this work, but it just doesn't support it! + Even calling GetOpenFileName(&_ofn); twice in a row. + +> 0.84: Ian MacArthur indicated mingw warns about strapp and strdump + being "defined but not used" cause it's a static. Should + probably make a lib, disable this warning, or reference + the calls in an unused subroutine. + +> 0.83d: Namespace pollution: Yuri wants Carbon.h/win32.h + to not be in the public .H files. Can probably make an 'underbar' + version of the class to isolate them. See erco's post on + fltk.general 06/24/07. + +> 0.83d: MAC / Save File: + If: + 1) /tmp/foo.xxx exists + 2) Directory: set to /tmp + 3) Preset File set to foo.xxx + ..when browser opens, foo.xxx is GRAYED OUT, even though filter + appears to be on. Can still click on grayed out items to pick them, + and Save As: name changes. Weird! + Looks OK when 'Single File' is selected. + +(?) > 0.83d: WINDOWS/FILE BROWSE: +(?) If simple-app's preset filename doesn't end in a backslash, +(?) browser shows contents of parent dir, not dir itself. +(?) Maybe append a slash if stat() shows preset filename is a DIR? + +-- 0.82c -- + + +(?) > 0.82c: Windows "Save As" is not setting correct directory in browser + + +(?) > 0.82: Andreas reports problem with Mac chooser not showing thumbnails +(?) that live on remote drives even though the Finder does. +(?) Files located on the local file system show up OK. +(?) (See Reference #1 below for Apple's SimpleText.c excerpts?) +(?) Also, see Andreas' email 12/05/2005 (Reference #2 below) + +(?) > 0.82: Presetting value() under linux causes chooser to open with file +(?) highlighted in the chooser, but not in windows or mac. + +(?) > 0.82: Fl_Native_File_Chooser: WINDOWS doesn't handle returning UTF8 from +(?) native windows file chooser.. comes back as garbage, possibly as non-UTF8? +(?) (Found problem on Japanese localized machine by browsing to a dir +(?) with Japanese chars. Shows OK in windows browser, but when returned +(?) to Fl_Input, comes up as garbage, even though the Fl_Input is capable +(?) of showing text correctly if japanese text pasted as text into the +(?) Fl_Input from Windows URL input) + +*** DONE *** +(DONE) > 0.82c: "Save As" + "Show Confirm Dialog" is not showing the confirm dialog..! +(DONE) TESTED ON LINUX AND OSX TO WORK OK. + +(DONE) > 0.83c: Shane Hill on 02/07/06 reports that under Windows, doing regular +(DONE) file browsing results in the current working dir being changed. +(DONE) Luckily there appears to be a WIN32 flag for this, but the docs +(DONE) say it's ineffective..!? +(DONE) OFN_NOCHANGEDIR +(DONE) Restores the current directory to its original value if the user changed +(DONE) the directory while searching for files. +(DONE) Windows NT 4.0/2000/XP: This flag is ineffective for GetOpenFileName. +(DONE) ^^^^^^^^^^^^^^^^^^^^^^^^ + +(DONE) > 0.82: Alessandro: uses very long wildcard strings.. +(DONE) Replace fixed arrays (char wildcard[80]) with new strapp() stuff. + +------------------------------------------------------------------------ + Reference #1 +------------------------------------------------------------------------ + + // REFERENCE FROM SimpleText.c for erco.. + + short numTypes; + OSType typeList[20]; + OSType fileType = '\?\?\?\?'; + NavDialogRef navDialog; + + DetermineWindowTypeOrOpen( nil, fileType, &typeList[0], &numTypes, nil ); + + // Open as many documents as the user wishes through Appleevents + return OpenFileDialog( 'ttxt', numTypes, typeList, &navDialog ); + + OSType typeList[20]; + OSType docList[20]; + + pFileTypes[*numTypes] = 'MooV'; + pDocumentTypes[*numTypes] = kMovieWindow; + (*numTypes)++; + + +OSStatus OpenFileDialog( + OSType applicationSignature, + short numTypes, + OSType typeList[], + NavDialogRef *outDialog ) +{ + OSStatus theErr = noErr; + if ( gOpenFileDialog == NULL ) + { + NavDialogCreationOptions dialogOptions; + NavTypeListHandle openList = NULL; + + NavGetDefaultDialogCreationOptions( &dialogOptions ); + + dialogOptions.modality = kWindowModalityNone; + dialogOptions.clientName = CFStringCreateWithPascalString( + NULL, LMGetCurApName(), GetApplicationTextEncoding()); + + openList = (NavTypeListHandle)NewOpenHandle( applicationSignature, numTypes, typeList ); + + theErr = NavCreateGetFileDialog( &dialogOptions, openList, + GetPrivateEventUPP(), + NULL, NULL, NULL, &gOpenFileDialog ); + +------------------------------------------------------------------------ + Reference #2 +------------------------------------------------------------------------ + +Andreas Schömann wrote: +> Greg Ercolano wrote: +> +>> Andreas Schömann wrote: +>> +>>> I've found a problem: when loading images (jpg) via network, no thumbnail is shown in the file chooser. Actual loading works. The network device is NTFS formatted. I thought it might be an access rights problem, but with 'Finder' it works fine... + + BTW, Andreas is specifically referring to the MAC file browser; + when you have it in 'column' mode, when you highlight eg. a jpg + image, the preview that normally displays off to the right + doesn't show up. + + I looked at the SimpleText.c code that comes with the OSX + developer toolkit, ie: + /Developer//Examples/Carbon/SimpleText/* + + ..and it looks like they use a few options to NavCreateGetFileDialog() + that I don't use, namely arg2 and arg3 to set a list of files + and a callback. + + I have to admit I don't fully understand the Mac stuff when it + comes to this sort of thing. Apparently there's a global database + somewhere of 4 character file types that dates back to the 1980's, + and it appears one needs to know these names and hard code them + into your app (as is done in SimpleText.cxx). + + I think I'll leave this unfinished, since it's not really a show + stopper, and I'm not sure how to make it work correctly. + +> 4) Here on my machine Fl_Native_File_Chooser displays a preview for _local_ images (e.g. .jpeg, .tiff and .png) + + Really? + + I actually couldn't get that to work on my 10.4.3 box. + + When I single click on a jpeg, png, or other image format, + it shows a generic image, but the correct text info about + the image's file type. + + What does work is if I click on .c or .cxx files it shows + correctly, and if I click on a .rm file (Real Media), + it shows Real's icon logo.. (shrug) + + In the Finder, when you click on images, it shows a thumbnail + of the actual image. + +> Hmmmh, somewhat confusing results... +> Especially the different behaviour of Finder and File Chooser is a mystery to me. Why did Apple do that? Maybe this has changed with Tiger? +> 1) and 2) tell me that one can disable file preview with 'Navigation Services', though probably not by intention. + + It might be I'm missing some args to NavCreateGetFileDialog() + (arg2 and 3?) Unfortunately I'm not sure what is the correct + code to make that work, if that's even the cause. + + Or maybe I need to link in some apple framework that's missing + to make that stuff work. + +> I also had a look into the documentation ( http://developer.apple.com/documentation/Carbon/Conceptual/ProvidingNavigationDialogs/index.html ) and it says that 'Navigation Services' offers to specify a 'preview callback' with 'NavCreateGetFileDialog'. This is called when the user selects a file and offers a way to preview custom file types. But Fl_Native_File_Chooser does not use this feature and in that case 'Navigation Services' inspects the file. + + Right, as described above. + However, I'd think there'd be defaults for things like jpeg/png/etc. + + I'd hate to think I have to manually provide callbacks for each + image type..! + +------------------------------------------------------------------------ + Reference #3 +------------------------------------------------------------------------ + diff --git a/contrib/NativeFileChooser/common.cxx b/contrib/NativeFileChooser/common.cxx new file mode 100644 index 0000000000000000000000000000000000000000..efde4875fd410f7d65bdcdbcc32462136ca61d6a --- /dev/null +++ b/contrib/NativeFileChooser/common.cxx @@ -0,0 +1,78 @@ +// +// common.cxx -- common string subs for Fl_Native_File_Chooser +// +// Copyright 2004 by Greg Ercolano. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please keep code 80 column compliant. +// +// 10 20 30 40 50 60 70 +// | | | | | | | +// 4567890123456789012345678901234567890123456789012345678901234567890123456789 +// + +#include <string.h> + +// COPY A STRING WITH 'new' +// Value can be NULL +// +static char *strnew(const char *val) { + if ( val == NULL ) return(NULL); + char *s = new char[strlen(val)+1]; + strcpy(s, val); + return(s); +} + +// FREE STRING CREATED WITH strnew(), NULLS OUT STRING +// Value can be NULL +// +static char *strfree(char *val) { + if ( val ) delete [] val; + return(NULL); +} + +// 'DYNAMICALLY' APPEND ONE STRING TO ANOTHER +// Returns newly allocated string, or NULL +// if s && val == NULL. +// 's' can be NULL; returns a strnew(val). +// 'val' can be NULL; s is returned unmodified. +// +// Usage: +// char *s = strnew("foo"); // s = "foo" +// s = strapp(s, "bar"); // s = "foobar" +// +static char *strapp(char *s, const char *val) { + if ( ! val ) { + return(s); // Nothing to append? return s + } + if ( ! s ) { + return(strnew(val)); // New string? return copy of val + } + char *news = new char[strlen(s)+strlen(val)+1]; + strcpy(news, s); + strcat(news, val); + delete [] s; // delete old string + return(news); // return new copy +} + +// APPEND A CHARACTER TO A STRING +// This does NOT allocate space for the new character. +// +static void chrcat(char *s, char c) { + char tmp[2] = { c, '\0' }; + strcat(s, tmp); +}