diff options
| -rw-r--r-- | FL/Fl_Native_File_Chooser.H | 47 | ||||
| -rw-r--r-- | FL/Fl_Native_File_Chooser_FLTK.H | 111 | ||||
| -rw-r--r-- | FL/Fl_Native_File_Chooser_MAC.H | 156 | ||||
| -rw-r--r-- | FL/Fl_Native_File_Chooser_WIN32.H | 106 | ||||
| -rw-r--r-- | src/Fl_Native_File_Chooser.cxx | 45 | ||||
| -rw-r--r-- | src/Fl_Native_File_Chooser_FLTK.cxx | 395 | ||||
| -rw-r--r-- | src/Fl_Native_File_Chooser_MAC.cxx | 1087 | ||||
| -rw-r--r-- | src/Fl_Native_File_Chooser_WIN32.cxx | 848 | ||||
| -rw-r--r-- | src/Fl_Native_File_Chooser_common.cxx | 83 | ||||
| -rw-r--r-- | src/Fl_cocoa.mm | 1030 | ||||
| -rw-r--r-- | src/Makefile | 6 | ||||
| -rw-r--r-- | src/makedepend | 2 | ||||
| -rw-r--r-- | src/makefile.wat | 1 | ||||
| -rw-r--r-- | test/Makefile | 4 | ||||
| -rw-r--r-- | test/makefile.wat | 3 | ||||
| -rw-r--r-- | test/native-filechooser.cxx | 83 |
16 files changed, 3479 insertions, 528 deletions
diff --git a/FL/Fl_Native_File_Chooser.H b/FL/Fl_Native_File_Chooser.H new file mode 100644 index 000000000..77b10dbd0 --- /dev/null +++ b/FL/Fl_Native_File_Chooser.H @@ -0,0 +1,47 @@ +// +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +#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/FL/Fl_Native_File_Chooser_FLTK.H b/FL/Fl_Native_File_Chooser_FLTK.H new file mode 100644 index 000000000..afc671048 --- /dev/null +++ b/FL/Fl_Native_File_Chooser_FLTK.H @@ -0,0 +1,111 @@ +// +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 2005 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. +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +#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; + + //added by MG + Fl_File_Browser *my_fileList; + Fl_Check_Button *show_hidden; + char old_dir[300]; + int prev_filtervalue; + static void show_hidden_cb(Fl_Check_Button *o, void *data); + static void remove_hidden_files(Fl_File_Browser *my_fileList); + + // 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(); + // added by MG + Fl_Choice *showChoice; +}; diff --git a/FL/Fl_Native_File_Chooser_MAC.H b/FL/Fl_Native_File_Chooser_MAC.H new file mode 100644 index 000000000..c65e8909e --- /dev/null +++ b/FL/Fl_Native_File_Chooser_MAC.H @@ -0,0 +1,156 @@ +// +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +// OSX-SPECIFIC NATIVE BROWSER +#ifdef __APPLE_CC__ +#include <Carbon/Carbon.h> +#else /*__APPLE_CC__*/ +#include <Carbon.h> +#endif /*__APPLE_CC__*/ +#include <config.h> + +#undef check // necessary for use of Fl::check() + +#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 + }; +#ifndef __APPLE_COCOA__ +protected: + NavDialogCreationOptions _opts; // file navigation options +#endif +private: + int _btype; // kind-of browser to show() + int _options; // general options +#ifdef __APPLE_COCOA__ + void *_panel; +#else + NavDialogRef _ref; // file navigation reference +#endif + 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 + +#ifndef __APPLE_COCOA__ + // 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); + }; +#endif + // Private methods + void errmsg(const char *msg); + void clear_pathnames(); + void set_single_pathname(const char *s); +#ifdef __APPLE_COCOA__ + int get_saveas_basename(void); +#else + int get_saveas_basename(NavDialogRef& ref); +#endif + 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); +#ifndef __APPLE_COCOA__ + static Boolean filter_proc_cb(AEDesc *, void *, void *, NavFilterModes); + Boolean filter_proc_cb2(AEDesc*, void*, void*, NavFilterModes); +#endif + 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/FL/Fl_Native_File_Chooser_WIN32.H b/FL/Fl_Native_File_Chooser_WIN32.H new file mode 100644 index 000000000..6429e461c --- /dev/null +++ b/FL/Fl_Native_File_Chooser_WIN32.H @@ -0,0 +1,106 @@ +// +// 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 + OPENFILENAMEW _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/src/Fl_Native_File_Chooser.cxx b/src/Fl_Native_File_Chooser.cxx new file mode 100644 index 000000000..62bb526e4 --- /dev/null +++ b/src/Fl_Native_File_Chooser.cxx @@ -0,0 +1,45 @@ +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2005 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems to: +// +// http://www.fltk.org/str.php +// + +// 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 + +// +// End of "$Id:". +// diff --git a/src/Fl_Native_File_Chooser_FLTK.cxx b/src/Fl_Native_File_Chooser_FLTK.cxx new file mode 100644 index 000000000..e77e18700 --- /dev/null +++ b/src/Fl_Native_File_Chooser_FLTK.cxx @@ -0,0 +1,395 @@ +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2005 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems to: +// +// http://www.fltk.org/str.php +// + +#include <FL/Fl_Native_File_Chooser.H> +#include <FL/Fl_File_Icon.H> +#define 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 + +#include "Fl_Native_File_Chooser_common.cxx" +#include <sys/stat.h> + +// CTOR +FNFC_CLASS::FNFC_CTOR(int val) { + //// CANT USE THIS -- MESSES UP LINKING/CREATES DEPENDENCY ON fltk_images. + //// Have user call this from app 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; + //// } + _btype = val; + _options = NO_OPTIONS; + _filter = NULL; + _filtvalue = 0; + _parsedfilt = NULL; + _preset_file = NULL; + _prevvalue = NULL; + _directory = NULL; + _errmsg = NULL; + file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL); + type(val); // do this after file_chooser created + _nfilters = 0; + + // Added by MG + Fl_Button *b = file_chooser->previewButton; + Fl_Window *w = b->window(); + Fl_Group::current(w); // adds a "Show hidden files" check button in file_chooser's window + show_hidden = new Fl_Check_Button(b->x() + b->w() + 10, b->y(), 145, b->h(), "Show hidden files"); + show_hidden->callback((Fl_Callback*)show_hidden_cb, this); + // This is a hack to bypass the fact that fileList is a private member of file_chooser. + // Find it as 1st child of 2nd child of file_chooser's window. + Fl_Group *g = (Fl_Group *)w->array()[1]; + my_fileList = (Fl_File_Browser *)g->array()[0]; + old_dir[0] = 0; // to detect directory changes + prev_filtervalue = file_chooser->filter_value(); // to detect filter changes + // Hack to get file_chooser's showChoice widget. + // Find it as 1st child of 1st child of file_chooser's window. + g = (Fl_Group *)w->array()[0]; + showChoice = (Fl_Choice *)g->array()[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(); + + // BLOCK WHILE BROWSER SHOWN + while ( file_chooser->shown() ) { + if(strcmp(old_dir, file_chooser->directory()) != 0) { + strcpy(old_dir, file_chooser->directory()); + if(!show_hidden->value()) remove_hidden_files(my_fileList); + } else if(prev_filtervalue != file_chooser->filter_value() ) { + prev_filtervalue = file_chooser->filter_value(); + if(!show_hidden->value() ) remove_hidden_files(my_fileList); + } + Fl::wait(); + } + + if ( file_chooser->value() && file_chooser->value()[0] ) { + _prevvalue = strfree(_prevvalue); + _prevvalue = strnew(file_chooser->value()); + _filtvalue = file_chooser->filter_value(); // update filter value + + // HANDLE SHOWING 'SaveAs' CONFIRM + if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { + struct stat buf; + if ( stat(file_chooser->value(), &buf) != -1 ) { + if ( buf.st_mode & S_IFREG ) { // Regular file + exists? + if ( exist_dialog() == 0 ) { + return(1); + } + } + } + } + } + + if ( file_chooser->count() ) return(0); + else return(1); +} + +// 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); +} + +void FNFC_CLASS::show_hidden_cb(Fl_Check_Button *o, void *data) +{ + Fl_Native_File_Chooser *mychooser = (Fl_Native_File_Chooser *)data; + if(o->value()) { + mychooser->my_fileList->load(mychooser->file_chooser->directory()); + } else { + remove_hidden_files(mychooser->my_fileList); + mychooser->my_fileList->redraw(); + } +} + +void FNFC_CLASS::remove_hidden_files(Fl_File_Browser *my_fileList) +{ + int count = my_fileList->size(); + for(int num = count; num >= 1; num--) { + const char *p = my_fileList->text(num); + if(*p == '.' && strcmp(p, "../") != 0) my_fileList->remove(num); + } + my_fileList->topline(1); +} + +// +// End of "$Id:". +// diff --git a/src/Fl_Native_File_Chooser_MAC.cxx b/src/Fl_Native_File_Chooser_MAC.cxx new file mode 100644 index 000000000..8d3b3f9d9 --- /dev/null +++ b/src/Fl_Native_File_Chooser_MAC.cxx @@ -0,0 +1,1087 @@ +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2005 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems to: +// +// http://www.fltk.org/str.php +// + +// TODO: +// o When doing 'open file', only dir is preset, not filename. +// Possibly 'preset_file' could be used to select the filename. +// + +#include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat +#include <libgen.h> // dirname(3) +#include <sys/types.h> // stat(2) +#include <sys/stat.h> // stat(2) + + +#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 + +#ifndef __APPLE_COCOA__ +// TRY TO CONVERT AN AEDesc TO AN FSRef +// 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 AEDescToFSRef(const AEDesc* desc, FSRef* fsref) { + OSStatus err = noErr; + AEDesc coerceDesc; + // If AEDesc isn't already an FSRef, convert it to one + if ( desc->descriptorType != typeFSRef ) { + if ( ( err = AECoerceDesc(desc, typeFSRef, &coerceDesc) ) == noErr ) { + // Get FSRef out of AEDesc + err = AEGetDescData(&coerceDesc, fsref, sizeof(FSRef)); + AEDisposeDesc(&coerceDesc); + } + } else { + err = AEGetDescData(desc, fsref, sizeof(FSRef)); + } + return( err ); +} + +// 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) { + FSRef fsref; + if ( AEDescToFSRef(&_reply.selection, &fsref) != noErr ) { + // Conversion failed? Return empty name + s[0] = 0; + return(-1); + } + FSRefMakePath(&fsref, (UInt8 *)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, typeFSRef, &keyWord, &desc) != noErr) { + pathnames[index-1] = strnew(""); + continue; + } + FSRef fsref; + if (AEGetDescData(&desc, &fsref, sizeof(FSRef)) != noErr ) { + pathnames[index-1] = strnew(""); + continue; + } + char s[4096]; + FSRefMakePath(&fsref, (UInt8 *)s, sizeof(s)-1); + pathnames[index-1] = strnew(s); + AEDisposeDesc(&desc); + } + return(0); +} +#endif /* !__APPLE_COCOA__ */ + +// 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; +} + +#ifndef __APPLE_COCOA__ +// 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); +} + +// IS PATHNAME A DIRECTORY? +// 1 - path is a dir +// 0 - path not a dir or error +// +static int IsDir(const char *pathname) { + struct stat buf; + if ( stat(pathname, &buf) != -1 ) { + if ( buf.st_mode & S_IFDIR ) return(1); + } + return(0); +} + +// PRESELECT PATHNAME IN BROWSER +static void PreselectPathname(NavCBRecPtr cbparm, const char *path) { + // XXX: path must be a dir, or kNavCtlSetLocation fails with -50. + // Why, I don't know. Let me know with a bug report. -erco + // + if ( ! IsDir(path) ) { + path = dirname((char*)path); + } + OSStatus err; + FSRef fsref; + err = FSPathMakeRef((const UInt8*)path, &fsref, NULL); + if ( err != noErr) { + fprintf(stderr, "FSPathMakeRef(%s) failed: err=%d\n", path, (int)err); + return; + } + AEDesc desc; + err = AECreateDesc(typeFSRef, &fsref, sizeof(FSRef), &desc); + if ( err != noErr) { + fprintf(stderr, "AECreateDesc() failed: err=%d\n", (int)err); + } + err = NavCustomControl(cbparm->context, kNavCtlSetLocation, &desc); + if ( err != noErr) { + fprintf(stderr, "NavCustomControl() failed: err=%d\n", (int)err); + } + AEDisposeDesc(&desc); +} + +// NAV CALLBACK EVENT HANDLER +void FNFC_CLASS::event_handler(NavEventCallbackMessage callBackSelector, + NavCBRecPtr cbparm, + void *data) { + FNFC_CLASS *nfb = (FNFC_CLASS*)data; + switch (callBackSelector) { + case kNavCBStart: + { + if ( nfb->directory() ) { // dir specified? + PreselectPathname(cbparm, nfb->directory()); // use it first + } else if ( nfb->preset_file() ) { // file specified? + PreselectPathname(cbparm, nfb->preset_file()); // use if no dir + } + if ( nfb->_btype == BROWSE_SAVE_FILE && nfb->preset_file() ) { + const char *p, *q; + p = nfb->preset_file();//don't use the path part of preset_file + q = strrchr(p, '/'); + if(q == NULL) q = p; else q++; + CFStringRef namestr = CFStringCreateWithCString(NULL, + q, + kCFStringEncodingUTF8); + 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: + { + long 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; + } +} +#endif /* !__APPLE_COCOA__ */ + +// CONSTRUCTOR +FNFC_CLASS::FNFC_CTOR(int val) { + _btype = val; +#ifdef __APPLE_COCOA__ + _panel = NULL; +#else + NavGetDefaultDialogCreationOptions(&_opts); + _opts.optionFlags |= kNavDontConfirmReplacement; // no confirms for "save as" + _ref = NULL; +#endif + _options = NO_OPTIONS; + 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 +#ifndef __APPLE_COCOA__ + if (_ref) { NavDialogDispose(_ref); _ref = NULL; } +#endif + // _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); +} + +#ifndef __APPLE_COCOA__ +// SET THE TYPE OF BROWSER +void FNFC_CLASS::type(int val) { + _btype = val; +} +#endif /* !__APPLE_COCOA__ */ + +// 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 + Fl::flush(); + + _keepstate = kNavNormalState; +#ifndef __APPLE_COCOA__ + // BROWSER TITLE + CFStringRef cfs_title; + cfs_title = CFStringCreateWithCString(NULL, + _title ? _title : "No Title", + kCFStringEncodingUTF8); + _opts.windowTitle = cfs_title; + // 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, + kCFStringEncodingUTF8); + 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 + } + // _opts.optionFlags |= kNavSupportPackages; // enables *.app TODO: Make a flag to control this +#endif /* !__APPLE_COCOA__ */ + + // POST BROWSER + int err = post(); + +#ifndef __APPLE_COCOA__ + // RELEASE _FILT_ARR + if ( filter_array ) CFRelease(filter_array); + filter_array = NULL; + _opts.popupExtension = NULL; + // RELEASE TITLE + if ( cfs_title ) CFRelease(cfs_title); + cfs_title = NULL; +#endif /* !__APPLE_COCOA__ */ + _filt_total = 0; + + return(err); +} + +#ifndef __APPLE_COCOA__ +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? + } + + if ( ! ( _options & NEW_FOLDER ) ) { + _keepstate |= kNavDontNewFolderState; + } + + switch (_btype) { + case BROWSE_FILE: + case BROWSE_MULTI_FILE: + // 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: + 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 + kNavGenericSignature, //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); +} +#endif /* !__APPLE_COCOA__ */ + +// 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 +} + +#ifndef __APPLE_COCOA__ +// 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); + + FSRef fsref; + char pathname[4096]; + + // On fail, filter should return true by default + if ( AEDescToFSRef(theItem, &fsref) != noErr ) { + return(true); + } + FSRefMakePath(&fsref, (UInt8 *)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); +} +#endif + +// 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); +} + +#ifdef __APPLE_COCOA__ +#import <Cocoa/Cocoa.h> +#define UNLIKELYPREFIX "___fl_very_unlikely_prefix_" + +int FNFC_CLASS::get_saveas_basename(void) { + char *q = strdup( [[(NSSavePanel*)_panel filename] fileSystemRepresentation] ); + id delegate = [(NSSavePanel*)_panel delegate]; + if(delegate != nil) { + const char *d = [[(NSSavePanel*)_panel directory] fileSystemRepresentation]; + int l = strlen(d) + 1; + int lu = strlen(UNLIKELYPREFIX); + //remove UNLIKELYPREFIX between directory and filename parts + memmove(q + l, q + l + lu, strlen(q + l + lu) + 1); + } + set_single_pathname( q ); + free(q); + return 0; +} + +// SET THE TYPE OF BROWSER +void FNFC_CLASS::type(int val) { + _btype = val; + switch (_btype) { + case BROWSE_FILE: + case BROWSE_MULTI_FILE: + case BROWSE_DIRECTORY: + case BROWSE_MULTI_DIRECTORY: + _panel = [NSOpenPanel openPanel]; + break; + case BROWSE_SAVE_DIRECTORY: + case BROWSE_SAVE_FILE: + _panel = [NSSavePanel savePanel]; + break; + } +} + +@interface FLopenDelegate : NSObject { + NSPopUpButton *nspopup; + char **filter_pattern; +} +- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern; +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; +@end +@implementation FLopenDelegate +- (FLopenDelegate*)setPopup:(NSPopUpButton*)popup filter_pattern:(char**)pattern +{ + nspopup = popup; + filter_pattern = pattern; + return self; +} +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename +{ + if( [nspopup indexOfSelectedItem] == [nspopup numberOfItems] - 1) return YES; + const char *pathname = [filename fileSystemRepresentation]; + if ( fl_filename_isdir(pathname) ) return YES; + if ( fl_filename_match(pathname, filter_pattern[ [nspopup indexOfSelectedItem] ]) ) return YES; + return NO; +} +@end + +@interface FLsaveDelegate : NSObject { +} +- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag; +@end +@implementation FLsaveDelegate +- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag +{ + if(! okFlag) return filename; + // User has clicked save, and no overwrite confirmation should occur. + // To get the latter, we need to change the name we return (hence the prefix): + return [@ UNLIKELYPREFIX stringByAppendingString:filename]; +} +@end + +static NSPopUpButton *createPopupAccessory(NSSavePanel *panel, const char *filter, const char *title, int rank) +{ + NSPopUpButton *popup; + NSRect rectview = NSMakeRect(5, 5, 350, 30 ); + NSView *view = [[[NSView alloc] initWithFrame:rectview] autorelease]; + NSRect rectbox = NSMakeRect(0, 3, 50, 1 ); + NSBox *box = [[[NSBox alloc] initWithFrame:rectbox] autorelease]; + NSRect rectpop = NSMakeRect(60, 0, 250, 30 ); + popup = [[[NSPopUpButton alloc ] initWithFrame:rectpop pullsDown:NO] autorelease]; + [view addSubview:box]; + [view addSubview:popup]; + [box setBorderType:NSNoBorder]; + NSString *nstitle = [[NSString alloc] initWithUTF8String:title]; + [box setTitle:nstitle]; + [nstitle release]; + NSFont *font = [NSFont controlContentFontOfSize:NSRegularControlSize]; + [box setTitleFont:font]; + [box sizeToFit]; + CFStringRef tab = CFSTR("\n"); + CFStringRef tmp_cfs; + tmp_cfs = CFStringCreateWithCString(NULL, filter, kCFStringEncodingASCII); + CFArrayRef array = CFStringCreateArrayBySeparatingStrings(NULL, tmp_cfs, tab); + CFRelease(tmp_cfs); + CFRelease(tab); + [popup addItemsWithTitles:(NSArray*)array]; + NSMenuItem *item = [popup itemWithTitle:@""]; + if(item) [popup removeItemWithTitle:@""]; + CFRelease(array); + [popup selectItemAtIndex:rank]; + [panel setAccessoryView:view]; + return popup; +} + +// 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 + if ( _filt_total == 0 ) { // Make sure they match + _filt_value = 0; // TBD: move to someplace more logical? + } + NSAutoreleasePool *localPool; + localPool = [[NSAutoreleasePool alloc] init]; + int retval; + NSString *nstitle = [NSString stringWithUTF8String: (_title ? _title : "No Title")]; + [(NSSavePanel*)_panel setTitle:nstitle]; + switch (_btype) { + case BROWSE_MULTI_FILE: + [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; + break; + case BROWSE_MULTI_DIRECTORY: + [(NSOpenPanel*)_panel setAllowsMultipleSelection:YES]; + case BROWSE_DIRECTORY: + [(NSOpenPanel*)_panel setCanChooseDirectories:YES]; + break; + case BROWSE_SAVE_DIRECTORY: + [(NSSavePanel*)_panel setCanCreateDirectories:YES]; + break; + } + + // SHOW THE DIALOG + if( [(NSSavePanel*)_panel isKindOfClass:[NSOpenPanel class]] ) { + NSPopUpButton *popup = nil; + if(_filt_total) { + char *p; p = _filter; + char *q; q = new char[strlen(p) + 1]; + char *r, *s, *t; + t = q; + do { //copy to t what is in _filter removing what is between \t and \n, if any + r = strchr(p, '\n'); + if(!r) r = p + strlen(p) - 1; + s = strchr(p, '\t'); + if(s && s < r) { memcpy(q, p, s - p); q += s - p; *(q++) = '\n'; } + else { memcpy(q, p, r - p + 1); q += r - p + 1; } + *q = 0; + p = r + 1; + } while(*p); + popup = createPopupAccessory((NSSavePanel*)_panel, t, "Enable:", 0); + delete t; + [[popup menu] addItem:[NSMenuItem separatorItem]]; + [popup addItemWithTitle:@"All Documents"]; + [popup setAction:@selector(validateVisibleColumns)]; + [popup setTarget:(NSObject*)_panel]; + static FLopenDelegate *openDelegate = nil; + if(openDelegate == nil) { + // not to be ever freed + openDelegate = [[FLopenDelegate alloc] init]; + } + [openDelegate setPopup:popup filter_pattern:_filt_patt]; + [(NSOpenPanel*)_panel setDelegate:openDelegate]; + } + NSString *dir = nil; + NSString *fname = nil; + NSString *preset = nil; + if(_preset_file) { + preset = [[NSString alloc] initWithUTF8String:_preset_file]; + if(strchr(_preset_file, '/') != NULL) + dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]]; + fname = [preset lastPathComponent]; + } + if(_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory]; + retval = [(NSOpenPanel*)_panel runModalForDirectory:dir file:fname types:nil]; + [dir release]; + [preset release]; + if(_filt_total) { + _filt_value = [popup indexOfSelectedItem]; + } + if ( retval == NSOKButton ) { + clear_pathnames(); + NSArray *array = [(NSOpenPanel*)_panel filenames]; + _tpathnames = [array count]; + _pathnames = new char*[_tpathnames]; + for(int i = 0; i < _tpathnames; i++) { + _pathnames[i] = strnew([(NSString*)[array objectAtIndex:i] fileSystemRepresentation]); + } + } + } + else { + NSString *dir = nil; + NSString *fname = nil; + NSString *preset = nil; + NSPopUpButton *popup = nil; + if( !(_options & SAVEAS_CONFIRM) ) { + static FLsaveDelegate *saveDelegate = nil; + if(saveDelegate == nil)saveDelegate = [[FLsaveDelegate alloc] init];//not to be ever freed + [(NSSavePanel*)_panel setDelegate:saveDelegate]; + } + if(_preset_file) { + preset = [[NSString alloc] initWithUTF8String:_preset_file]; + if(strchr(_preset_file, '/') != NULL) { + dir = [[NSString alloc] initWithString:[preset stringByDeletingLastPathComponent]]; + } + fname = [preset lastPathComponent]; + } + if(_directory && !dir) dir = [[NSString alloc] initWithUTF8String:_directory]; + if(_filt_total) { + popup = createPopupAccessory((NSSavePanel*)_panel, _filter, "Format:", _filt_value); + } + retval = [(NSSavePanel*)_panel runModalForDirectory:dir file:fname]; + if(_filt_total) { + _filt_value = [popup indexOfSelectedItem]; + } + [dir release]; + [preset release]; + if ( retval == NSOKButton ) get_saveas_basename(); + } + [localPool release]; + return (retval == NSOKButton ? 0 : 1); +} + +#endif //__APPLE_COCOA__ + +// +// End of "$Id:". +// diff --git a/src/Fl_Native_File_Chooser_WIN32.cxx b/src/Fl_Native_File_Chooser_WIN32.cxx new file mode 100644 index 000000000..3d9545903 --- /dev/null +++ b/src/Fl_Native_File_Chooser_WIN32.cxx @@ -0,0 +1,848 @@ +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2005 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems to: +// +// http://www.fltk.org/str.php +// + +// Any application to multi-folder implementation: +// http://www.codeproject.com/dialog/selectfolder.asp +// + +#include <stdio.h> // debugging +#include <wchar.h> //MG +#include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat +typedef const wchar_t *LPCWSTR; //MG +LPCWSTR utf8towchar(const char *in); //MG +char *wchartoutf8(LPCWSTR in); //MG + +#include <FL/Fl_Native_File_Chooser.H> +#define FNFC_CLASS Fl_Native_File_Chooser +#define FNFC_CTOR Fl_Native_File_Chooser + +#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(OPENFILENAMEW)); + _ofn.lStructSize = sizeof(OPENFILENAMEW); + _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 ) { + delete [] _ofn.lpstrFile; + _ofn.lpstrFile = NULL; + } + if ( _ofn.lpstrInitialDir ) { + delete [] _ofn.lpstrInitialDir; + _ofn.lpstrInitialDir = NULL; + } + _ofn.lpstrFilter = NULL; // (deleted elsewhere) + int temp = _ofn.nFilterIndex; // keep the filter_value + memset((void*)&_ofn, 0, sizeof(_ofn)); + _ofn.lStructSize = sizeof(OPENFILENAMEW); + _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 = MAX_PATH; + _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 WCHAR[fsize]; + _ofn.nMaxFile = fsize-1; + _ofn.lpstrFile[0] = 0; + _ofn.lpstrFile[1] = 0; // dnull + // PARENT WINDOW + _ofn.hwndOwner = GetForegroundWindow(); + // DIALOG TITLE + if (_title) { + static WCHAR wtitle[200]; + wcscpy(wtitle, utf8towchar(_title)); + _ofn.lpstrTitle = wtitle; + } else { + _ofn.lpstrTitle = NULL; + } + // FILTER + if (_parsedfilt != NULL) { // to convert a null-containing char string into a widechar string + static WCHAR wpattern[MAX_PATH]; + const char *p = _parsedfilt; + while(*(p + strlen(p) + 1) != 0) p += strlen(p) + 1; + p += strlen(p) + 2; + MultiByteToWideChar(CP_UTF8, 0, _parsedfilt, p - _parsedfilt, wpattern, MAX_PATH); + _ofn.lpstrFilter = wpattern; + } else { + _ofn.lpstrFilter = 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); + } + wcscpy(_ofn.lpstrFile, utf8towchar(_preset_file)); +// Unix2Win(_ofn.lpstrFile); + len = wcslen(_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 = new WCHAR[MAX_PATH]; + wcscpy((WCHAR *)_ofn.lpstrInitialDir, utf8towchar(_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 = GetSaveFileNameW(&_ofn); + } else { + err = GetOpenFileNameW(&_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(wchartoutf8(_ofn.lpstrFile)); + // Win2Unix(_pathnames[_tpathnames-1]); + break; + case BROWSE_MULTI_FILE: { + // EXTRACT MULTIPLE FILENAMES + const WCHAR *dirname = _ofn.lpstrFile; + int dirlen = wcslen(dirname); + if ( dirlen > 0 ) { + // WALK STRING SEARCHING FOR 'DOUBLE-NULL' + // eg. "/dir/name\0foo1\0foo2\0foo3\0\0" + // + char pathname[MAX_PATH]; + for ( const WCHAR *s = dirname + dirlen + 1; + *s; s+= (wcslen(s)+1)) { + strcpy(pathname, wchartoutf8(dirname)); + strcat(pathname, "\\"); + strcat(pathname, wchartoutf8(s)); + add_pathname(pathname); + } + } + // 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(wchartoutf8(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 + // + // NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares + // from being visible in BROWSE_DIRECTORY mode. + // See Walter Garm's comments in ./TODO. + +#if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 + if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON; + _binf.ulFlags |= BIF_USENEWUI | 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_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); +} + +char *wchartoutf8(LPCWSTR in) +{ + static char *out = NULL; + static int lchar = 0; + if (in == NULL)return NULL; + int utf8len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); + if (utf8len > lchar) { + lchar = utf8len; + out = (char *)realloc(out, lchar * sizeof(char)); + } + WideCharToMultiByte(CP_UTF8, 0, in, -1, out, utf8len, NULL, NULL); + return out; +} + +LPCWSTR utf8towchar(const char *in) +{ + static WCHAR *wout = NULL; + static int lwout = 0; + if (in == NULL)return NULL; + int wlen = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0); + if (wlen > lwout) { + lwout = wlen; + wout = (WCHAR *)realloc(wout, lwout * sizeof(WCHAR)); + } + MultiByteToWideChar(CP_UTF8, 0, in, -1, wout, wlen); + return wout; +} + +// +// End of "$Id:". +// diff --git a/src/Fl_Native_File_Chooser_common.cxx b/src/Fl_Native_File_Chooser_common.cxx new file mode 100644 index 000000000..4eefcad14 --- /dev/null +++ b/src/Fl_Native_File_Chooser_common.cxx @@ -0,0 +1,83 @@ +// "$Id$" +// +// FLTK native OS file chooser widget +// +// Copyright 1998-2005 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems to: +// +// http://www.fltk.org/str.php +// + +#include <string.h> +#include <FL/Enumerations.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); +} + +// +// End of "$Id:". +// diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index 1e8068a95..215637225 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -1,5 +1,5 @@ // -// "$Id: Fl_mac.cxx 6971 2009-04-13 07:32:01Z matt $" +// "$Id: Fl_cocoa.mm 6971 2009-04-13 07:32:01Z matt $" // // MacOS specific code for the Fast Light Tool Kit (FLTK). // @@ -63,7 +63,7 @@ extern "C" { #import <Cocoa/Cocoa.h> -#ifndef NSINTEGER_DEFINED //appears with 10.5 in NSObjCRuntime.h +#ifndef NSINTEGER_DEFINED // appears with 10.5 in NSObjCRuntime.h #if defined(__LP64__) && __LP64__ typedef long NSInteger; typedef unsigned long NSUInteger; @@ -146,7 +146,7 @@ void fl_set_status(int x, int y, int w, int h) { } -/** +/* * Mac keyboard lookup table */ static unsigned short macKeyLookUp[128] = @@ -178,7 +178,7 @@ static unsigned short macKeyLookUp[128] = FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/, }; -/** +/* * convert the current mouse chord into the FLTK modifier state */ static unsigned int mods_to_e_state( NSUInteger mods ) @@ -197,7 +197,7 @@ static unsigned int mods_to_e_state( NSUInteger mods ) } -/** +/* * convert the current mouse chord into the FLTK keysym */ /* @@ -211,7 +211,8 @@ static unsigned int mods_to_e_state( NSUInteger mods ) else if ( mods & NSAlphaShiftKeyMask ) Fl::e_keysym = FL_Caps_Lock; else Fl::e_keysym = 0; //printf( "to sym 0x%08x (%04x)\n", Fl::e_keysym, mods ); - }*/ + } + */ // these pointers are set by the Fl::lock() function: static void nothing() {} void (*fl_lock_function)() = nothing; @@ -327,17 +328,16 @@ void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v) void DataReady::RemoveFD(int n, int events) { int i,j; - for (i=j=0; i<nfds; i++) - { - if (fds[i].fd == n) - { + for (i=j=0; i<nfds; i++) { + if (fds[i].fd == n) { int e = fds[i].events & ~events; if (!e) continue; // if no events left, delete this fd fds[i].events = e; } // move it down in the array if necessary: - if (j<i) - { fds[j] = fds[i]; } + if (j<i) { + fds[j] = fds[i]; + } j++; } nfds = j; @@ -358,23 +358,22 @@ int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x) /*LOCK*/ r = _fdsets[0], w = _fdsets[1], x = _fdsets[2]; /*LOCK*/ ret = ::select(_maxfd+1, &r, &w, &x, &t); DataUnlock(); - if ( ret == -1 ) - { DEBUGPERRORMSG("CheckData(): select()"); } + if ( ret == -1 ) { + DEBUGPERRORMSG("CheckData(): select()"); + } return(ret); } // HANDLE DATA READY CALLBACKS void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x) { - for (int i=0; i<nfds; i++) - { + for (int i=0; i<nfds; i++) { int f = fds[i].fd; short revents = 0; if (FD_ISSET(f, &r)) revents |= POLLIN; if (FD_ISSET(f, &w)) revents |= POLLOUT; if (FD_ISSET(f, &x)) revents |= POLLERR; - if (fds[i].events & revents) - { + if (fds[i].events & revents) { DEBUGMSG("DOING CALLBACK: "); fds[i].cb(f, fds[i].arg); DEBUGMSG("DONE\n"); @@ -391,8 +390,7 @@ void* DataReady::DataReadyThread(void *o) DataReady *self = (DataReady*)o; NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; - while ( 1 ) // loop until thread cancel or error - { + while ( 1 ) { // loop until thread cancel or error // Thread safe local copies of data before each select() self->DataLock(); /*LOCK*/ int maxfd = self->_maxfd; @@ -409,8 +407,7 @@ void* DataReady::DataReadyThread(void *o) timeval t = { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem int ret = ::select(maxfd+1, &r, &w, &x, &t); pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel - switch ( ret ) - { + switch ( ret ) { case 0: // NO DATA continue; case -1: // ERROR @@ -421,7 +418,7 @@ void* DataReady::DataReadyThread(void *o) default: // DATA READY { if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) // cancel? - { return(NULL); } // just exit + { return(NULL); } // just exit DEBUGMSG("CHILD THREAD: DATA IS READY\n"); NSPoint pt={0,0}; NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt @@ -451,12 +448,10 @@ void DataReady::StartThread(void *new_userdata) // CANCEL 'DATA READY' THREAD, CLOSE PIPE void DataReady::CancelThread(const char *reason) { - if ( tid ) - { + if ( tid ) { DEBUGMSG("*** CANCEL THREAD: "); DEBUGMSG(reason); - if ( pthread_cancel(tid) == 0 ) // cancel first - { + if ( pthread_cancel(tid) == 0 ) { // cancel first DataLock(); /*LOCK*/ write(_cancelpipe[1], "x", 1); // wake thread from select DataUnlock(); @@ -473,26 +468,33 @@ void DataReady::CancelThread(const char *reason) } void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v ) -{ dataready.AddFD(n, events, cb, v); } +{ + dataready.AddFD(n, events, cb, v); +} void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) -{ dataready.AddFD(fd, POLLIN, cb, v); } +{ + dataready.AddFD(fd, POLLIN, cb, v); +} void Fl::remove_fd(int n, int events) -{ dataready.RemoveFD(n, events); } +{ + dataready.RemoveFD(n, events); +} void Fl::remove_fd(int n) -{ dataready.RemoveFD(n, -1); } +{ + dataready.RemoveFD(n, -1); +} -/** +/* * Check if there is actually a message pending! */ int fl_ready() { - NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0] - inMode:NSDefaultRunLoopMode dequeue:NO]; - if(retval != nil) [retval release]; - return retval != nil; + NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0] + inMode:NSDefaultRunLoopMode dequeue:NO]; + return retval != nil; } @@ -503,8 +505,7 @@ static void processFLTKEvent(void) { // Check to see what's ready, and invoke user's cb's // fd_set r,w,x; - switch(dataready.CheckData(r,w,x)) - { + switch(dataready.CheckData(r,w,x)) { case 0: // NO DATA break; case -1: // ERROR @@ -517,7 +518,7 @@ static void processFLTKEvent(void) { } -/** +/* * break the current event loop */ static void breakMacEventLoop() @@ -549,7 +550,6 @@ static MacTimeout* mac_timers; static int mac_timer_alloc; static int mac_timer_used; - static void realloc_timers() { if (mac_timer_alloc == 0) { @@ -573,7 +573,6 @@ static void delete_timer(MacTimeout& t) } } - static void do_timer(EventLoopTimerRef timer, void* data) { for (int i = 0; i < mac_timer_used; ++i) { @@ -589,7 +588,6 @@ static void do_timer(EventLoopTimerRef timer, void* data) breakMacEventLoop(); } - @interface FLWindow : NSWindow { Fl_Window *w; BOOL containsGLsubwindow; @@ -613,10 +611,10 @@ static void do_timer(EventLoopTimerRef timer, void* data) defer:(BOOL)deferCreation { self = [super initWithContentRect:rect styleMask:windowStyle backing:bufferingType defer:deferCreation]; - if(self) { + if (self) { w = flw; containsGLsubwindow = NO; - } + } return self; } - (Fl_Window *)getFl_Window; @@ -641,7 +639,7 @@ static void do_timer(EventLoopTimerRef timer, void* data) } @end -/** +/* * This function is the central event handler. * It reads events from the event queue using the given maximum time * Funny enough, it returns the same time that it got as the argument. @@ -651,53 +649,46 @@ static double do_queued_events( double time = 0.0 ) got_events = 0; // Check for re-entrant condition - if ( dataready.IsThreadRunning() ) - { dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); } + if ( dataready.IsThreadRunning() ) { + dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); + } // Start thread to watch for data ready - if ( dataready.GetNfds() ) - { dataready.StartThread((void*)GetCurrentEventQueue()); } + if ( dataready.GetNfds() ) { + dataready.StartThread((void*)GetCurrentEventQueue()); + } fl_unlock_function(); - //necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser) - //the front window turns key again + // necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser) + // the front window turns key again NSWindow *nsk = [NSApp keyWindow]; NSWindow *nsm = [NSApp mainWindow]; - if([nsm isMemberOfClass:[FLWindow class]] && (nsk == nil || ( ! [nsk isMemberOfClass:[FLWindow class]] && + if ([nsm isMemberOfClass:[FLWindow class]] && (nsk == nil || ( ! [nsk isMemberOfClass:[FLWindow class]] && ! [nsk isVisible] ) ) ) { [nsm makeKeyAndOrderFront:nil]; - } -/* if([NSApp keyWindow] == nil) { - Fl_Window *w = Fl::first_window(); - if (w) { - NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid; - if([cw isVisible] && ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { - if(![cw isKeyWindow]) {//always make Fl::first_window() the key window - [cw makeKeyAndOrderFront:nil]; - } - } - } - } */ + } NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:time] inMode:NSDefaultRunLoopMode dequeue:YES]; - BOOL needSendEvent = YES; - if([event type] == NSLeftMouseDown) { - Fl_Window *grab = Fl::grab(); - if(grab && grab != [(FLWindow *)[event window] getFl_Window]) { - //a click event out of a menu window, so we should close this menu - //done here to catch also clicks on window title bar/resize box - cocoaMouseHandler(event); + if (event != nil) { + BOOL needSendEvent = YES; + if ([event type] == NSLeftMouseDown) { + Fl_Window *grab = Fl::grab(); + if (grab && grab != [(FLWindow *)[event window] getFl_Window]) { + // a click event out of a menu window, so we should close this menu + // done here to catch also clicks on window title bar/resize box + cocoaMouseHandler(event); } } - else if([event type] == NSApplicationDefined) { - if([event subtype] == FLTKDataReadyEvent) { - processFLTKEvent(); + else if ([event type] == NSApplicationDefined) { + if ([event subtype] == FLTKDataReadyEvent) { + processFLTKEvent(); } - needSendEvent = NO; + needSendEvent = NO; } - if(needSendEvent) [NSApp sendEvent:event]; + if (needSendEvent) [NSApp sendEvent:event]; + } fl_lock_function(); #if CONSOLIDATE_MOTION @@ -710,8 +701,7 @@ static double do_queued_events( double time = 0.0 ) return time; } - -/** +/* * This public function handles all events. It wait a maximum of * 'time' seconds for an event. This version returns 1 if events * other than the timeout timer were processed. @@ -724,10 +714,7 @@ int fl_wait( double time ) return (got_events); } - - - -/** +/* * Cocoa Mousewheel handler */ void cocoaMouseWheelHandler(NSEvent *theEvent) @@ -738,24 +725,22 @@ void cocoaMouseWheelHandler(NSEvent *theEvent) fl_lock_function(); Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if ( !window->shown() ) - { + if ( !window->shown() ) { fl_unlock_function(); return; } Fl::first_window(window); - if([theEvent deltaX] != 0) { - Fl::e_dx = -[theEvent deltaX]; + if ([theEvent deltaX] != 0) { + Fl::e_dx = (int)-[theEvent deltaX]; Fl::e_dy = 0; if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window ); - } else if([theEvent deltaY] != 0) { + } else if ([theEvent deltaY] != 0) { Fl::e_dx = 0; - Fl::e_dy = -[theEvent deltaY]; + Fl::e_dy = (int)-[theEvent deltaY]; if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window ); } else { fl_unlock_function(); - return; } @@ -764,8 +749,7 @@ void cocoaMouseWheelHandler(NSEvent *theEvent) // return noErr; } - -/** +/* * Cocoa Mouse Button Handler */ static void cocoaMouseHandler(NSEvent *theEvent) @@ -777,13 +761,12 @@ static void cocoaMouseHandler(NSEvent *theEvent) fl_lock_function(); Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; - if ( !window->shown() ) - { + if ( !window->shown() ) { fl_unlock_function(); return; } Fl_Window *first = Fl::first_window(); - if(first != window && !(first->modal() || first->non_modal())) Fl::first_window(window); + if (first != window && !(first->modal() || first->non_modal())) Fl::first_window(window); NSPoint pos = [theEvent locationInWindow]; pos.y = window->h() - pos.y; NSInteger btn = [theEvent buttonNumber] + 1; @@ -791,26 +774,25 @@ static void cocoaMouseHandler(NSEvent *theEvent) NSUInteger mods = [theEvent modifierFlags]; int sendEvent = 0; - switch ( [theEvent type] ) - { - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: + switch ( [theEvent type] ) { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: suppressed = 0; sendEvent = FL_PUSH; Fl::e_is_click = 1; - px = pos.x; py = pos.y; - if(btn == 1) Fl::e_state |= FL_BUTTON1; - else if(btn == 3) Fl::e_state |= FL_BUTTON2; - else if(btn == 2) Fl::e_state |= FL_BUTTON3; + px = (int)pos.x; py = (int)pos.y; + if (btn == 1) Fl::e_state |= FL_BUTTON1; + else if (btn == 3) Fl::e_state |= FL_BUTTON2; + else if (btn == 2) Fl::e_state |= FL_BUTTON3; if (clickCount>1) Fl::e_clicks++; else Fl::e_clicks = 0; // fall through - case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: Fl::e_state &= 0xff0000; if (suppressed) { suppressed = 0; @@ -822,19 +804,19 @@ static void cocoaMouseHandler(NSEvent *theEvent) } Fl::e_keysym = keysym[ btn ]; // fall through - case NSMouseMoved: + case NSMouseMoved: suppressed = 0; if ( !sendEvent ) { sendEvent = FL_MOVE; } // fall through - case NSLeftMouseDragged: - case NSRightMouseDragged: - case NSOtherMouseDragged: { + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: { if (suppressed) break; if ( !sendEvent ) { sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG - if (abs(pos.x-px)>5 || abs(pos.y-py)>5) + if (fabs(pos.x-px)>5 || fabs(pos.y-py)>5) Fl::e_is_click = 0; } mods_to_e_state( mods ); @@ -844,9 +826,10 @@ static void cocoaMouseHandler(NSEvent *theEvent) Fl::e_x = pos.x; Fl::e_y = pos.y; Fl::handle( sendEvent, window ); - } break; - default: - break; + } + break; + default: + break; } fl_unlock_function(); @@ -867,112 +850,111 @@ static void cocoaMouseHandler(NSEvent *theEvent) unsigned char, // not used in this function unsigned short) // not used in this function { - // first get the keyboard mapping in a post 10.2 way - - Ptr resource; - TextEncoding encoding; - static TextEncoding lastEncoding = kTextEncodingMacRoman; - int len = 0; - KeyboardLayoutRef currentLayout = NULL; - static KeyboardLayoutRef lastLayout = NULL; - SInt32 currentLayoutId = 0; - static SInt32 lastLayoutId; - int hasLayoutChanged = false; - static Ptr uchr = NULL; - static Ptr KCHR = NULL; - // ScriptCode currentKeyScript; - - KLGetCurrentKeyboardLayout(¤tLayout); - if (currentLayout) { - KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, (const void**)¤tLayoutId); - if ( (lastLayout != currentLayout) || (lastLayoutId != currentLayoutId) ) { - lastLayout = currentLayout; - lastLayoutId = currentLayoutId; - uchr = NULL; - KCHR = NULL; - if ((KLGetKeyboardLayoutProperty(currentLayout, kKLuchrData, (const void**)&uchr) == noErr) && (uchr != NULL)) { - // done - } else if ((KLGetKeyboardLayoutProperty(currentLayout, kKLKCHRData, (const void**)&KCHR) == noErr) && (KCHR != NULL)) { - // done - } - // FIXME No Layout property found. Now we have a problem. - } - } - if (hasLayoutChanged) { - //deadKeyStateUp = deadKeyStateDown = 0; - if (KCHR != NULL) { - // FIXME this must not happen - } else if (uchr == NULL) { - KCHR = (Ptr) GetScriptManagerVariable(smKCHRCache); - } - } - if (uchr != NULL) { - // this is what I expect - resource = uchr; - } else { - resource = KCHR; - encoding = lastEncoding; - // this is actually not supported by the following code and will likely crash - } - - // now apply that keyboard mapping to our keycode - - int action; - //OptionBits options = 0; - // not used yet: OptionBits options = kUCKeyTranslateNoDeadKeysMask; - unsigned long keyboardType; - keycode &= 0xFF; - modifiers = (modifiers >> 8) & 0xFF; - keyboardType = LMGetKbdType(); - OSStatus status; - UniCharCount actuallength; - UniChar utext[10]; - - switch(eKind) { - case kEventRawKeyDown: action = kUCKeyActionDown; break; - case kEventRawKeyUp: action = kUCKeyActionUp; break; - case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break; - default: return 0; - } - - UInt32 deadKeyState = *deadKeyStatePtr; - if ((action==kUCKeyActionUp)&&(*deadKeyStatePtr)) - deadKeyStatePtr = &deadKeyState; - - status = UCKeyTranslate( - (const UCKeyboardLayout *) uchr, - keycode, action, modifiers, keyboardType, - 0, deadKeyStatePtr, - 10, &actuallength, utext); - - if (noErr != status) { - fprintf(stderr,"UCKeyTranslate failed: %d\n", (int) status); - actuallength = 0; - } - - // convert the list of unicode chars into utf8 - // FIXME no bounds check (see maxchars) - unsigned i; - for (i=0; i<actuallength; ++i) { - len += fl_utf8encode(utext[i], uniChars+len); - } - uniChars[len] = 0; - return len; + // first get the keyboard mapping in a post 10.2 way + + Ptr resource; + TextEncoding encoding; + static TextEncoding lastEncoding = kTextEncodingMacRoman; + int len = 0; + KeyboardLayoutRef currentLayout = NULL; + static KeyboardLayoutRef lastLayout = NULL; + SInt32 currentLayoutId = 0; + static SInt32 lastLayoutId; + int hasLayoutChanged = false; + static Ptr uchr = NULL; + static Ptr KCHR = NULL; + // ScriptCode currentKeyScript; + + KLGetCurrentKeyboardLayout(¤tLayout); + if (currentLayout) { + KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, (const void**)¤tLayoutId); + if ( (lastLayout != currentLayout) || (lastLayoutId != currentLayoutId) ) { + lastLayout = currentLayout; + lastLayoutId = currentLayoutId; + uchr = NULL; + KCHR = NULL; + if ((KLGetKeyboardLayoutProperty(currentLayout, kKLuchrData, (const void**)&uchr) == noErr) && (uchr != NULL)) { + // done + } else if ((KLGetKeyboardLayoutProperty(currentLayout, kKLKCHRData, (const void**)&KCHR) == noErr) && (KCHR != NULL)) { + // done + } + // FIXME No Layout property found. Now we have a problem. + } + } + if (hasLayoutChanged) { + // deadKeyStateUp = deadKeyStateDown = 0; + if (KCHR != NULL) { + // FIXME this must not happen + } else if (uchr == NULL) { + KCHR = (Ptr) GetScriptManagerVariable(smKCHRCache); + } + } + if (uchr != NULL) { + // this is what I expect + resource = uchr; + } else { + resource = KCHR; + encoding = lastEncoding; + // this is actually not supported by the following code and will likely crash + } + + // now apply that keyboard mapping to our keycode + + int action; + //OptionBits options = 0; + // not used yet: OptionBits options = kUCKeyTranslateNoDeadKeysMask; + unsigned long keyboardType; + keycode &= 0xFF; + modifiers = (modifiers >> 8) & 0xFF; + keyboardType = LMGetKbdType(); + OSStatus status; + UniCharCount actuallength; + UniChar utext[10]; + + switch(eKind) { + case kEventRawKeyDown: action = kUCKeyActionDown; break; + case kEventRawKeyUp: action = kUCKeyActionUp; break; + case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break; + default: return 0; + } + + UInt32 deadKeyState = *deadKeyStatePtr; + if ((action==kUCKeyActionUp)&&(*deadKeyStatePtr)) { + deadKeyStatePtr = &deadKeyState; + } + + status = UCKeyTranslate((const UCKeyboardLayout *) uchr, + keycode, action, modifiers, keyboardType, + 0, deadKeyStatePtr, + 10, &actuallength, utext); + + if (noErr != status) { + fprintf(stderr,"UCKeyTranslate failed: %d\n", (int) status); + actuallength = 0; + } + + // convert the list of unicode chars into utf8 + // FIXME no bounds check (see maxchars) + unsigned i; + for (i=0; i<actuallength; ++i) { + len += fl_utf8encode(utext[i], uniChars+len); + } + uniChars[len] = 0; + return len; } */ /* * keycode_function for pre-10.5 systems, this is the "historic" fltk Mac key handling */ -static int keycode_wrap_old( - char * buffer, +static int keycode_wrap_old(char * buffer, int, EventKind, UInt32, // not used in this function UInt32, UInt32 *, // not used in this function unsigned char key, unsigned short sym) { if ( (sym >= FL_KP && sym <= FL_KP_Last) || !(sym & 0xff00) || - sym == FL_Tab || sym == FL_Enter) { + sym == FL_Tab || sym == FL_Enter) { buffer[0] = key; return 1; } else { @@ -980,6 +962,7 @@ static int keycode_wrap_old( return 0; } } /* keycode_wrap_old */ + /* * Stub pointer to select appropriate keycode_function per operating system version. This function pointer * is initialised in fl_open_display, based on the runtime identification of the host OS version. This is @@ -990,19 +973,19 @@ static int (*keycode_function)(char*, int, EventKind, UInt32, UInt32, UInt32*, u // EXPERIMENTAL! -//this gets called by CJK character palette input +// this gets called by CJK character palette input OSStatus carbonTextHandler( EventHandlerCallRef nextHandler, EventRef event, void *unused ) { - //make sure the key window is an FLTK window + // make sure the key window is an FLTK window NSWindow *keywindow = [NSApp keyWindow]; - if(keywindow == nil || ![keywindow isMemberOfClass:[FLWindow class]]) return eventNotHandledErr; - //under 10.5 this gets called only after character palette inputs - //but under 10.6 this gets also called by interpretKeyEvents - //during character composition when we don't want to run it - if([[NSApp currentEvent] type] != NSSystemDefined) return eventNotHandledErr; + if (keywindow == nil || ![keywindow isMemberOfClass:[FLWindow class]]) return eventNotHandledErr; + // under 10.5 this gets called only after character palette inputs + // but under 10.6 this gets also called by interpretKeyEvents + // during character composition when we don't want to run it + if ([[NSApp currentEvent] type] != NSSystemDefined) return eventNotHandledErr; Fl_Window *window = [(FLWindow*)keywindow getFl_Window]; fl_lock_function(); - //int kind = GetEventKind(event); + // int kind = GetEventKind(event); unsigned short buf[200]; ByteCount size; GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, @@ -1028,27 +1011,27 @@ OSStatus carbonTextHandler( EventHandlerCallRef nextHandler, EventRef event, voi } static void processCompositionSequence(CFStringRef s, Fl_Window *window) -//composed character sequences are sent here -//they contain 2 unichars, the first comes from the deadkey and can be non ascii (e.g., diaeresis), -//the second is the character to be modified (e.g., e to be accented) +// composed character sequences are sent here +// they contain 2 unichars, the first comes from the deadkey and can be non ascii (e.g., diaeresis), +// the second is the character to be modified (e.g., e to be accented) { - //unicodes: non-ascii unicode chars produced by deadkeys - //asciis: corresponding ascii chars expected by Fl::compose() + // unicodes: non-ascii unicode chars produced by deadkeys + // asciis: corresponding ascii chars expected by Fl::compose() // diaeresis acute-accent circumflex tilde ring-above static UniChar unicodes[] = {0xA8, 0xB4, 0x2C6, 0x2DC, 0x2DA }; static char asciis[] = { ':', '\'', '^', '~', '*' }; - if(CFStringGetLength(s) == 0) return; + if (CFStringGetLength(s) == 0) return; char text[10]; char buffer[10]; UniChar unis[2]; CFStringGetCharacters(s, CFRangeMake(0, 2), unis); for(unsigned int i = 0; i < sizeof(asciis)/sizeof(char); i++) { - if(unis[0] == unicodes[i]) { - //replace the non-ascii unicode by the corresponding ascii value + if (unis[0] == unicodes[i]) { + // replace the non-ascii unicode by the corresponding ascii value unis[0] = (UniChar)asciis[i]; break; - } } + } CFStringRef smod = CFStringCreateWithCharacters(kCFAllocatorDefault, unis, 2); CFStringGetCString(smod, buffer, sizeof(buffer), kCFStringEncodingUTF8); CFRelease(smod); @@ -1066,7 +1049,7 @@ static void processCompositionSequence(CFStringRef s, Fl_Window *window) } -/** +/* * handle cocoa keyboard events */ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) @@ -1094,11 +1077,11 @@ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) static BOOL compose = NO; static NSText *edit; static int countevents; - static CFMutableStringRef sequence;//will contain the two characters of the composition sequence - if(compose) {//we are in a composition sequence - //the only benefit of sending events to the NSText object edit is that the deadkey becomes visible - //at its keyUp event; without this, the deadkey remains invisible - if([s length] == 0) {//occurs if 2 deadkeys are typed successively by error + static CFMutableStringRef sequence; // will contain the two characters of the composition sequence + if (compose) { // we are in a composition sequence + // the only benefit of sending events to the NSText object edit is that the deadkey becomes visible + // at its keyUp event; without this, the deadkey remains invisible + if ([s length] == 0) { // occurs if 2 deadkeys are typed successively by error compose = NO; [edit setString:@""]; CFRelease(sequence); @@ -1109,55 +1092,53 @@ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) countevents++; UniChar deadkey; CFStringGetCharacters((CFStringRef)s, CFRangeMake(0, 1), &deadkey); - CFStringAppendCharacters(sequence, &deadkey, 1);//done for keyUp of deadkey and keyDown of next key - if(countevents >= 3) {//end of composition sequence + CFStringAppendCharacters(sequence, &deadkey, 1);// done for keyUp of deadkey and keyDown of next key + if (countevents >= 3) { // end of composition sequence processCompositionSequence( sequence, window ); CFRelease(sequence); - [edit setString:@""];//clear the content of the edit object - compose=NO;//character composition is now complete + [edit setString:@""]; // clear the content of the edit object + compose=NO; // character composition is now complete } fl_unlock_function(); return noErr; } - if([s length] == 0) {//this is a dead key that must be combined with the next key to be pressed + if ([s length] == 0) { // this is a dead key that must be combined with the next key to be pressed while (window->parent()) window = window->window(); - Fl::e_keysym = FL_Control_R;//first simulate pressing of the compose key (FL_Control_R) + Fl::e_keysym = FL_Control_R; // first simulate pressing of the compose key (FL_Control_R) Fl::e_text = (char*)""; Fl::e_length = 0; Fl::handle(FL_KEYBOARD, window); compose=YES; - //then send remaining events to an object of type NSText that helps handle character composition sequences + // then send remaining events to an object of type NSText that helps handle character composition sequences edit = [[theEvent window] fieldEditor:YES forObject:nil]; [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; countevents = 1; sequence = CFStringCreateMutable(NULL, 2); fl_unlock_function(); return noErr; - } - else { + } else { char buff[10]; CFStringGetCString((CFStringRef)s, buff, sizeof(buff), kCFStringEncodingUnicode); key = *(unichar*)buff; keychar = buff[0]; - } + } // extended keyboards can also send sequences on key-up to generate Kanji etc. codes. // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode. // In this mode, there seem to be no key-down codes // printf("%08x %08x %08x\n", keyCode, mods, key); maskedKeyCode = keyCode & 0x7f; /* output a human readable event identifier for debugging - const char *ev = ""; - switch (kind) { - case kEventRawKeyDown: ev = "kEventRawKeyDown"; break; - case kEventRawKeyRepeat: ev = "kEventRawKeyRepeat"; break; - case kEventRawKeyUp: ev = "kEventRawKeyUp"; break; - case kEventRawKeyModifiersChanged: ev = "kEventRawKeyModifiersChanged"; break; - default: ev = "unknown"; - } - printf("%08x %08x %08x '%c' %s \n", mods, keyCode, key, key, ev); + * const char *ev = ""; + * switch (kind) { + * case kEventRawKeyDown: ev = "kEventRawKeyDown"; break; + * case kEventRawKeyRepeat: ev = "kEventRawKeyRepeat"; break; + * case kEventRawKeyUp: ev = "kEventRawKeyUp"; break; + * case kEventRawKeyModifiersChanged: ev = "kEventRawKeyModifiersChanged"; break; + * default: ev = "unknown"; break; + * } + * printf("%08x %08x %08x '%c' %s \n", mods, keyCode, key, key, ev); */ - switch([theEvent type]) - { + switch([theEvent type]) { case NSKeyDown: sendEvent = FL_KEYBOARD; // fall through @@ -1166,7 +1147,7 @@ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) sendEvent = FL_KEYUP; Fl::e_state &= 0xbfffffff; // clear the deadkey flag } - mods_to_e_state( mods ); //we process modifier keys at the same time + mods_to_e_state( mods ); // we process modifier keys at the same time // if the user pressed alt/option, event_key should have the keycap, // but event_text should generate the international symbol sym = macKeyLookUp[maskedKeyCode]; @@ -1174,26 +1155,25 @@ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) sym = tolower(key); else if ( Fl::e_state&FL_CTRL && key<32 && sym<0xff00) sym = key+96; - - else if ( Fl::e_state&FL_ALT && sym<0xff00) {// find the keycap of this key + else if ( Fl::e_state&FL_ALT && sym<0xff00) { // find the keycap of this key NSString *sim = [theEvent charactersIgnoringModifiers]; UniChar one; CFStringGetCharacters((CFStringRef)sim, CFRangeMake(0, 1), &one); sym = one; - } + } Fl::e_keysym = Fl::e_original_keysym = sym; // Handle FL_KP_Enter on regular keyboards and on Powerbooks if ( maskedKeyCode==0x4c || maskedKeyCode==0x34) key=0x0d; static UInt32 deadKeyState = 0; // must be cleared when losing focus int l = (*keycode_function)(buffer, 31, kind, keyCode, mods, &deadKeyState, keychar, sym); - if(l > 0) { + if (l > 0) { CFStringGetCString((CFStringRef)s, buffer, sizeof(buffer), kCFStringEncodingUTF8); } Fl::e_length = strlen(buffer); Fl::e_text = buffer; buffer[Fl::e_length] = 0; // just in case... - } + } break; default: fl_unlock_function(); @@ -1211,14 +1191,14 @@ OSStatus cocoaKeyboardHandler(NSEvent *theEvent) -/** +/* * Open callback function to call... */ static void (*open_cb)(const char *) = 0; -/** +/* * Install an open documents event handler... */ @interface FLAppleEventHandler : NSObject @@ -1250,7 +1230,7 @@ static void (*open_cb)(const char *) = 0; (*open_cb)(filename); } - } + } // Unlock access to FLTK for all threads... fl_unlock_function(); @@ -1261,10 +1241,10 @@ void fl_open_callback(void (*cb)(const char *)) { static NSAppleEventManager *aeventmgr = nil; static FLAppleEventHandler *handler; fl_open_display(); - if(!aeventmgr) { + if (!aeventmgr) { aeventmgr = [NSAppleEventManager sharedAppleEventManager]; handler = [[FLAppleEventHandler alloc] init]; - } + } open_cb = cb; if (cb) { @@ -1276,7 +1256,7 @@ void fl_open_callback(void (*cb)(const char *)) { } -/** +/* * initialize the Mac toolboxes, dock status, and set the default menubar */ @@ -1310,10 +1290,10 @@ extern "C" { pt.x = 0; pt.y = [[nsw contentView] frame].size.height; pt2 = [nsw convertBaseToScreen:pt]; - window->position(pt2.x, [[nsw screen] frame].size.height - pt2.y); - if([nsw containsGLsubwindow] ) { - [nsw display];//redraw window after moving if it contains OpenGL subwindows - } + window->position((int)pt2.x, (int)([[nsw screen] frame].size.height - pt2.y)); + if ([nsw containsGLsubwindow] ) { + [nsw display];// redraw window after moving if it contains OpenGL subwindows + } } - (void)windowDidResize:(NSNotification *)notif { @@ -1325,7 +1305,10 @@ extern "C" { pt.y = [[nsw contentView] frame].size.height; pt2 = [nsw convertBaseToScreen:pt]; resize_from_system = window; - window->resize(pt2.x, [[nsw screen] frame].size.height - pt2.y, r.size.width, r.size.height); + window->resize((int)pt2.x, + (int)([[nsw screen] frame].size.height - pt2.y), + (int)r.size.width, + (int)r.size.height); } - (void)windowDidBecomeKey:(NSNotification *)notif { @@ -1354,13 +1337,13 @@ extern "C" { - (void)windowWillClose:(NSNotification *)notif { Fl_Window *w = Fl::first_window(); - if(!w) return; + if (!w) return; NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid; - if( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { - if(![cw isKeyWindow]) {//always make Fl::first_window() the key widow + if ( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { + if (![cw isKeyWindow]) { // always make Fl::first_window() the key widow [cw makeKeyAndOrderFront:nil]; } - if(![cw isMainWindow]) {//always make Fl::first_window() the main widow + if (![cw isMainWindow]) { // always make Fl::first_window() the main widow [cw makeMainWindow]; } } @@ -1386,7 +1369,7 @@ static FLDelegate *mydelegate; void fl_open_display() { static char beenHereDoneThat = 0; - if ( !beenHereDoneThat ) { + if ( !beenHereDoneThat ) { beenHereDoneThat = 1; [NSApplication sharedApplication]; @@ -1404,13 +1387,11 @@ void fl_open_display() { // bring the application into foreground without a 'CARB' resource Boolean same_psn; ProcessSerialNumber cur_psn, front_psn; - if( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) && - !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) - { + if ( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) && + !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) { // only transform the application type for unbundled apps CFBundleRef bundle = CFBundleGetMainBundle(); - if( bundle ) - { + if ( bundle ) { FSRef execFs; CFURLRef execUrl = CFBundleCopyExecutableURL( bundle ); CFURLGetFSRef( execUrl, &execFs ); @@ -1418,23 +1399,24 @@ void fl_open_display() { FSRef bundleFs; GetProcessBundleLocation( &cur_psn, &bundleFs ); - if( !FSCompareFSRefs( &execFs, &bundleFs ) ) + if ( !FSCompareFSRefs( &execFs, &bundleFs ) ) bundle = NULL; CFRelease(execUrl); } - keycode_function = keycode_wrap_old; //under Cocoa we always use this one + keycode_function = keycode_wrap_old; // under Cocoa we always use this one /* // imm: keycode handler stub setting - use Gestalt to determine the running system version, - // then set the keycode_function pointer accordingly - if(MACsystemVersion >= 0x1050) { // 10.5.0 or later - keycode_function = keycodeToUnicode; - } - else { - keycode_function = keycode_wrap_old; // pre-10.5 mechanism - }*/ + * // then set the keycode_function pointer accordingly + * if (MACsystemVersion >= 0x1050) { // 10.5.0 or later + * keycode_function = keycodeToUnicode; + * } + * else { + * keycode_function = keycode_wrap_old; // pre-10.5 mechanism + * } + */ - if( !bundle ) + if ( !bundle ) { // Earlier versions of this code tried to use weak linking, however it // appears that this does not work on 10.2. Since 10.3 and higher provide @@ -1460,17 +1442,18 @@ void fl_open_display() { createAppleMenu(); // Install Carbon Event handler for character palette input static EventTypeSpec textEvents[] = { - { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } }; + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } + }; EventHandlerUPP textHandler = NewEventHandlerUPP( carbonTextHandler ); InstallEventHandler(GetEventDispatcherTarget(), textHandler, 1, textEvents, NULL, 0L); } } -/** +/* * get rid of allocated resources */ -void fl_close_display() { +void fl_close_display() { } @@ -1484,7 +1467,7 @@ static void get_window_frame_sizes(int &bx, int &by, int &bt) { bt = outside.size.height - inside.size.height - by; } -/** +/* * smallest x ccordinate in screen space */ int Fl::x() { @@ -1492,7 +1475,7 @@ int Fl::x() { } -/** +/* * smallest y coordinate in screen space */ int Fl::y() { @@ -1502,7 +1485,7 @@ int Fl::y() { } -/** +/* * screen width (single monitor!?) */ int Fl::w() { @@ -1510,7 +1493,7 @@ int Fl::w() { } -/** +/* * screen height (single monitor!?) */ int Fl::h() { @@ -1520,7 +1503,7 @@ int Fl::h() { } -/** +/* * get the current mouse pointer world coordinates */ void Fl::get_mouse(int &x, int &y) @@ -1532,19 +1515,19 @@ void Fl::get_mouse(int &x, int &y) } -/** +/* * convert Mac keystrokes to FLTK */ /* - unsigned short mac2fltk(ulong macKey) - { - unsigned short cc = macKeyLookUp[(macKey>>8)&0x7f]; - if (cc) return cc; - return macKey&0xff; - } + * unsigned short mac2fltk(ulong macKey) + * { + * unsigned short cc = macKeyLookUp[(macKey>>8)&0x7f]; + * if (cc) return cc; + * return macKey&0xff; + * } */ -/** +/* * Initialize the given port for redraw and call the window's flush() to actually draw the content */ void Fl_X::flush() @@ -1552,15 +1535,14 @@ void Fl_X::flush() w->flush(); if (fl_gc) { CGContextFlush(fl_gc); - if(viewWithLockedFocus) { - [viewWithLockedFocus unlockFocus]; - viewWithLockedFocus = nil; + if (viewWithLockedFocus) { + [viewWithLockedFocus unlockFocus]; + viewWithLockedFocus = nil; } - } + } } - -/** +/* * Gets called when a window is created, resized, or deminiaturized */ static void handleUpdateEvent( Fl_Window *window ) @@ -1571,29 +1553,28 @@ static void handleUpdateEvent( Fl_Window *window ) // FIXME: Matt: this is in the Carbon version. Does it need to be here? /* - //I don't think so (MG). This function gets called only when a full - //redraw is needed (creation, resize, deminiaturization) - //and later in it we set damages to DAMAGE_ALL, so there is no - //point in limiting redraw to i->region - if ( i->xid && window->damage() ) { - NSView *view = [(NSWindow*)i->xid contentView]; - if ( view && i->region ) { - int ix; - Fl_Region rgn = i->region; - for (ix=0; ix<rgn->count; ix++) { - NSRect rect = NSRectFromCGRect(rgn->rects[ix]); - [view setNeedsDisplayInRect:rect]; - } - } - } - */ + * // I don't think so (MG). This function gets called only when a full + * // redraw is needed (creation, resize, deminiaturization) + * // and later in it we set damages to DAMAGE_ALL, so there is no + * // point in limiting redraw to i->region + * if ( i->xid && window->damage() ) { + * NSView *view = [(NSWindow*)i->xid contentView]; + * if ( view && i->region ) { + * int ix; + * Fl_Region rgn = i->region; + * for (ix=0; ix<rgn->count; ix++) { + * NSRect rect = NSRectFromCGRect(rgn->rects[ix]); + * [view setNeedsDisplayInRect:rect]; + * } + * } + * } + */ if ( i->region ) { XDestroyRegion(i->region); i->region = 0; } - for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) - { + for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { if ( cx->region ) { XDestroyRegion(cx->region); cx->region = 0; @@ -1619,7 +1600,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) get_window_frame_sizes(bx, by, bt); } } - //The coordinates of the whole window, including non-client area + // The coordinates of the whole window, including non-client area xoff = bx; yoff = by + bt; dx = 2*bx; @@ -1629,7 +1610,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) W = w->w()+dx; H = w->h()+dy; - //Proceed to positioning the window fully inside the screen, if possible + // Proceed to positioning the window fully inside the screen, if possible // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk // that we want to avoid when positioning our window, namely the Dock and the @@ -1649,7 +1630,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) && cy >= r.origin.y && cy <= r.origin.y + r.size.height) break; } - if(i < count) gd = [a objectAtIndex:i]; + if (i < count) gd = [a objectAtIndex:i]; // if the center doesn't fall on a screen, try the top left if (!gd) { @@ -1659,7 +1640,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) break; } - if(i < count) gd = [a objectAtIndex:i]; + if (i < count) gd = [a objectAtIndex:i]; } // if that doesn't fall on a screen, try the top right if (!gd) { @@ -1669,7 +1650,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) break; } - if(i < count) gd = [a objectAtIndex:i]; + if (i < count) gd = [a objectAtIndex:i]; } // if that doesn't fall on a screen, try the bottom left if (!gd) { @@ -1679,7 +1660,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) break; } - if(i < count) gd = [a objectAtIndex:i]; + if (i < count) gd = [a objectAtIndex:i]; } // last resort, try the bottom right if (!gd) { @@ -1689,7 +1670,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) break; } - if(i < count) gd = [a objectAtIndex:i]; + if (i < count) gd = [a objectAtIndex:i]; } // if we still have not found a screen, we will use the main // screen, the one that has the application menu bar. @@ -1703,7 +1684,7 @@ int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) if ( Y < sh - (r.origin.y + r.size.height) ) Y = sh - (r.origin.y + r.size.height); } - //Return the client area's top left corner in (X,Y) + // Return the client area's top left corner in (X,Y) X+=xoff; Y+=yoff; @@ -1725,7 +1706,7 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { - (void)drawRect:(NSRect)rect; - (BOOL)acceptsFirstResponder; - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; -//- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; +- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; - (void)mouseUp:(NSEvent *)theEvent; - (void)rightMouseUp:(NSEvent *)theEvent; - (void)otherMouseUp:(NSEvent *)theEvent; @@ -1755,12 +1736,11 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { { return YES; } -/* - (BOOL)performKeyEquivalent:(NSEvent*)theEvent { OSStatus err = cocoaKeyboardHandler(theEvent); return (err ? NO : YES); -}*/ +} - (BOOL)acceptsFirstMouse:(NSEvent*)theEvent { Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; @@ -1837,8 +1817,8 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { return NO; } NSPasteboard *pboard; - //NSDragOperation sourceDragMask; - //sourceDragMask = [sender draggingSourceOperationMask]; + // NSDragOperation sourceDragMask; + // sourceDragMask = [sender draggingSourceOperationMask]; pboard = [sender draggingPasteboard]; NSPoint pt = [sender draggingLocation]; Fl::e_x = pt.x; @@ -1846,22 +1826,22 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { Fl::e_x_root = [[self window] frame].origin.x + Fl::e_x; Fl::e_y_root = [[self window] frame].origin.y + pt.y; Fl::e_y_root = [[[self window] screen] frame].size.height - Fl::e_y_root; - if(DragData) { free(DragData); DragData = NULL; } + if (DragData) { free(DragData); DragData = NULL; } if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { - CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType]; - CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); - int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8); - DragData = (char *)malloc(l + 1); - CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); - CFRelease(all); - } + CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType]; + CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); + int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8); + DragData = (char *)malloc(l + 1); + CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); + CFRelease(all); + } else if ( [[pboard types] containsObject:NSStringPboardType] ) { - NSData *data = [pboard dataForType:NSStringPboardType]; - DragData = (char *)malloc([data length] + 1); - [data getBytes:DragData]; - DragData[[data length]] = 0; - convert_crlf(DragData, strlen(DragData)); - } + NSData *data = [pboard dataForType:NSStringPboardType]; + DragData = (char *)malloc([data length] + 1); + [data getBytes:DragData]; + DragData[[data length]] = 0; + convert_crlf(DragData, strlen(DragData)); + } else { breakMacEventLoop(); return NO; @@ -1871,7 +1851,7 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { int old_event = Fl::e_number; Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); Fl::e_number = old_event; - if(DragData) { free(DragData); DragData = NULL; } + if (DragData) { free(DragData); DragData = NULL; } Fl::e_text = NULL; Fl::e_length = 0; fl_dnd_target_window = NULL; @@ -1880,8 +1860,7 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { } - (void)draggingExited:(id < NSDraggingInfo >)sender { - if ( fl_dnd_target_window ) - { + if ( fl_dnd_target_window ) { Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); fl_dnd_target_window = 0; } @@ -1893,15 +1872,14 @@ static void q_set_window_title(NSWindow *nsw, const char * name ) { @end -/** +/* * go ahead, create that (sub)window * \todo we should make menu windows slightly transparent for the new Mac look */ void Fl_X::make(Fl_Window* w) { static int xyPos = 100; - if ( w->parent() ) // create a subwindow - { + if ( w->parent() ) { // create a subwindow Fl_Group::current(0); Rect wRect; wRect.top = w->y(); @@ -1914,50 +1892,52 @@ void Fl_X::make(Fl_Window* w) x->region = 0; x->subRegion = 0; x->cursor = fl_default_cursor; - x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz + x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz Fl_Window *win = w->window(); Fl_X *xo = Fl_X::i(win); if (xo) { x->xidNext = xo->xidChildren; x->xidChildren = 0L; xo->xidChildren = x; - x->xid = win->i->xid; + x->xid = win->i->xid; x->w = w; w->i = x; x->wait_for_expose = 0; - Fl_X *z = xo->next; //we don't want a subwindow in Fl_X::first - xo->next = x; - x->next = z; + { + Fl_X *z = xo->next; // we don't want a subwindow in Fl_X::first + xo->next = x; + x->next = z; + } int old_event = Fl::e_number; w->handle(Fl::e_number = FL_SHOW); Fl::e_number = old_event; - w->redraw(); // force draw to happen + w->redraw(); // force draw to happen } fl_show_iconic = 0; } - else // create a desktop window - { + else { // create a desktop window NSAutoreleasePool *localPool; localPool = [[NSAutoreleasePool alloc] init]; Fl_Group::current(0); fl_open_display(); NSInteger winlevel = NSNormalWindowLevel; NSUInteger winstyle; - if(w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + if (w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; else winstyle = NSBorderlessWindowMask; int xp = w->x(); int yp = w->y(); int wp = w->w(); int hp = w->h(); if (w->size_range_set) { - if ( w->minh != w->maxh || w->minw != w->maxw) + if ( w->minh != w->maxh || w->minw != w->maxw) { winstyle |= NSResizableWindowMask; + } } else { if (w->resizable()) { Fl_Widget *o = w->resizable(); int minw = o->w(); if (minw > 100) minw = 100; int minh = o->h(); if (minh > 100) minh = 100; w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); - winstyle |= NSResizableWindowMask; + winstyle |= NSResizableWindowMask; } else { w->size_range(w->w(), w->h(), w->w(), w->h()); } @@ -1974,7 +1954,7 @@ void Fl_X::make(Fl_Window* w) } } else if (w->modal()) { winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask); - // winlevel = NSModalPanelWindowLevel; + // winlevel = NSModalPanelWindowLevel; } else if (w->non_modal()) { winlevel = NSFloatingWindowLevel; @@ -2040,8 +2020,7 @@ void Fl_X::make(Fl_Window* w) [cw setLevel:winlevel]; q_set_window_title(cw, name); - if (!(w->flags() & Fl_Window::FORCE_POSITION)) - { + if (!(w->flags() & Fl_Window::FORCE_POSITION)) { if (w->modal()) { [cw center]; } else if (w->non_modal()) { @@ -2058,8 +2037,7 @@ void Fl_X::make(Fl_Window* w) // Install DnD handlers [myview registerForDraggedTypes:[NSArray arrayWithObjects: NSStringPboardType, NSFilenamesPboardType, nil]]; - if ( ! Fl_X::first->next ) // if this is the first window, we need to bring the application to the front - { + if ( ! Fl_X::first->next ) { // if this is the first window, we need to bring the application to the front ProcessSerialNumber psn; OSErr err = GetCurrentProcess( &psn ); if ( err==noErr ) SetFrontProcess( &psn ); @@ -2067,7 +2045,7 @@ void Fl_X::make(Fl_Window* w) if (w->size_range_set) w->size_range_(); - if(winlevel != NSMainMenuWindowLevel) { + if (winlevel != NSMainMenuWindowLevel) { Fl_Tooltip::enter(0); } [cw makeKeyAndOrderFront:nil]; @@ -2098,7 +2076,7 @@ void Fl_X::make(Fl_Window* w) } -/** +/* * Tell the OS what window sizes we want to allow */ void Fl_Window::size_range_() { @@ -2108,35 +2086,34 @@ void Fl_Window::size_range_() { NSSize minSize = { minw, minh + bt }; NSSize maxSize = { maxw?maxw:32000, maxh?maxh + bt:32000 }; if (i && i->xid) { - [(NSWindow*)i->xid setMinSize:minSize]; - [(NSWindow*)i->xid setMaxSize:maxSize]; - } + [(NSWindow*)i->xid setMinSize:minSize]; + [(NSWindow*)i->xid setMaxSize:maxSize]; + } } -/** +/* * returns pointer to the filename, or null if name ends with ':' */ const char *fl_filename_name( const char *name ) { const char *p, *q; if (!name) return (0); - for ( p = q = name ; *p ; ) - { - if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) - { + for ( p = q = name ; *p ; ) { + if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) { q = p+2; p++; } - else if (p[0] == '/') + else if (p[0] == '/') { q = p + 1; + } p++; } return q; } -/** +/* * set the window title bar * \todo make the titlebar icon work! */ @@ -2148,7 +2125,7 @@ void Fl_Window::label(const char *name,const char */*iname*/) { } -/** +/* * make a window visible */ void Fl_Window::show() { @@ -2163,27 +2140,25 @@ void Fl_Window::show() { if (!shown() || !i) { Fl_X::make(this); } else { - if ( !parent() ) - { - if([(NSWindow*)i->xid isMiniaturized]) { - i->w->redraw(); - [(NSWindow*)i->xid deminiaturize:nil]; - } - - if (!fl_capture) { - [(NSWindow*)i->xid makeKeyAndOrderFront:nil]; + if ( !parent() ) { + if ([(NSWindow*)i->xid isMiniaturized]) { + i->w->redraw(); + [(NSWindow*)i->xid deminiaturize:nil]; } - } + if (!fl_capture) { + [(NSWindow*)i->xid makeKeyAndOrderFront:nil]; + } + } } } -/** +/* * resize a window */ void Fl_Window::resize(int X,int Y,int W,int H) { int bx, by, bt; - if( ! this->border() ) bt = 0; + if ( ! this->border() ) bt = 0; else get_window_frame_sizes(bx, by, bt); if (W<=0) W = 1; // OS X does not like zero width windows if (H<=0) H = 1; @@ -2228,7 +2203,7 @@ void Fl_Window::resize(int X,int Y,int W,int H) { } -/** +/* * make all drawing go into this window (called by subclass flush() impl.) */ void Fl_Window::make_current() @@ -2239,8 +2214,7 @@ void Fl_Window::make_current() int xp = 0, yp = 0; Fl_Window *win = this; - while ( win ) - { + while ( win ) { if ( !win->window() ) break; xp += win->x(); @@ -2249,29 +2223,27 @@ void Fl_Window::make_current() } viewWithLockedFocus = [(NSWindow*)i->xid contentView]; - [viewWithLockedFocus lockFocusIfCanDraw];//important + [viewWithLockedFocus lockFocusIfCanDraw];// important i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; fl_gc = i->gc; if ( fl_window_region ) XDestroyRegion(fl_window_region); - if(this->window()) { + if (this->window()) { fl_window_region = XRectangleRegion(0,0,w(),h()); - } - else { + } else { fl_window_region = XRectangleRegion(0, 0, w(), h()); - for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) - {//clip-out all sub-windows - Fl_Window *cw = cx->w; - Fl_Region from = fl_window_region; - fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() ); - XDestroyRegion(from); - } + for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) { // clip-out all sub-windows + Fl_Window *cw = cx->w; + Fl_Region from = fl_window_region; + fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() ); + XDestroyRegion(from); + } } - //antialiasing must be deactivated because it applies to rectangles too - //and escapes even clipping!!! - //it gets activated when needed (e.g., draw text) + // antialiasing must be deactivated because it applies to rectangles too + // and escapes even clipping!!! + // it gets activated when needed (e.g., draw text) CGContextSetShouldAntialias(fl_gc, false); - //this is the native view context with origin at bottom left + // this is the native view context with origin at bottom left CGContextSaveGState(fl_gc); #if defined(USE_CAIRO) if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately @@ -2296,7 +2268,7 @@ void Fl_X::q_fill_context() { if (!fl_gc) return; int hgt = 0; if (fl_window) { - hgt = [[(NSWindow*)Fl_X::i(Fl_Window::current_)->xid contentView] frame].size.height; + hgt = [[(NSWindow*)Fl_X::i(Fl_Window::current_)->xid contentView] frame].size.height; } else { hgt = CGBitmapContextGetHeight(fl_gc); } @@ -2305,13 +2277,13 @@ void Fl_X::q_fill_context() { fl_font(fl_fontsize); fl_color(fl_color_); fl_quartz_restore_line_style_(); - if (fl_window) {//translate to subwindow origin if this is a subwindow context + if (fl_window) { // translate to subwindow origin if this is a subwindow context Fl_Window *w = Fl_Window::current_; while(w && w->window()) { CGContextTranslateCTM(fl_gc, w->x(), w->y()); w = w->window(); - } - } + } + } } // The only way to reset clipping to its original state is to pop the current graphics @@ -2326,7 +2298,7 @@ void Fl_X::q_clear_clipping() { void Fl_X::q_release_context(Fl_X *x) { if (x && x->gc!=fl_gc) return; if (!fl_gc) return; - CGContextRestoreGState(fl_gc); //matches the CGContextSaveGState of make_current + CGContextRestoreGState(fl_gc); // matches the CGContextSaveGState of make_current fl_gc = 0; #if defined(USE_CAIRO) if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately @@ -2387,7 +2359,7 @@ static void allocatePasteboard() { } -/** +/* * create a selection * owner: widget that created the selection * stuff: pointer to selected data @@ -2430,7 +2402,7 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { ItemCount nFlavor = 0, i, j; err = PasteboardGetItemCount(myPasteboard, &nFlavor); if (err==noErr) { - for (i=1; i<=nFlavor; i++) { + for (i=1; i<=nFlavor; i++) { PasteboardItemID itemID = 0; CFArrayRef flavorTypeArray = NULL; found = false; @@ -2447,18 +2419,18 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { CFStringRef flavorType = (CFStringRef)CFArrayGetValueAtIndex(flavorTypeArray, flavorIndex); if (UTTypeConformsTo(flavorType, flavorNames[j])) { err = PasteboardCopyItemFlavorData( myPasteboard, itemID, flavorNames[j], &flavorData ); - if(err != noErr) continue; + if (err != noErr) continue; encoding = encodings[j]; found = true; break; } } - if(found) break; + if (found) break; } if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;} if (found) break; - } - if(found) { + } + if (found) { CFIndex len = CFDataGetLength(flavorData); CFStringRef mycfs = CFStringCreateWithBytes(NULL, CFDataGetBytePtr(flavorData), len, encoding, false); CFRelease(flavorData); @@ -2473,7 +2445,7 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { len = strlen(fl_selection_buffer[1]); fl_selection_length[1] = len; convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n: - } + } } } Fl::e_text = fl_selection_buffer[clipboard]; @@ -2485,7 +2457,7 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) { void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) { // check, if this timer slot exists already - for (int i = 0; i < mac_timer_used; ++i) { + for (int i = 0; i < mac_timer_used; ++i) { MacTimeout& t = mac_timers[i]; // if so, simply change the fire interval if (t.callback == cb && t.data == data) { @@ -2497,7 +2469,7 @@ void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) // no existing timer to use. Create a new one: int timer_id = -1; // find an empty slot in the timer array - for (int i = 0; i < mac_timer_used; ++i) { + for (int i = 0; i < mac_timer_used; ++i) { if ( !mac_timers[i].timer ) { timer_id = i; break; @@ -2539,7 +2511,7 @@ void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) int Fl::has_timeout(Fl_Timeout_Handler cb, void* data) { - for (int i = 0; i < mac_timer_used; ++i) { + for (int i = 0; i < mac_timer_used; ++i) { MacTimeout& t = mac_timers[i]; if (t.callback == cb && t.data == data && t.pending) { return 1; @@ -2550,7 +2522,7 @@ int Fl::has_timeout(Fl_Timeout_Handler cb, void* data) void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) { - for (int i = 0; i < mac_timer_used; ++i) { + for (int i = 0; i < mac_timer_used; ++i) { MacTimeout& t = mac_timers[i]; if (t.callback == cb && ( t.data == data || data == NULL)) { delete_timer(t); @@ -2603,13 +2575,13 @@ void MacDestroyWindow(Fl_Window *w, void *p) { if (w && !w->parent() && p) { [[(NSWindow *)p contentView] release]; [(NSWindow *)p close]; - } + } } void MacMapWindow(Fl_Window *w, void *p) { if (w && p) { [(NSWindow *)p orderFront:nil]; - } + } //+ link to window list if (w && w->parent()) { MacRelinkWindow(Fl_X::i(w), Fl_X::i(w->window())); @@ -2620,7 +2592,7 @@ void MacMapWindow(Fl_Window *w, void *p) { void MacUnmapWindow(Fl_Window *w, void *p) { if (w && !w->parent() && p) { [(NSWindow *)p orderOut:nil]; - } + } if (w && Fl_X::i(w)) MacUnlinkWindow(Fl_X::i(w)); } @@ -2636,38 +2608,38 @@ Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h) for( int i = 0; i < r->count; i++) { CGRect A = r->rects[i]; CGRect test = CGRectIntersection(A, rect); - if(CGRectIsEmpty(test)) { + if (CGRectIsEmpty(test)) { outr->rects[(outr->count)++] = A; - } + } else { const CGFloat verylarge = 100000.; - CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);//W side + CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);// W side test = CGRectIntersection(A, side); - if( ! CGRectIsEmpty(test)) { + if ( ! CGRectIsEmpty(test)) { outr->rects[(outr->count)++] = test; - } - side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);//N side + } + side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);// N side test = CGRectIntersection(A, side); - if( ! CGRectIsEmpty(test)) { + if ( ! CGRectIsEmpty(test)) { outr->rects[(outr->count)++] = test; - } - side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);//E side + } + side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);// E side test = CGRectIntersection(A, side); - if( ! CGRectIsEmpty(test)) { + if ( ! CGRectIsEmpty(test)) { outr->rects[(outr->count)++] = test; - } - side = CGRectMake(0, 0, verylarge, rect.origin.y);//S side + } + side = CGRectMake(0, 0, verylarge, rect.origin.y);// S side test = CGRectIntersection(A, side); - if( ! CGRectIsEmpty(test)) { + if ( ! CGRectIsEmpty(test)) { outr->rects[(outr->count)++] = test; - } } } - if(outr->count == 0) { + } + if (outr->count == 0) { free(outr->rects); free(outr); outr = XRectangleRegion(0,0,0,0); - } + } else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); return outr; } @@ -2676,23 +2648,23 @@ Fl_Region MacRectRegionIntersect(Fl_Region current, int x,int y,int w, int h) /* intersects current and x,y,w,h rectangle and returns result as a new Fl_Region */ { - if(current == NULL) return XRectangleRegion(x,y,w,h); + if (current == NULL) return XRectangleRegion(x,y,w,h); CGRect r = CGRectMake(x, y, w - 1, h - 1); Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); outr->count = current->count; outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect)); int j = 0; for(int i = 0; i < current->count; i++) { - CGRect test = CGRectIntersection(current->rects[i], r); - if(!CGRectIsEmpty(test)) outr->rects[j++] = test; + CGRect test = CGRectIntersection(current->rects[i], r); + if (!CGRectIsEmpty(test)) outr->rects[j++] = test; } - if(j) { - outr->count = j; - outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); + if (j) { + outr->count = j; + outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); } else { - XDestroyRegion(outr); - outr = XRectangleRegion(0,0,0,0); + XDestroyRegion(outr); + outr = XRectangleRegion(0,0,0,0); } return outr; } @@ -2703,7 +2675,7 @@ void MacCollapseWindow(Window w) } static NSImage *CGBitmapContextToNSImage(CGContextRef c) -//the returned NSImage is autoreleased +// the returned NSImage is autoreleased { unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c); NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata @@ -2723,7 +2695,7 @@ static NSImage *CGBitmapContextToNSImage(CGContextRef c) static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() ) { - if(cursor == nil) { + if (cursor == nil) { CGContextRef c = f(); NSImage *image = CGBitmapContextToNSImage(c); fl_delete_offscreen( (Fl_Offscreen)c ); @@ -2794,14 +2766,14 @@ int MACscreen_init(XRectangle screens[]) NSRect r; int i, num_screens = 0; for( i = 0; i < count; i++) { - r = [[a objectAtIndex:i] frame]; + r = [[a objectAtIndex:i] frame]; screens[num_screens].x = r.origin.x; screens[num_screens].y = r.size.height - (r.origin.y + r.size.height); screens[num_screens].width = r.size.width; screens[num_screens].height = r.size.height; num_screens ++; if (num_screens >= 16) break; - } + } [localPool release]; return num_screens; } @@ -2827,7 +2799,7 @@ static NSMenu *appleMenu; static void createAppleMenu(void) { static BOOL donethat = NO; - if(donethat) return; + if (donethat) return; donethat = YES; NSMenu *mainmenu, *services; NSMenuItem *menuItem; @@ -2845,7 +2817,7 @@ static void createAppleMenu(void) [[appleMenu itemAtIndex:0] setTarget:about]; [appleMenu addItem:[NSMenuItem separatorItem]]; // Services Menu - services = [[[NSMenu alloc] init] autorelease]; + services = [[NSMenu alloc] init]; [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]]; // Hide AppName @@ -2866,7 +2838,7 @@ static void createAppleMenu(void) [menuItem setSubmenu:appleMenu]; mainmenu = [[NSMenu alloc] initWithTitle:@""]; [mainmenu addItem:menuItem]; - if(MACsystemVersion < 0x1060) { + if (MACsystemVersion < 0x1060) { // [NSApp setAppleMenu:appleMenu]; // to avoid compiler warning raised by use of undocumented setAppleMenu : [NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu]; @@ -2892,12 +2864,12 @@ static void createAppleMenu(void) int flRank = [self tag]; const Fl_Menu_Item *items = fl_sys_menu_bar->Fl_Menu_::menu(); const Fl_Menu_Item *item = items + flRank; - if(item) { + if (item) { fl_sys_menu_bar->picked(item); - if ( item->flags & FL_MENU_TOGGLE ) {// update the menu toggle symbol + if ( item->flags & FL_MENU_TOGGLE ) { // update the menu toggle symbol [self setState:(item->value() ? NSOnState : NSOffState)]; } - else if ( item->flags & FL_MENU_RADIO ) {// update the menu radio symbols + else if ( item->flags & FL_MENU_RADIO ) { // update the menu radio symbols int from = flRank; while( from > 0 && items[from - 1].label() && (items[from - 1].flags & FL_MENU_RADIO) && !(items[from - 1].flags & FL_MENU_DIVIDER) ) { @@ -2912,7 +2884,7 @@ static void createAppleMenu(void) int nsrank = (int)[nsmenu indexOfItem:self]; for(int i = from - flRank + nsrank ; i <= to - flRank + nsrank; i++) { NSMenuItem *nsitem = [nsmenu itemAtIndex:i]; - if(nsitem != self) [nsitem setState:NSOffState]; + if (nsitem != self) [nsitem setState:NSOffState]; else [nsitem setState:(item->value() ? NSOnState : NSOffState) ]; } } @@ -2921,7 +2893,7 @@ static void createAppleMenu(void) - (void) directCallback:(id)unused { Fl_Menu_Item *item = (Fl_Menu_Item *)[(NSData*)[self representedObject] bytes]; - if( item && item->callback() ) item->do_callback(NULL); + if ( item && item->callback() ) item->do_callback(NULL); } @end @@ -2952,10 +2924,10 @@ void fl_mac_set_about( Fl_Callback *cb, void *user_data, int shortcut) FLMenuItem *item = [[[FLMenuItem alloc] initWithTitle:(NSString*)cfname action:@selector(directCallback:) keyEquivalent:@""] autorelease]; - if(aboutItem.shortcut()) { + if (aboutItem.shortcut()) { Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalent, item, aboutItem.shortcut() & 0xff); Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask, item, aboutItem.shortcut() ); - } + } NSData *pointer = [NSData dataWithBytes:&aboutItem length:sizeof(Fl_Menu_Item)]; [item setRepresentedObject:pointer]; [appleMenu insertItem:item atIndex:0]; @@ -2998,12 +2970,12 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio va_list ap; va_start(ap, operation); - if(operation == Fl_Sys_Menu_Bar::itemAtIndex) {//arguments: NSMenu*, int. Returns the item + if (operation == Fl_Sys_Menu_Bar::itemAtIndex) { // arguments: NSMenu*, int. Returns the item menu = va_arg(ap, NSMenu*); value = va_arg(ap, int); retval = (void *)[menu itemAtIndex:value]; } - else if(operation == Fl_Sys_Menu_Bar::setKeyEquivalent) {//arguments: NSMenuItem*, int + else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalent) { // arguments: NSMenuItem*, int item = va_arg(ap, NSMenuItem*); value = va_arg(ap, int); char key = value; @@ -3011,7 +2983,7 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio [item setKeyEquivalent:equiv]; [equiv release]; } - else if(operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) {//arguments: NSMenuItem*, int + else if (operation == Fl_Sys_Menu_Bar::setKeyEquivalentModifierMask) { // arguments: NSMenuItem*, int item = va_arg(ap, NSMenuItem*); value = va_arg(ap, int); NSUInteger macMod = 0; @@ -3021,13 +2993,13 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio if ( value & FL_CTRL ) macMod |= NSControlKeyMask; [item setKeyEquivalentModifierMask:macMod]; } - else if(operation == Fl_Sys_Menu_Bar::setState) {//arguments: NSMenuItem*, int + else if (operation == Fl_Sys_Menu_Bar::setState) { // arguments: NSMenuItem*, int item = va_arg(ap, NSMenuItem*); value = va_arg(ap, int); [item setState:(value ? NSOnState : NSOffState)]; } - else if(operation == Fl_Sys_Menu_Bar::initWithTitle) {//arguments: const char*title. Returns the newly created menu - //creates a new (sub)menu + else if (operation == Fl_Sys_Menu_Bar::initWithTitle) { // arguments: const char*title. Returns the newly created menu + // creates a new (sub)menu char *ts = remove_ampersand(va_arg(ap, char *)); CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); free(ts); @@ -3036,29 +3008,29 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio [menu setAutoenablesItems:NO]; retval = (void *)menu; } - else if(operation == Fl_Sys_Menu_Bar::numberOfItems) {//arguments: NSMenu *menu, int *pcount - //upon return, *pcount is set to menu's item count + else if (operation == Fl_Sys_Menu_Bar::numberOfItems) { // arguments: NSMenu *menu, int *pcount + // upon return, *pcount is set to menu's item count menu = va_arg(ap, NSMenu*); pter = va_arg(ap, void *); *(int*)pter = [menu numberOfItems]; } - else if(operation == Fl_Sys_Menu_Bar::setSubmenu) {//arguments: NSMenuItem *item, NSMenu *menu - //sets 'menu' as submenu attached to 'item' + else if (operation == Fl_Sys_Menu_Bar::setSubmenu) { // arguments: NSMenuItem *item, NSMenu *menu + // sets 'menu' as submenu attached to 'item' item = va_arg(ap, NSMenuItem*); menu = va_arg(ap, NSMenu*); [item setSubmenu:menu]; [menu release]; } - else if(operation == Fl_Sys_Menu_Bar::setEnabled) {//arguments: NSMenuItem*, int + else if (operation == Fl_Sys_Menu_Bar::setEnabled) { // arguments: NSMenuItem*, int item = va_arg(ap, NSMenuItem*); value = va_arg(ap, int); [item setEnabled:(value ? YES : NO)]; } - else if(operation == Fl_Sys_Menu_Bar::addSeparatorItem) {//arguments: NSMenu* + else if (operation == Fl_Sys_Menu_Bar::addSeparatorItem) { // arguments: NSMenu* menu = va_arg(ap, NSMenu*); [menu addItem:[NSMenuItem separatorItem]]; } - else if(operation == Fl_Sys_Menu_Bar::setTitle) {//arguments: NSMenuItem*, const char * + else if (operation == Fl_Sys_Menu_Bar::setTitle) { // arguments: NSMenuItem*, const char * item = va_arg(ap, NSMenuItem*); char *ts = remove_ampersand(va_arg(ap, char *)); CFStringRef title = CFStringCreateWithCString(NULL, ts, kCFStringEncodingUTF8); @@ -3066,15 +3038,15 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio [item setTitle:(NSString*)title]; CFRelease(title); } - else if(operation == Fl_Sys_Menu_Bar::removeItem) {//arguments: NSMenu*, int + else if (operation == Fl_Sys_Menu_Bar::removeItem) { // arguments: NSMenu*, int menu = va_arg(ap, NSMenu*); value = va_arg(ap, int); [menu removeItem:[menu itemAtIndex:value]]; } - else if(operation == Fl_Sys_Menu_Bar::addNewItem) {//arguments: NSMenu *menu, int flrank, int *prank - //creates a new menu item at the end of 'menu' - //attaches the item of rank flrank (counted in Fl_Menu_) of fl_sys_menu_bar to it - //upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL + else if (operation == Fl_Sys_Menu_Bar::addNewItem) { // arguments: NSMenu *menu, int flrank, int *prank + // creates a new menu item at the end of 'menu' + // attaches the item of rank flrank (counted in Fl_Menu_) of fl_sys_menu_bar to it + // upon return, puts the rank (counted in NSMenu) of the new item in *prank unless prank is NULL menu = va_arg(ap, NSMenu*); int flRank = va_arg(ap, int); char *name = remove_ampersand( (fl_sys_menu_bar->Fl_Menu_::menu() + flRank)->label()); @@ -3088,27 +3060,27 @@ void *Fl_Sys_Menu_Bar::doMenuOrItemOperation(Fl_Sys_Menu_Bar::menuOrItemOperatio [menu addItem:item]; CFRelease(cfname); [item setTarget:item]; - if(prank != NULL) *prank = [menu indexOfItem:item]; + if (prank != NULL) *prank = [menu indexOfItem:item]; [item release]; - } - else if(operation == Fl_Sys_Menu_Bar::renameItem) {//arguments: int rank, const char *newname - //renames the system menu item numbered rank in fl_sys_menu_bar->menu() + } + else if (operation == Fl_Sys_Menu_Bar::renameItem) { // arguments: int rank, const char *newname + // renames the system menu item numbered rank in fl_sys_menu_bar->menu() int rank = va_arg(ap, int); char *newname = remove_ampersand( va_arg(ap, const char *) ); int countmenus = [(NSMenu*)fl_system_menu numberOfItems]; bool found = NO; - NSMenuItem *macitem; + NSMenuItem *macitem = 0; for(int i = 1; (!found) && i < countmenus; i++) { NSMenuItem *item = [(NSMenu*)fl_system_menu itemAtIndex:i]; NSMenu *submenu = [item submenu]; - if(submenu == nil) continue; + if (submenu == nil) continue; int countitems = [submenu numberOfItems]; for(int j = 0; j < countitems; j++) { macitem = [submenu itemAtIndex:j]; - if([macitem tag] == rank) { found = YES; break; } + if ([macitem tag] == rank) { found = YES; break; } } } - if(found) { + if (found) { [macitem setTitle:[[[NSString alloc] initWithUTF8String:newname] autorelease]]; } free(newname); @@ -3133,13 +3105,13 @@ static NSImage *imageFromText(const char *text, int *pwidth, int *pheight) while((q=strchr(p, '\n')) != NULL) { nl++; w2 = fl_width(p, q - p); - if(w2 > width) width = w2; + if (w2 > width) width = w2; p = q + 1; } - if(text[ ltext - 1] != '\n') { + if (text[ ltext - 1] != '\n') { nl++; w2 = fl_width(p); - if(w2 > width) width = w2; + if (w2 > width) width = w2; } height = nl * fl_height() + 3; width += 6; @@ -3152,8 +3124,9 @@ static NSImage *imageFromText(const char *text, int *pwidth, int *pheight) int y = fl_height(); while(TRUE) { q = strchr(p, '\n'); - if(q) fl_draw(p, q - p, 3, y); - else { + if (q) { + fl_draw(p, q - p, 3, y); + } else { fl_draw(p, 3, y); break; } @@ -3198,20 +3171,22 @@ int MACpreparedrag(void) CFRelease(text); Fl_Widget *w = Fl::pushed(); Fl_Window *win = w->window(); - if(win == NULL) { win = (Fl_Window*)w; } - else { while(win->window()) win = win->window(); } + if (win == NULL) { + win = (Fl_Window*)w; + } else { + while(win->window()) win = win->window(); + } NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView]; NSEvent *theEvent = [NSApp currentEvent]; int width, height; NSImage *image; - if( dynamic_cast<Fl_Input_*>(w) != NULL) { + if ( dynamic_cast<Fl_Input_*>(w) != NULL) { fl_selection_buffer[0][ fl_selection_length[0] ] = 0; image = imageFromText(fl_selection_buffer[0], &width, &height); - } - else { + } else { image = defaultDragImage(&width, &height); - } + } static NSSize offset={0,0}; NSPoint pt = [theEvent locationInWindow]; @@ -3220,8 +3195,7 @@ int MACpreparedrag(void) [myview dragImage:image at:pt offset:offset event:theEvent pasteboard:mypasteboard source:myview slideBack:YES]; - if ( w ) - { + if ( w ) { int old_event = Fl::e_number; w->handle(Fl::e_number = FL_RELEASE); Fl::e_number = old_event; @@ -3232,20 +3206,20 @@ int MACpreparedrag(void) } unsigned char *MACbitmapFromRectOfWindow(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel) -//delete the returned pointer after use +// delete the returned pointer after use { while(win->window()) { - x += win->x(); - y += win->y(); - win = win->window(); + x += win->x(); + y += win->y(); + win = win->window(); } NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView]; [myview lockFocus]; CGFloat epsilon = 0; - if(MACsystemVersion >= 0x1060) epsilon = 0.001; - //The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and - //left pixel column are not read, and bitmap is read shifted by one pixel in both directions. - //Under 10.5, we want no offset. + if (MACsystemVersion >= 0x1060) epsilon = 0.001; + // The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and + // left pixel column are not read, and bitmap is read shifted by one pixel in both directions. + // Under 10.5, we want no offset. NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h); NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; [myview unlockFocus]; @@ -3262,7 +3236,7 @@ void imgProviderReleaseData (void *info, const void *data, size_t size) } CGImageRef MAC_CGImageFromRectOfWindow(Fl_Window *win, int x, int y, int w, int h) -//CFRelease the returned CGImageRef after use +// CFRelease the returned CGImageRef after use { int bpp; unsigned char *bitmap = MACbitmapFromRectOfWindow(win, x, y, w, h, &bpp); @@ -3284,10 +3258,10 @@ void MACsetContainsGLsubwindow(Fl_Window *w) WindowRef MACwindowRef(Fl_Window *w) { - return (WindowRef)[(FLWindow*)Fl_X::i(w)->xid windowRef]; + return (WindowRef)[(FLWindow*)Fl_X::i(w)->xid windowRef]; } #endif // FL_DOXYGEN // -// End of "$Id: Fl_mac.cxx 6971 2009-04-13 07:32:01Z matt $". +// End of "$Id: Fl_cocoa.mm 6971 2009-04-13 07:32:01Z matt $". // diff --git a/src/Makefile b/src/Makefile index 0aa6ed1ba..f1ef45132 100644 --- a/src/Makefile +++ b/src/Makefile @@ -63,6 +63,7 @@ CPPFILES = \ Fl_Menu_add.cxx \ Fl_Menu_global.cxx \ Fl_Multi_Label.cxx \ + Fl_Native_File_Chooser.cxx \ Fl_Overlay_Window.cxx \ Fl_Pack.cxx \ Fl_Pixmap.cxx \ @@ -476,6 +477,8 @@ include makedepend # These dependencies aren't part of the makedepend file since # they are part of the WIN32 and MacOS code base... Fl_get_key.o: Fl_get_key_mac.cxx Fl_get_key_win32.cxx +Fl_Native_File_Chooser.o : Fl_Native_File_Chooser_MAC.cxx Fl_Native_File_Chooser_WIN32.cxx \ + Fl_Native_File_Chooser_FLTK.cxx Fl.o: Fl_mac.cxx Fl_win32.cxx Fl_cocoa.mm fl_color.o: fl_color_mac.cxx fl_color_win32.cxx fl_dnd.o: fl_dnd_mac.cxx fl_dnd_win32.cxx fl_dnd_x.cxx @@ -512,6 +515,9 @@ Fl_Image.o: ../FL/mac.H ../FL/win32.H fl_line_style.o: ../FL/mac.H ../FL/win32.H Fl_mac.o: ../FL/mac.H ../FL/win32.H Fl_cocoa.o: ../FL/mac.H ../FL/win32.H +Fl_Native_File_Chooser_MAC.o: ../FL/Fl_Native_File_Chooser.H ../FL/Fl_Native_File_Chooser_MAC.H +Fl_Native_File_Chooser_WIN32.o: ../FL/Fl_Native_File_Chooser.H ../FL/Fl_Native_File_Chooser_WIN32.H +Fl_Native_File_Chooser_FLTK.o: ../FL/Fl_Native_File_Chooser.H ../FL/Fl_Native_File_Chooser_FLTK.H Fl_Menu_Window.o: ../FL/mac.H ../FL/win32.H fl_overlay.o: ../FL/mac.H ../FL/win32.H fl_overlay_visual.o: ../FL/mac.H ../FL/win32.H diff --git a/src/makedepend b/src/makedepend index 39a259f75..7a39ebd35 100644 --- a/src/makedepend +++ b/src/makedepend @@ -215,6 +215,8 @@ Fl_Multi_Label.o: ../FL/fl_types.h ../FL/Xutf8.h ../FL/Enumerations.H Fl_Multi_Label.o: ../FL/Fl_Export.H ../FL/fl_types.h ../FL/Fl_Widget.H Fl_Multi_Label.o: ../FL/Fl_Menu_Item.H ../FL/Fl_Widget.H ../FL/Fl_Image.H Fl_Multi_Label.o: ../FL/Fl_Multi_Label.H +Fl_Native_File_Chooser.o: ../config.h Fl_Native_File_Chooser_common.cxx \ + ../FL/Fl_Native_File_Chooser.H ../FL/Fl.H ../FL/filename.H Fl_Overlay_Window.o: ../config.h ../FL/Fl.H ../FL/fl_utf8.h ../FL/Fl_Export.H Fl_Overlay_Window.o: ../FL/fl_types.h ../FL/Xutf8.h ../FL/Enumerations.H Fl_Overlay_Window.o: ../FL/Fl_Export.H ../FL/fl_types.h diff --git a/src/makefile.wat b/src/makefile.wat index 87ae4d3c3..637474137 100644 --- a/src/makefile.wat +++ b/src/makefile.wat @@ -61,6 +61,7 @@ CPPFILES = & Fl_Menu_add.obj &
Fl_Menu_global.obj &
Fl_Multi_Label.obj &
+ Fl_Native_File_Chooser.obj &
Fl_Overlay_Window.obj &
Fl_Pack.obj &
Fl_Pixmap.obj &
diff --git a/test/Makefile b/test/Makefile index 1f8b91aad..6479e2c61 100644 --- a/test/Makefile +++ b/test/Makefile @@ -74,6 +74,7 @@ CPPFILES =\ menubar.cxx \ message.cxx \ minimum.cxx \ + native-filechooser.cxx \ navigation.cxx \ output.cxx \ overlay.cxx \ @@ -139,6 +140,7 @@ ALL = \ menubar$(EXEEXT) \ message$(EXEEXT) \ minimum$(EXEEXT) \ + native-filechooser$(EXEEXT) \ navigation$(EXEEXT) \ output$(EXEEXT) \ overlay$(EXEEXT) \ @@ -377,6 +379,8 @@ message$(EXEEXT): message.o minimum$(EXEEXT): minimum.o +native-filechooser$(EXEEXT): native-filechooser.o + navigation$(EXEEXT): navigation.o output$(EXEEXT): output.o $(FLLIBNAME) diff --git a/test/makefile.wat b/test/makefile.wat index c1e83b9c2..44d3d4821 100644 --- a/test/makefile.wat +++ b/test/makefile.wat @@ -62,6 +62,7 @@ ALL = & $(ODIR)/menubar$(EXEEXT) &
$(ODIR)/message$(EXEEXT) &
$(ODIR)/minimum$(EXEEXT) &
+ $(ODIR)/native-filechooser$(EXEEXT) &
$(ODIR)/navigation$(EXEEXT) &
$(ODIR)/output$(EXEEXT) &
$(ODIR)/overlay$(EXEEXT) &
@@ -179,6 +180,8 @@ $(ODIR)/message$(EXEEXT) : $(ODIR)/message.obj $(ODIR)/minimum$(EXEEXT) : $(ODIR)/minimum.obj
+$(ODIR)/native-filechooser$(EXEEXT) : $(ODIR)/native-filechooser.obj
+
$(ODIR)/navigation$(EXEEXT) : $(ODIR)/navigation.obj
$(ODIR)/output$(EXEEXT) : $(ODIR)/output.obj
diff --git a/test/native-filechooser.cxx b/test/native-filechooser.cxx new file mode 100644 index 000000000..e4655e6e6 --- /dev/null +++ b/test/native-filechooser.cxx @@ -0,0 +1,83 @@ +// +// "$Id$" +// +// Simple test of the Fl_Native_File_Chooser. +// +// Copyright 1998-2009 by Bill Spitzak and others. +// Copyright 2004 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 report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// +#include <stdio.h> +#include <FL/Fl.H> +#include <FL/fl_ask.H> // fl_beep() +#include <FL/Fl_Window.H> +#include <FL/Fl_Button.H> +#include <FL/Fl_Input.H> +#include <FL/Fl_Native_File_Chooser.H> + +// GLOBALS +Fl_Input *G_filename = NULL; + +void Butt_CB(Fl_Widget*, void*) { + // Create native chooser + Fl_Native_File_Chooser native; + native.title("Pick a file"); + native.type(Fl_Native_File_Chooser::BROWSE_FILE); + native.filter("Text\t*.txt\n" + "C Files\t*.{cxx,h,c}\n" + "Apps\t*.{app}\n"); // TODO: need to add kNavSupportPackages to non-cocoa <FNFC>_MAC.cxx + native.preset_file(G_filename->value()); + // Show native chooser + switch ( native.show() ) { + case -1: fprintf(stderr, "ERROR: %s\n", native.errmsg()); break; // ERROR + case 1: fprintf(stderr, "*** CANCEL\n"); fl_beep(); break; // CANCEL + default: // PICKED FILE + if ( native.filename() ) { + G_filename->value(native.filename()); + } else { + G_filename->value("NULL"); + } + break; + } +} + +int main(int argc, char **argv) { + //// For a nicer looking browser under linux, uncomment the following line. + //// (If you do this, you'll need to link with fltk_images) + //// + //// Fl_File_Icon::load_system_icons(); + + Fl_Window *win = new Fl_Window(600, 100, "FLTK Window"); + win->begin(); + { + int y = 10; + G_filename = new Fl_Input(80, y, win->w()-80-10, 25, "Filename"); + G_filename->value(argc < 2 ? "." : argv[1]); + G_filename->tooltip("Default filename"); + y += G_filename->h() + 5; + Fl_Button *but = new Fl_Button(win->w()-80-10, win->h()-25-10, 80, 25, "Pick File"); + but->callback(Butt_CB); + } + win->end(); + win->resizable(win); + win->show(); + return(Fl::run()); +} |
