diff options
| author | Greg Ercolano <erco@seriss.com> | 2010-01-13 23:13:59 +0000 |
|---|---|---|
| committer | Greg Ercolano <erco@seriss.com> | 2010-01-13 23:13:59 +0000 |
| commit | 1de0988c317c7b916daa7b4f102f0889e026c77d (patch) | |
| tree | e479de5817a0d0388e1f4c1a62db018ea9f2981f /src/Fl_Native_File_Chooser_WIN32.cxx | |
| parent | 139c88c618ae6a186c59bde73e05ea93caa3e297 (diff) | |
Added Fl_Native_File_Chooser widget (with manolo's cocoa mods) to FLTK.
* Source brought into CMP standards compliance
* test program added (test/native-filechooser.cxx)
* Tested with linux (Ubuntu8) and with OSX (cocoa and non-cocoa builds)
TODO:
* Needs doxygen docs from Greg's original HTML documentation
* Needs mods to Windows build files
* Needs mods to cmake, and other build files
* Needs Manolo's latest mods (from STR #2298) mentioned on and after "02:05 Jan 13, 2010"
git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6997 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
Diffstat (limited to 'src/Fl_Native_File_Chooser_WIN32.cxx')
| -rw-r--r-- | src/Fl_Native_File_Chooser_WIN32.cxx | 848 |
1 files changed, 848 insertions, 0 deletions
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:". +// |
