fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_Native_File_Chooser_WIN32.cxx

Go to the documentation of this file.
00001 // "$Id: Fl_Native_File_Chooser_WIN32.cxx 7977 2010-12-08 13:16:27Z AlbrechtS $"
00002 //
00003 // FLTK native OS file chooser widget
00004 //
00005 // Copyright 1998-2010 by Bill Spitzak and others.
00006 // Copyright 2004 Greg Ercolano.
00007 // API changes + filter improvements by Nathan Vander Wilt 2005
00008 //
00009 // This library is free software; you can redistribute it and/or
00010 // modify it under the terms of the GNU Library General Public
00011 // License as published by the Free Software Foundation; either
00012 // version 2 of the License, or (at your option) any later version.
00013 //
00014 // This library is distributed in the hope that it will be useful,
00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 // Library General Public License for more details.
00018 //
00019 // You should have received a copy of the GNU Library General Public
00020 // License along with this library; if not, write to the Free Software
00021 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00022 // USA.
00023 //
00024 // Please report all bugs and problems to:
00025 //
00026 //     http://www.fltk.org/str.php
00027 //
00028 
00029 // Any application to multi-folder implementation:
00030 //     http://www.codeproject.com/dialog/selectfolder.asp
00031 //
00032 
00033 #ifndef FL_DOXYGEN              // PREVENT DOXYGEN'S USE OF THIS FILE
00034 
00035 #include <stdio.h>              // debugging
00036 #include <wchar.h>              //MG
00037 #include "Fl_Native_File_Chooser_common.cxx"            // strnew/strfree/strapp/chrcat
00038 typedef const wchar_t *LPCWSTR; //MG
00039 LPCWSTR utf8towchar(const char *in); //MG
00040 char *wchartoutf8(LPCWSTR in);  //MG
00041 
00042 #include <FL/Fl_Native_File_Chooser.H>
00043 
00044 #define LCURLY_CHR      '{'
00045 #define RCURLY_CHR      '}'
00046 #define LBRACKET_CHR    '['
00047 #define RBRACKET_CHR    ']'
00048 #define MAXFILTERS      80
00049 
00050 void fl_OleInitialize();        // in Fl.cxx (Windows only)
00051 
00052 // STATIC: PRINT WINDOWS 'DOUBLE NULL' STRING (DEBUG)
00053 #ifdef DEBUG
00054 static void dnullprint(char *wp) {
00055   if ( ! wp ) return;
00056   for ( int t=0; true; t++ ) {
00057     if ( wp[t] == '\0' && wp[t+1] == '\0' ) {
00058       printf("\\0\\0");
00059       fflush(stdout);
00060       return;
00061     } else if ( wp[t] == '\0' ) {
00062       printf("\\0");
00063     } else { 
00064       printf("%c",wp[t]);
00065     }
00066   }
00067 }
00068 #endif
00069 
00070 // RETURN LENGTH OF DOUBLENULL STRING
00071 //    Includes single nulls in count, excludes trailing doublenull.
00072 //
00073 //         1234 567
00074 //         |||/\|||
00075 //    IN: "one\0two\0\0"
00076 //   OUT: 7
00077 //
00078 static int dnulllen(const char *wp) {
00079   int len = 0;
00080   while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) {
00081     ++wp;
00082     ++len;
00083   }
00084   return(len);
00085 }
00086 
00087 // STATIC: Append a string to another, leaving terminated with DOUBLE NULL.
00088 //     Automatically handles extending length of string.
00089 //     wp can be NULL (a new wp will be allocated and initialized).
00090 //     string must be NULL terminated.
00091 //     The pointer wp may be modified on return.
00092 //
00093 static void dnullcat(char*&wp, const char *string, int n = -1 ) {
00094   //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n");
00095   int inlen = ( n < 0 ) ? strlen(string) : n;
00096   if ( ! wp ) {
00097     wp = new char[inlen + 4];
00098     *(wp+0) = '\0';
00099     *(wp+1) = '\0';
00100   } else {
00101     int wplen = dnulllen(wp);
00102     // Make copy of wp into larger buffer
00103     char *tmp = new char[wplen + inlen + 4];
00104     memcpy(tmp, wp, wplen+2);   // copy of wp plus doublenull
00105     delete [] wp;                       // delete old wp
00106     wp = tmp;                   // use new copy
00107     //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen);
00108   }
00109 
00110   // Find end of double null string
00111   //     *wp2 is left pointing at second null.
00112   //
00113   char *wp2 = wp;
00114   if ( *(wp2+0) != '\0' && *(wp2+1) != '\0' ) {
00115     for ( ; 1; wp2++ ) {
00116       if ( *(wp2+0) == '\0' && *(wp2+1) == '\0' ) {
00117         wp2++;
00118         break;
00119       }
00120     }
00121   }
00122 
00123   if ( n == -1 ) n = strlen(string);
00124   strncpy(wp2, string, n);
00125 
00126   // Leave string double-null terminated
00127   *(wp2+n+0) = '\0';
00128   *(wp2+n+1) = '\0';
00129   //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n");
00130 }
00131 
00132 // CTOR
00133 Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
00134   _btype           = val;
00135   _options         = NO_OPTIONS;
00136   memset((void*)&_ofn, 0, sizeof(OPENFILENAMEW));
00137   _ofn.lStructSize = sizeof(OPENFILENAMEW);
00138   _ofn.hwndOwner   = NULL;
00139   memset((void*)&_binf, 0, sizeof(BROWSEINFO));
00140   _pathnames       = NULL;
00141   _tpathnames      = 0;
00142   _directory       = NULL;
00143   _title           = NULL;
00144   _filter          = NULL;
00145   _parsedfilt      = NULL;
00146   _nfilters        = 0;
00147   _preset_file     = NULL;
00148   _errmsg          = NULL;
00149 }
00150 
00151 // DTOR
00152 Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
00153   //_pathnames                // managed by clear_pathnames()
00154   //_tpathnames               // managed by clear_pathnames()
00155   _directory   = strfree(_directory);
00156   _title       = strfree(_title);
00157   _filter      = strfree(_filter);
00158   //_parsedfilt               // managed by clear_filters()
00159   //_nfilters                 // managed by clear_filters()
00160   _preset_file = strfree(_preset_file);
00161   _errmsg      = strfree(_errmsg);
00162   clear_filters();
00163   clear_pathnames();
00164   ClearOFN();
00165   ClearBINF();
00166 }
00167 
00168 // SET TYPE OF BROWSER
00169 void Fl_Native_File_Chooser::type(int val) {
00170   _btype = val;
00171 }
00172 
00173 // GET TYPE OF BROWSER
00174 int Fl_Native_File_Chooser::type() const {
00175   return( _btype );
00176 }
00177 
00178 // SET OPTIONS
00179 void Fl_Native_File_Chooser::options(int val) {
00180   _options = val;
00181 }
00182 
00183 // GET OPTIONS
00184 int Fl_Native_File_Chooser::options() const {
00185   return(_options);
00186 }
00187 
00188 // PRIVATE: SET ERROR MESSAGE
00189 void Fl_Native_File_Chooser::errmsg(const char *val) {
00190   _errmsg = strfree(_errmsg);
00191   _errmsg = strnew(val);
00192 }
00193 
00194 // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
00195 void Fl_Native_File_Chooser::clear_pathnames() {
00196   if ( _pathnames ) {
00197     while ( --_tpathnames >= 0 ) {
00198       _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
00199     }
00200     delete [] _pathnames;
00201     _pathnames = NULL;
00202   }
00203   _tpathnames = 0;
00204 }
00205 
00206 // SET A SINGLE PATHNAME
00207 void Fl_Native_File_Chooser::set_single_pathname(const char *s) {
00208   clear_pathnames();
00209   _pathnames = new char*[1];
00210   _pathnames[0] = strnew(s);
00211   _tpathnames = 1;
00212 }
00213 
00214 // ADD PATHNAME TO EXISTING ARRAY
00215 void Fl_Native_File_Chooser::add_pathname(const char *s) {
00216   if ( ! _pathnames ) {
00217     // Create first element in array
00218     ++_tpathnames;
00219     _pathnames = new char*[_tpathnames];
00220   } else {
00221     // Grow array by 1
00222     char **tmp = new char*[_tpathnames+1];              // create new buffer
00223     memcpy((void*)tmp, (void*)_pathnames, 
00224                        sizeof(char*)*_tpathnames);      // copy old
00225     delete [] _pathnames;                               // delete old
00226     _pathnames = tmp;                                   // use new
00227     ++_tpathnames;
00228   }
00229   _pathnames[_tpathnames-1] = strnew(s);
00230 }
00231 
00232 // FREE A PIDL (Pointer to IDentity List)
00233 void Fl_Native_File_Chooser::FreePIDL(ITEMIDLIST *pidl) {
00234   IMalloc *imalloc = NULL;
00235   if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) {
00236     imalloc->Free(pidl);
00237     imalloc->Release();
00238     imalloc = NULL;
00239   }
00240 }
00241 
00242 // CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS
00243 void Fl_Native_File_Chooser::ClearOFN() {
00244   // Free any previously allocated lpstrFile before zeroing out _ofn
00245   if ( _ofn.lpstrFile ) {
00246     delete [] _ofn.lpstrFile;
00247     _ofn.lpstrFile = NULL;
00248   }
00249   if ( _ofn.lpstrInitialDir ) {
00250     delete [] (TCHAR*) _ofn.lpstrInitialDir; //msvc6 compilation fix
00251     _ofn.lpstrInitialDir = NULL;
00252   }
00253   _ofn.lpstrFilter = NULL;              // (deleted elsewhere)
00254   int temp = _ofn.nFilterIndex;         // keep the filter_value
00255   memset((void*)&_ofn, 0, sizeof(_ofn));
00256   _ofn.lStructSize  = sizeof(OPENFILENAMEW);
00257   _ofn.nFilterIndex = temp;
00258 }
00259 
00260 // CLEAR MICROSOFT BINF (BROWSER INFO) CLASS
00261 void Fl_Native_File_Chooser::ClearBINF() {
00262   if ( _binf.pidlRoot ) {
00263     FreePIDL((ITEMIDLIST*)_binf.pidlRoot);
00264     _binf.pidlRoot = NULL;
00265   }
00266   memset((void*)&_binf, 0, sizeof(_binf));
00267 }
00268 
00269 // CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES
00270 void Fl_Native_File_Chooser::Win2Unix(char *s) {
00271   for ( ; *s; s++ )
00272     if ( *s == '\\' ) *s = '/';
00273 }
00274 
00275 // CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES
00276 void Fl_Native_File_Chooser::Unix2Win(char *s) {
00277   for ( ; *s; s++ )
00278     if ( *s == '/' ) *s = '\\';
00279 }
00280 
00281 // SHOW FILE BROWSER
00282 int Fl_Native_File_Chooser::showfile() {
00283   ClearOFN();
00284   clear_pathnames();
00285   size_t fsize = MAX_PATH;
00286   _ofn.Flags |= OFN_NOVALIDATE;         // prevent disabling of front slashes
00287   _ofn.Flags |= OFN_HIDEREADONLY;       // hide goofy readonly flag
00288   // USE NEW BROWSER
00289   _ofn.Flags |= OFN_EXPLORER;           // use newer explorer windows
00290   _ofn.Flags |= OFN_ENABLESIZING;       // allow window to be resized (hey, why not?)
00291 
00292   // XXX: The docs for OFN_NOCHANGEDIR says the flag is 'ineffective' on XP/2K/NT!
00293   //      But let's set it anyway..
00294   //
00295   _ofn.Flags |= OFN_NOCHANGEDIR;        // prevent dialog for messing up the cwd
00296 
00297   switch ( _btype ) {
00298     case BROWSE_DIRECTORY:
00299     case BROWSE_MULTI_DIRECTORY:
00300     case BROWSE_SAVE_DIRECTORY:
00301       abort();                          // never happens: handled by showdir()
00302     case BROWSE_FILE:
00303       fsize = 65536;                    // XXX: there must be a better way
00304       break;
00305     case BROWSE_MULTI_FILE:
00306       _ofn.Flags |= OFN_ALLOWMULTISELECT;
00307       fsize = 65536;                    // XXX: there must be a better way
00308       break;
00309     case BROWSE_SAVE_FILE:
00310       if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) {
00311           _ofn.Flags |= OFN_OVERWRITEPROMPT;
00312       }
00313       break;
00314   }
00315   // SPACE FOR RETURNED FILENAME
00316   _ofn.lpstrFile    = new WCHAR[fsize];
00317   _ofn.nMaxFile     = fsize-1;
00318   _ofn.lpstrFile[0] = 0;
00319   _ofn.lpstrFile[1] = 0;                // dnull
00320   // PARENT WINDOW
00321   _ofn.hwndOwner = GetForegroundWindow();
00322   // DIALOG TITLE
00323   if (_title) {
00324     static WCHAR wtitle[200];
00325     wcscpy(wtitle, utf8towchar(_title));
00326     _ofn.lpstrTitle =  wtitle;
00327   } else {
00328     _ofn.lpstrTitle = NULL;
00329   }
00330   // FILTER
00331   if (_parsedfilt != NULL) {    // to convert a null-containing char string into a widechar string
00332     static WCHAR wpattern[MAX_PATH];
00333     const char *p = _parsedfilt;
00334     while(*(p + strlen(p) + 1) != 0) p += strlen(p) + 1;
00335     p += strlen(p) + 2;
00336     MultiByteToWideChar(CP_UTF8, 0, _parsedfilt, p - _parsedfilt, wpattern, MAX_PATH);
00337     _ofn.lpstrFilter = wpattern;
00338   } else {
00339     _ofn.lpstrFilter = NULL;
00340   }
00341   // PRESET FILE
00342   //     If set, supercedes _directory. See KB Q86920 for details
00343   //
00344   if ( _preset_file ) {
00345     size_t len = strlen(_preset_file);
00346     if ( len >= _ofn.nMaxFile ) {
00347       char msg[80];
00348       sprintf(msg, "preset_file() filename is too long: %ld is >=%ld", (long)len, (long)fsize);
00349       return(-1);
00350     }
00351     wcscpy(_ofn.lpstrFile, utf8towchar(_preset_file));
00352 //  Unix2Win(_ofn.lpstrFile);
00353     len = wcslen(_ofn.lpstrFile);
00354     _ofn.lpstrFile[len+0] = 0;  // multiselect needs dnull
00355     _ofn.lpstrFile[len+1] = 0;
00356   }
00357   if ( _directory ) {
00358     // PRESET DIR
00359     //     XXX: See KB Q86920 for doc bug:
00360     //     http://support.microsoft.com/default.aspx?scid=kb;en-us;86920
00361     //
00362     _ofn.lpstrInitialDir    = new WCHAR[MAX_PATH];
00363     wcscpy((WCHAR *)_ofn.lpstrInitialDir, utf8towchar(_directory));
00364     // Unix2Win((char*)_ofn.lpstrInitialDir);
00365   }
00366   // SAVE THE CURRENT DIRECTORY
00367   //     XXX: Save the cwd because GetOpenFileName() is probably going to
00368   //     change it, in spite of the OFN_NOCHANGEDIR flag, due to its docs
00369   //     saying the flag is 'ineffective'. %^(
00370   //
00371   char oldcwd[MAX_PATH];
00372   GetCurrentDirectory(MAX_PATH, oldcwd);
00373   oldcwd[MAX_PATH-1] = '\0';
00374   // OPEN THE DIALOG WINDOW
00375   int err;
00376   if ( _btype == BROWSE_SAVE_FILE ) {
00377     err = GetSaveFileNameW(&_ofn);
00378   } else {
00379     err = GetOpenFileNameW(&_ofn);
00380   }
00381   if ( err == 0 ) {
00382     // EXTENDED ERROR CHECK
00383     int err = CommDlgExtendedError();
00384     // CANCEL?
00385     if ( err == 0 ) return(1);  // user hit 'cancel'
00386     // AN ERROR OCCURRED
00387     char msg[80];
00388     sprintf(msg, "CommDlgExtendedError() code=%d", err);
00389     errmsg(msg);
00390     // XXX: RESTORE CWD
00391     if ( oldcwd[0] ) SetCurrentDirectory(oldcwd);
00392     return(-1);
00393   }
00394   // XXX: RESTORE CWD
00395   if ( oldcwd[0] ) {
00396     SetCurrentDirectory(oldcwd);
00397   }
00398   // PREPARE PATHNAMES FOR RETURN
00399   switch ( _btype ) {
00400     case BROWSE_FILE: 
00401     case BROWSE_SAVE_FILE:
00402       set_single_pathname(wchartoutf8(_ofn.lpstrFile));
00403       // Win2Unix(_pathnames[_tpathnames-1]);
00404       break;
00405     case BROWSE_MULTI_FILE: {
00406       // EXTRACT MULTIPLE FILENAMES
00407       const WCHAR *dirname = _ofn.lpstrFile;
00408       int dirlen = wcslen(dirname);
00409       if ( dirlen > 0 ) {
00410         // WALK STRING SEARCHING FOR 'DOUBLE-NULL'
00411         //     eg. "/dir/name\0foo1\0foo2\0foo3\0\0"
00412         //
00413         char pathname[MAX_PATH]; 
00414         for ( const WCHAR *s = dirname + dirlen + 1; 
00415                  *s; s+= (wcslen(s)+1)) {
00416                 strcpy(pathname, wchartoutf8(dirname));
00417                 strcat(pathname, "\\");
00418                 strcat(pathname, wchartoutf8(s));
00419                 add_pathname(pathname);
00420         }
00421       }
00422       // XXX
00423       //    Work around problem where pasted forward-slash pathname
00424       //    into the file browser causes new "Explorer" interface
00425       //    not to grok forward slashes, passing back as a 'filename'..!
00426       //
00427       if ( _tpathnames == 0 ) {
00428         add_pathname(wchartoutf8(dirname)); 
00429         // Win2Unix(_pathnames[_tpathnames-1]);
00430       }
00431       break;
00432     }
00433     case BROWSE_DIRECTORY:
00434     case BROWSE_MULTI_DIRECTORY:
00435     case BROWSE_SAVE_DIRECTORY:
00436       abort();                  // never happens: handled by showdir()
00437   }
00438   return(0);
00439 }
00440 
00441 // Used by SHBrowseForFolder(), sets initial selected dir.
00442 // Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes
00443 //              Subject: How to specify to select an initial folder .."
00444 //
00445 int CALLBACK Fl_Native_File_Chooser::Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) {
00446   switch (msg) {
00447     case BFFM_INITIALIZED:
00448       if (data) ::SendMessage(win, BFFM_SETSELECTION, TRUE, data);
00449       break;
00450     case BFFM_SELCHANGED:
00451       TCHAR path[MAX_PATH];
00452       if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) {
00453         ::SendMessage(win, BFFM_ENABLEOK, 0, 1);
00454       } else {
00455         // disable ok button if not a path
00456         ::SendMessage(win, BFFM_ENABLEOK, 0, 0);
00457       }
00458       break;
00459     case BFFM_VALIDATEFAILED:
00460       // we could pop up an annoying message here. 
00461       // also needs set ulFlags |= BIF_VALIDATE
00462       break;
00463     default:
00464       break;
00465   }
00466   return(0);
00467 }
00468 
00469 // SHOW DIRECTORY BROWSER
00470 int Fl_Native_File_Chooser::showdir() {
00471   // initialize OLE only once
00472   fl_OleInitialize();           // init needed by BIF_USENEWUI
00473   ClearBINF();
00474   clear_pathnames();
00475   // PARENT WINDOW
00476   _binf.hwndOwner = GetForegroundWindow();
00477   // DIALOG TITLE
00478   _binf.lpszTitle = _title ? _title : NULL;
00479   // FLAGS
00480   _binf.ulFlags = 0;            // initialize
00481 
00482   // TBD: make sure matches to runtime system, if need be.
00483   //(what if _WIN32_IE doesn't match system? does the program not run?)
00484   //
00485   // TBD: match all 3 types of directories
00486   //
00487   // NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares
00488   //       from being visible in BROWSE_DIRECTORY mode. 
00489   //       See Walter Garm's comments in ./TODO.
00490 
00491 #if defined(BIF_NONEWFOLDERBUTTON)                              // Version 6.0
00492   if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON;
00493   _binf.ulFlags |= BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
00494 #elif defined(BIF_USENEWUI)                                     // Version 5.0
00495   if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_EDITBOX;
00496   else if ( _btype == BROWSE_SAVE_DIRECTORY ) _binf.ulFlags |= BIF_USENEWUI;
00497   _binf.ulFlags |= BIF_RETURNONLYFSDIRS;
00498 #elif defined(BIF_EDITBOX)                                      // Version 4.71
00499   _binf.ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX;
00500 #else                                                           // Version Old
00501   _binf.ulFlags |= BIF_RETURNONLYFSDIRS;
00502 #endif
00503 
00504   // BUFFER
00505   char displayname[MAX_PATH];
00506   _binf.pszDisplayName = displayname;
00507   // PRESET DIR
00508   char presetname[MAX_PATH];
00509   if ( _directory ) {
00510     strcpy(presetname, _directory);
00511     // Unix2Win(presetname);
00512     _binf.lParam = (LPARAM)presetname;
00513   }
00514   else _binf.lParam = 0;
00515   _binf.lpfn = Dir_CB;
00516   // OPEN BROWSER
00517   ITEMIDLIST *pidl = SHBrowseForFolder(&_binf);
00518   // CANCEL?
00519   if ( pidl == NULL ) return(1);
00520 
00521   // GET THE PATHNAME(S) THE USER SELECTED
00522   // TBD: expand NetHood shortcuts from this PIDL??
00523   // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp
00524 
00525   TCHAR path[MAX_PATH];
00526   if ( SHGetPathFromIDList(pidl, path) ) {
00527     // Win2Unix(path);
00528     add_pathname(path);
00529   }
00530   FreePIDL(pidl);
00531   if ( !strlen(path) ) return(1);             // don't return empty pathnames
00532   return(0);
00533 }
00534 
00535 // RETURNS:
00536 //    0 - user picked a file
00537 //    1 - user cancelled
00538 //   -1 - failed; errmsg() has reason
00539 //
00540 int Fl_Native_File_Chooser::show() {
00541   if ( _btype == BROWSE_DIRECTORY || 
00542        _btype == BROWSE_MULTI_DIRECTORY || 
00543        _btype == BROWSE_SAVE_DIRECTORY ) {
00544     return(showdir());
00545   } else {
00546     return(showfile());
00547   }
00548 }
00549 
00550 // RETURN ERROR MESSAGE
00551 const char *Fl_Native_File_Chooser::errmsg() const {
00552   return(_errmsg ? _errmsg : "No error");
00553 }
00554 
00555 // GET FILENAME
00556 const char* Fl_Native_File_Chooser::filename() const {
00557   if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
00558   return("");
00559 }
00560 
00561 // GET FILENAME FROM LIST OF FILENAMES
00562 const char* Fl_Native_File_Chooser::filename(int i) const {
00563   if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
00564   return("");
00565 }
00566 
00567 // GET TOTAL FILENAMES CHOSEN
00568 int Fl_Native_File_Chooser::count() const {
00569   return(_tpathnames);
00570 }
00571 
00572 // PRESET PATHNAME
00573 //     Can be NULL if no preset is desired.
00574 //
00575 void Fl_Native_File_Chooser::directory(const char *val) {
00576   _directory = strfree(_directory);
00577   _directory = strnew(val);
00578 }
00579 
00580 // GET PRESET PATHNAME
00581 //    Can return NULL if none set.
00582 //
00583 const char *Fl_Native_File_Chooser::directory() const {
00584   return(_directory);
00585 }
00586 
00587 // SET TITLE
00588 //     Can be NULL if no title desired.
00589 //
00590 void Fl_Native_File_Chooser::title(const char *val) {
00591   _title = strfree(_title);
00592   _title = strnew(val);
00593 }
00594 
00595 // GET TITLE
00596 //    Can return NULL if none set.
00597 //
00598 const char *Fl_Native_File_Chooser::title() const {
00599   return(_title);
00600 }
00601 
00602 // SET FILTER
00603 //     Can be NULL if no filter needed
00604 //
00605 void Fl_Native_File_Chooser::filter(const char *val) {
00606   _filter = strfree(_filter);
00607   clear_filters();
00608   if ( val ) {
00609     _filter = strnew(val);
00610     parse_filter(_filter);
00611   }
00612   add_filter("All Files", "*.*");       // always include 'all files' option
00613 
00614 #ifdef DEBUG
00615   nullprint(_parsedfilt);
00616 #endif /*DEBUG*/
00617 }
00618 
00619 // GET FILTER
00620 //    Can return NULL if none set.
00621 //
00622 const char *Fl_Native_File_Chooser::filter() const {
00623   return(_filter);
00624 }
00625 
00626 // CLEAR FILTERS
00627 void Fl_Native_File_Chooser::clear_filters() {
00628   _nfilters = 0;
00629   _parsedfilt = strfree(_parsedfilt);
00630 }
00631 
00632 // ADD A FILTER
00633 void Fl_Native_File_Chooser::add_filter(const char *name_in,    // name of filter (optional: can be null)
00634                             const char *winfilter) {            // windows style filter (eg. "*.cxx;*.h")
00635   // No name? Make one..
00636   char name[1024];
00637   if ( !name_in || name_in[0] == '\0' ) {
00638     sprintf(name, "%.*s Files", int(sizeof(name)-10), winfilter);
00639   } else {
00640     sprintf(name, "%.*s", int(sizeof(name)-10), name_in);
00641   }
00642   dnullcat(_parsedfilt, name);
00643   dnullcat(_parsedfilt, winfilter);
00644   _nfilters++;
00645   //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter);
00646 }
00647 
00648 // CONVERT FLTK STYLE PATTERN MATCHES TO WINDOWS 'DOUBLENULL' PATTERN
00649 //    Handles:
00650 //        IN              OUT
00651 //        -----------     -----------------------------
00652 //        *.{ma,mb}       "*.{ma,mb} Files\0*.ma;*.mb\0\0"
00653 //        *.[abc]         "*.[abc] Files\0*.a;*.b;*.c\0\0"
00654 //        *.txt           "*.txt Files\0*.txt\0\0"
00655 //        C Files\t*.[ch] "C Files\0*.c;*.h\0\0"
00656 //
00657 //    Example:
00658 //         IN: "*.{ma,mb}"
00659 //        OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0"
00660 //              ---------------  ---------  ---------  ---
00661 //                     |             |          |       |
00662 //                   Title       Wildcards    Title    Wildcards
00663 //
00664 // Parsing Mode:
00665 //         IN:"C Files\t*.{cxx,h}"
00666 //             |||||||  |||||||||
00667 //       mode: nnnnnnn  ww{{{{{{{
00668 //             \_____/  \_______/
00669 //              Name     Wildcard
00670 //
00671 void Fl_Native_File_Chooser::parse_filter(const char *in) {
00672   clear_filters();
00673   if ( ! in ) return;
00674 
00675   int has_name = strchr(in, '\t') ? 1 : 0;
00676 
00677   char mode = has_name ? 'n' : 'w';     // parse mode: n=name, w=wildcard
00678   int nwildcards = 0;
00679   char wildcards[MAXFILTERS][1024];     // parsed wildcards (can be several)
00680   char wildprefix[512] = "";
00681   char name[512] = "";
00682 
00683   // Init
00684   int t;
00685   for ( t=0; t<MAXFILTERS; t++ ) {
00686     wildcards[t][0] = '\0';
00687   }
00688 
00689   // Parse
00690   for ( ; 1; in++ ) {
00691 
00695 
00696     switch (*in) {
00697       case ',':
00698       case '|':
00699         if ( mode == LCURLY_CHR ) {
00700           // create new wildcard, copy in prefix
00701           strcat(wildcards[nwildcards++], wildprefix);
00702           continue;
00703         } else {
00704           goto regchar;
00705         }
00706         continue;
00707 
00708       // FINISHED PARSING A NAME?
00709       case '\t':
00710         if ( mode != 'n' ) goto regchar;
00711         // finish parsing name? switch to wildcard mode
00712         mode = 'w';
00713         break;
00714 
00715       // ESCAPE NEXT CHAR
00716       case '\\':
00717         ++in;
00718         goto regchar;
00719 
00720       // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
00721       case '\r':
00722       case '\n':
00723       case '\0':
00724       {
00725         if ( mode == 'w' ) {            // finished parsing wildcard?
00726           if ( nwildcards == 0 ) {
00727             strcpy(wildcards[nwildcards++], wildprefix);
00728           }
00729           // Append wildcards in Microsoft's "*.one;*.two" format
00730           char comp[4096] = "";
00731           for ( t=0; t<nwildcards; t++ ) {
00732             if ( t != 0 ) strcat(comp, ";");
00733             strcat(comp, wildcards[t]);
00734           }
00735           // Add if not empty
00736           if ( comp[0] ) {
00737             add_filter(name, comp);
00738           }
00739         }
00740         // RESET
00741         for ( t=0; t<MAXFILTERS; t++ ) {
00742           wildcards[t][0] = '\0';
00743         }
00744         nwildcards = 0;
00745         wildprefix[0] = name[0] = '\0';
00746         mode = strchr(in,'\t') ? 'n' : 'w';
00747         // DONE?
00748         if ( *in == '\0' ) return;      // done
00749         continue;                       // not done yet, more filters
00750       }
00751 
00752       // STARTING A WILDCARD?
00753       case LBRACKET_CHR:
00754       case LCURLY_CHR:
00755         mode = *in;
00756         if ( *in == LCURLY_CHR ) {
00757           // create new wildcard
00758           strcat(wildcards[nwildcards++], wildprefix);
00759         }
00760         continue;
00761 
00762       // ENDING A WILDCARD?
00763       case RBRACKET_CHR:
00764       case RCURLY_CHR:
00765         mode = 'w';     // back to wildcard mode
00766         continue;
00767 
00768       // ALL OTHER NON-SPECIAL CHARACTERS
00769       default:
00770       regchar:          // handle regular char
00771         switch ( mode ) {
00772           case LBRACKET_CHR: 
00773             // create new wildcard
00774             ++nwildcards;
00775             // copy in prefix
00776             strcpy(wildcards[nwildcards-1], wildprefix);
00777             // append search char
00778             chrcat(wildcards[nwildcards-1], *in);
00779             continue;
00780 
00781           case LCURLY_CHR:
00782             if ( nwildcards > 0 ) {
00783               chrcat(wildcards[nwildcards-1], *in);
00784             }
00785             continue;
00786 
00787           case 'n':
00788             chrcat(name, *in);
00789             continue;
00790 
00791           case 'w':
00792             chrcat(wildprefix, *in);
00793             for ( t=0; t<nwildcards; t++ ) {
00794               chrcat(wildcards[t], *in);
00795             }
00796             continue;
00797         }
00798         break;
00799     }
00800   }
00801 }
00802 
00803 // SET 'CURRENTLY SELECTED FILTER'
00804 void Fl_Native_File_Chooser::filter_value(int i) {
00805   _ofn.nFilterIndex = i + 1;
00806 }
00807 
00808 // RETURN VALUE OF 'CURRENTLY SELECTED FILTER'
00809 int Fl_Native_File_Chooser::filter_value() const {
00810   return(_ofn.nFilterIndex ? _ofn.nFilterIndex-1 : _nfilters+1);
00811 }
00812 
00813 // PRESET FILENAME FOR 'SAVE AS' CHOOSER
00814 void Fl_Native_File_Chooser::preset_file(const char* val) {
00815   _preset_file = strfree(_preset_file);
00816   _preset_file = strnew(val);
00817 }
00818 
00819 // GET PRESET FILENAME FOR 'SAVE AS' CHOOSER
00820 const char* Fl_Native_File_Chooser::preset_file() const {
00821   return(_preset_file);
00822 }
00823 
00824 char *wchartoutf8(LPCWSTR in)
00825 {
00826   static char *out = NULL;
00827   static int lchar = 0;
00828   if (in == NULL)return NULL;
00829   int utf8len  = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
00830   if (utf8len > lchar) {
00831     lchar = utf8len;
00832     out = (char *)realloc(out, lchar * sizeof(char));
00833   }
00834   WideCharToMultiByte(CP_UTF8, 0, in, -1, out, utf8len, NULL, NULL);
00835   return out;
00836 }
00837 
00838 LPCWSTR utf8towchar(const char *in)
00839 {
00840   static WCHAR *wout = NULL;
00841   static int lwout = 0;
00842   if (in == NULL)return NULL;
00843   int wlen = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
00844   if (wlen > lwout) {
00845     lwout = wlen;
00846     wout = (WCHAR *)realloc(wout, lwout * sizeof(WCHAR));
00847   }
00848   MultiByteToWideChar(CP_UTF8, 0, in, -1, wout, wlen);
00849   return wout;
00850 }
00851 
00852 #endif 
00854 //
00855 // End of "$Id: Fl_Native_File_Chooser_WIN32.cxx 7977 2010-12-08 13:16:27Z AlbrechtS $".
00856 //