themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_Native_File_Chooser_WIN32.cxx
blob71b5a92526006de219d165d83c783a99c65b23d7
1 // "$Id: Fl_Native_File_Chooser_WIN32.cxx 8454 2011-02-20 21:46:11Z manolo $"
2 //
3 // FLTK native OS file chooser widget
4 //
5 // Copyright 1998-2010 by Bill Spitzak and others.
6 // Copyright 2004 Greg Ercolano.
7 // API changes + filter improvements by Nathan Vander Wilt 2005
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Library General Public
11 // License as published by the Free Software Foundation; either
12 // version 2 of the License, or (at your option) any later version.
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Library General Public License for more details.
19 // You should have received a copy of the GNU Library General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 // USA.
24 // Please report all bugs and problems to:
26 // http://www.fltk.org/str.php
29 // Any application to multi-folder implementation:
30 // http://www.codeproject.com/dialog/selectfolder.asp
33 #ifndef FL_DOXYGEN // PREVENT DOXYGEN'S USE OF THIS FILE
35 #include <stdio.h> // debugging
36 #include <wchar.h> //MG
37 #include "Fl_Native_File_Chooser_common.cxx" // strnew/strfree/strapp/chrcat
38 typedef const wchar_t *LPCWSTR; //MG
39 LPCWSTR utf8towchar(const char *in); //MG
40 char *wchartoutf8(LPCWSTR in); //MG
42 #include <FL/Fl_Native_File_Chooser.H>
44 #define LCURLY_CHR '{'
45 #define RCURLY_CHR '}'
46 #define LBRACKET_CHR '['
47 #define RBRACKET_CHR ']'
48 #define MAXFILTERS 80
50 void fl_OleInitialize(); // in Fl.cxx (Windows only)
52 // STATIC: PRINT WINDOWS 'DOUBLE NULL' STRING (DEBUG)
53 #ifdef DEBUG
54 static void dnullprint(char *wp) {
55 if ( ! wp ) return;
56 for ( int t=0; true; t++ ) {
57 if ( wp[t] == '\0' && wp[t+1] == '\0' ) {
58 printf("\\0\\0");
59 fflush(stdout);
60 return;
61 } else if ( wp[t] == '\0' ) {
62 printf("\\0");
63 } else {
64 printf("%c",wp[t]);
68 #endif
70 // RETURN LENGTH OF DOUBLENULL STRING
71 // Includes single nulls in count, excludes trailing doublenull.
73 // 1234 567
74 // |||/\|||
75 // IN: "one\0two\0\0"
76 // OUT: 7
78 static int dnulllen(const char *wp) {
79 int len = 0;
80 while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) {
81 ++wp;
82 ++len;
84 return(len);
87 // STATIC: Append a string to another, leaving terminated with DOUBLE NULL.
88 // Automatically handles extending length of string.
89 // wp can be NULL (a new wp will be allocated and initialized).
90 // string must be NULL terminated.
91 // The pointer wp may be modified on return.
93 static void dnullcat(char*&wp, const char *string, int n = -1 ) {
94 //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n");
95 int inlen = ( n < 0 ) ? strlen(string) : n;
96 if ( ! wp ) {
97 wp = new char[inlen + 4];
98 *(wp+0) = '\0';
99 *(wp+1) = '\0';
100 } else {
101 int wplen = dnulllen(wp);
102 // Make copy of wp into larger buffer
103 char *tmp = new char[wplen + inlen + 4];
104 memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull
105 delete [] wp; // delete old wp
106 wp = tmp; // use new copy
107 //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen);
110 // Find end of double null string
111 // *wp2 is left pointing at second null.
113 char *wp2 = wp;
114 if ( *(wp2+0) != '\0' && *(wp2+1) != '\0' ) {
115 for ( ; 1; wp2++ ) {
116 if ( *(wp2+0) == '\0' && *(wp2+1) == '\0' ) {
117 wp2++;
118 break;
123 if ( n == -1 ) n = strlen(string);
124 strncpy(wp2, string, n);
126 // Leave string double-null terminated
127 *(wp2+n+0) = '\0';
128 *(wp2+n+1) = '\0';
129 //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n");
132 // CTOR
133 Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
134 _btype = val;
135 _options = NO_OPTIONS;
136 memset((void*)&_ofn, 0, sizeof(OPENFILENAMEW));
137 _ofn.lStructSize = sizeof(OPENFILENAMEW);
138 _ofn.hwndOwner = NULL;
139 memset((void*)&_binf, 0, sizeof(BROWSEINFO));
140 _pathnames = NULL;
141 _tpathnames = 0;
142 _directory = NULL;
143 _title = NULL;
144 _filter = NULL;
145 _parsedfilt = NULL;
146 _nfilters = 0;
147 _preset_file = NULL;
148 _errmsg = NULL;
151 // DTOR
152 Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
153 //_pathnames // managed by clear_pathnames()
154 //_tpathnames // managed by clear_pathnames()
155 _directory = strfree(_directory);
156 _title = strfree(_title);
157 _filter = strfree(_filter);
158 //_parsedfilt // managed by clear_filters()
159 //_nfilters // managed by clear_filters()
160 _preset_file = strfree(_preset_file);
161 _errmsg = strfree(_errmsg);
162 clear_filters();
163 clear_pathnames();
164 ClearOFN();
165 ClearBINF();
168 // SET TYPE OF BROWSER
169 void Fl_Native_File_Chooser::type(int val) {
170 _btype = val;
173 // GET TYPE OF BROWSER
174 int Fl_Native_File_Chooser::type() const {
175 return( _btype );
178 // SET OPTIONS
179 void Fl_Native_File_Chooser::options(int val) {
180 _options = val;
183 // GET OPTIONS
184 int Fl_Native_File_Chooser::options() const {
185 return(_options);
188 // PRIVATE: SET ERROR MESSAGE
189 void Fl_Native_File_Chooser::errmsg(const char *val) {
190 _errmsg = strfree(_errmsg);
191 _errmsg = strnew(val);
194 // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS
195 void Fl_Native_File_Chooser::clear_pathnames() {
196 if ( _pathnames ) {
197 while ( --_tpathnames >= 0 ) {
198 _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]);
200 delete [] _pathnames;
201 _pathnames = NULL;
203 _tpathnames = 0;
206 // SET A SINGLE PATHNAME
207 void Fl_Native_File_Chooser::set_single_pathname(const char *s) {
208 clear_pathnames();
209 _pathnames = new char*[1];
210 _pathnames[0] = strnew(s);
211 _tpathnames = 1;
214 // ADD PATHNAME TO EXISTING ARRAY
215 void Fl_Native_File_Chooser::add_pathname(const char *s) {
216 if ( ! _pathnames ) {
217 // Create first element in array
218 ++_tpathnames;
219 _pathnames = new char*[_tpathnames];
220 } else {
221 // Grow array by 1
222 char **tmp = new char*[_tpathnames+1]; // create new buffer
223 memcpy((void*)tmp, (void*)_pathnames,
224 sizeof(char*)*_tpathnames); // copy old
225 delete [] _pathnames; // delete old
226 _pathnames = tmp; // use new
227 ++_tpathnames;
229 _pathnames[_tpathnames-1] = strnew(s);
232 // FREE A PIDL (Pointer to IDentity List)
233 void Fl_Native_File_Chooser::FreePIDL(ITEMIDLIST *pidl) {
234 IMalloc *imalloc = NULL;
235 if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) {
236 imalloc->Free(pidl);
237 imalloc->Release();
238 imalloc = NULL;
242 // CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS
243 void Fl_Native_File_Chooser::ClearOFN() {
244 // Free any previously allocated lpstrFile before zeroing out _ofn
245 if ( _ofn.lpstrFile ) {
246 delete [] _ofn.lpstrFile;
247 _ofn.lpstrFile = NULL;
249 if ( _ofn.lpstrInitialDir ) {
250 delete [] (TCHAR*) _ofn.lpstrInitialDir; //msvc6 compilation fix
251 _ofn.lpstrInitialDir = NULL;
253 _ofn.lpstrFilter = NULL; // (deleted elsewhere)
254 int temp = _ofn.nFilterIndex; // keep the filter_value
255 memset((void*)&_ofn, 0, sizeof(_ofn));
256 _ofn.lStructSize = sizeof(OPENFILENAMEW);
257 _ofn.nFilterIndex = temp;
260 // CLEAR MICROSOFT BINF (BROWSER INFO) CLASS
261 void Fl_Native_File_Chooser::ClearBINF() {
262 if ( _binf.pidlRoot ) {
263 FreePIDL((ITEMIDLIST*)_binf.pidlRoot);
264 _binf.pidlRoot = NULL;
266 memset((void*)&_binf, 0, sizeof(_binf));
269 // CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES
270 void Fl_Native_File_Chooser::Win2Unix(char *s) {
271 for ( ; *s; s++ )
272 if ( *s == '\\' ) *s = '/';
275 // CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES
276 void Fl_Native_File_Chooser::Unix2Win(char *s) {
277 for ( ; *s; s++ )
278 if ( *s == '/' ) *s = '\\';
281 // SHOW FILE BROWSER
282 int Fl_Native_File_Chooser::showfile() {
283 ClearOFN();
284 clear_pathnames();
285 size_t fsize = MAX_PATH;
286 _ofn.Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes
287 _ofn.Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag
288 // USE NEW BROWSER
289 _ofn.Flags |= OFN_EXPLORER; // use newer explorer windows
290 _ofn.Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?)
292 // XXX: The docs for OFN_NOCHANGEDIR says the flag is 'ineffective' on XP/2K/NT!
293 // But let's set it anyway..
295 _ofn.Flags |= OFN_NOCHANGEDIR; // prevent dialog for messing up the cwd
297 switch ( _btype ) {
298 case BROWSE_DIRECTORY:
299 case BROWSE_MULTI_DIRECTORY:
300 case BROWSE_SAVE_DIRECTORY:
301 abort(); // never happens: handled by showdir()
302 case BROWSE_FILE:
303 fsize = 65536; // XXX: there must be a better way
304 break;
305 case BROWSE_MULTI_FILE:
306 _ofn.Flags |= OFN_ALLOWMULTISELECT;
307 fsize = 65536; // XXX: there must be a better way
308 break;
309 case BROWSE_SAVE_FILE:
310 if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) {
311 _ofn.Flags |= OFN_OVERWRITEPROMPT;
313 break;
315 // SPACE FOR RETURNED FILENAME
316 _ofn.lpstrFile = new WCHAR[fsize];
317 _ofn.nMaxFile = fsize-1;
318 _ofn.lpstrFile[0] = 0;
319 _ofn.lpstrFile[1] = 0; // dnull
320 // PARENT WINDOW
321 _ofn.hwndOwner = GetForegroundWindow();
322 // DIALOG TITLE
323 if (_title) {
324 static WCHAR wtitle[200];
325 wcscpy(wtitle, utf8towchar(_title));
326 _ofn.lpstrTitle = wtitle;
327 } else {
328 _ofn.lpstrTitle = NULL;
330 // FILTER
331 if (_parsedfilt != NULL) { // to convert a null-containing char string into a widechar string
332 static WCHAR wpattern[MAX_PATH];
333 const char *p = _parsedfilt;
334 while(*(p + strlen(p) + 1) != 0) p += strlen(p) + 1;
335 p += strlen(p) + 2;
336 MultiByteToWideChar(CP_UTF8, 0, _parsedfilt, p - _parsedfilt, wpattern, MAX_PATH);
337 _ofn.lpstrFilter = wpattern;
338 } else {
339 _ofn.lpstrFilter = NULL;
341 // PRESET FILE
342 // If set, supercedes _directory. See KB Q86920 for details
344 if ( _preset_file ) {
345 size_t len = strlen(_preset_file);
346 if ( len >= _ofn.nMaxFile ) {
347 char msg[80];
348 sprintf(msg, "preset_file() filename is too long: %ld is >=%ld", (long)len, (long)fsize);
349 return(-1);
351 wcscpy(_ofn.lpstrFile, utf8towchar(_preset_file));
352 // Unix2Win(_ofn.lpstrFile);
353 len = wcslen(_ofn.lpstrFile);
354 _ofn.lpstrFile[len+0] = 0; // multiselect needs dnull
355 _ofn.lpstrFile[len+1] = 0;
357 if ( _directory ) {
358 // PRESET DIR
359 // XXX: See KB Q86920 for doc bug:
360 // http://support.microsoft.com/default.aspx?scid=kb;en-us;86920
362 _ofn.lpstrInitialDir = new WCHAR[MAX_PATH];
363 wcscpy((WCHAR *)_ofn.lpstrInitialDir, utf8towchar(_directory));
364 // Unix2Win((char*)_ofn.lpstrInitialDir);
366 // SAVE THE CURRENT DIRECTORY
367 // XXX: Save the cwd because GetOpenFileName() is probably going to
368 // change it, in spite of the OFN_NOCHANGEDIR flag, due to its docs
369 // saying the flag is 'ineffective'. %^(
371 char oldcwd[MAX_PATH];
372 GetCurrentDirectory(MAX_PATH, oldcwd);
373 oldcwd[MAX_PATH-1] = '\0';
374 // OPEN THE DIALOG WINDOW
375 int err;
376 if ( _btype == BROWSE_SAVE_FILE ) {
377 err = GetSaveFileNameW(&_ofn);
378 } else {
379 err = GetOpenFileNameW(&_ofn);
381 if ( err == 0 ) {
382 // EXTENDED ERROR CHECK
383 int err = CommDlgExtendedError();
384 // CANCEL?
385 if ( err == 0 ) return(1); // user hit 'cancel'
386 // AN ERROR OCCURRED
387 char msg[80];
388 sprintf(msg, "CommDlgExtendedError() code=%d", err);
389 errmsg(msg);
390 // XXX: RESTORE CWD
391 if ( oldcwd[0] ) SetCurrentDirectory(oldcwd);
392 return(-1);
394 // XXX: RESTORE CWD
395 if ( oldcwd[0] ) {
396 SetCurrentDirectory(oldcwd);
398 // PREPARE PATHNAMES FOR RETURN
399 switch ( _btype ) {
400 case BROWSE_FILE:
401 case BROWSE_SAVE_FILE:
402 set_single_pathname(wchartoutf8(_ofn.lpstrFile));
403 // Win2Unix(_pathnames[_tpathnames-1]);
404 break;
405 case BROWSE_MULTI_FILE: {
406 // EXTRACT MULTIPLE FILENAMES
407 const WCHAR *dirname = _ofn.lpstrFile;
408 int dirlen = wcslen(dirname);
409 if ( dirlen > 0 ) {
410 // WALK STRING SEARCHING FOR 'DOUBLE-NULL'
411 // eg. "/dir/name\0foo1\0foo2\0foo3\0\0"
413 char pathname[MAX_PATH];
414 for ( const WCHAR *s = dirname + dirlen + 1;
415 *s; s+= (wcslen(s)+1)) {
416 strcpy(pathname, wchartoutf8(dirname));
417 strcat(pathname, "\\");
418 strcat(pathname, wchartoutf8(s));
419 add_pathname(pathname);
422 // XXX
423 // Work around problem where pasted forward-slash pathname
424 // into the file browser causes new "Explorer" interface
425 // not to grok forward slashes, passing back as a 'filename'..!
427 if ( _tpathnames == 0 ) {
428 add_pathname(wchartoutf8(dirname));
429 // Win2Unix(_pathnames[_tpathnames-1]);
431 break;
433 case BROWSE_DIRECTORY:
434 case BROWSE_MULTI_DIRECTORY:
435 case BROWSE_SAVE_DIRECTORY:
436 abort(); // never happens: handled by showdir()
438 return(0);
441 // Used by SHBrowseForFolder(), sets initial selected dir.
442 // Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes
443 // Subject: How to specify to select an initial folder .."
445 int CALLBACK Fl_Native_File_Chooser::Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) {
446 switch (msg) {
447 case BFFM_INITIALIZED:
448 if (data) ::SendMessage(win, BFFM_SETSELECTION, TRUE, data);
449 break;
450 case BFFM_SELCHANGED:
451 TCHAR path[MAX_PATH];
452 if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) {
453 ::SendMessage(win, BFFM_ENABLEOK, 0, 1);
454 } else {
455 // disable ok button if not a path
456 ::SendMessage(win, BFFM_ENABLEOK, 0, 0);
458 break;
459 case BFFM_VALIDATEFAILED:
460 // we could pop up an annoying message here.
461 // also needs set ulFlags |= BIF_VALIDATE
462 break;
463 default:
464 break;
466 return(0);
469 // SHOW DIRECTORY BROWSER
470 int Fl_Native_File_Chooser::showdir() {
471 // initialize OLE only once
472 fl_OleInitialize(); // init needed by BIF_USENEWUI
473 ClearBINF();
474 clear_pathnames();
475 // PARENT WINDOW
476 _binf.hwndOwner = GetForegroundWindow();
477 // DIALOG TITLE
478 _binf.lpszTitle = _title ? _title : NULL;
479 // FLAGS
480 _binf.ulFlags = 0; // initialize
482 // TBD: make sure matches to runtime system, if need be.
483 //(what if _WIN32_IE doesn't match system? does the program not run?)
485 // TBD: match all 3 types of directories
487 // NOTE: *Don't* use BIF_SHAREABLE. It /disables/ mapped network shares
488 // from being visible in BROWSE_DIRECTORY mode.
489 // See Walter Garm's comments in ./TODO.
491 #if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0
492 if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_NONEWFOLDERBUTTON;
493 _binf.ulFlags |= BIF_USENEWUI | BIF_RETURNONLYFSDIRS;
494 #elif defined(BIF_USENEWUI) // Version 5.0
495 if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_EDITBOX;
496 else if ( _btype == BROWSE_SAVE_DIRECTORY ) _binf.ulFlags |= BIF_USENEWUI;
497 _binf.ulFlags |= BIF_RETURNONLYFSDIRS;
498 #elif defined(BIF_EDITBOX) // Version 4.71
499 _binf.ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX;
500 #else // Version Old
501 _binf.ulFlags |= BIF_RETURNONLYFSDIRS;
502 #endif
504 // BUFFER
505 char displayname[MAX_PATH];
506 _binf.pszDisplayName = displayname;
507 // PRESET DIR
508 char presetname[MAX_PATH];
509 if ( _directory ) {
510 strcpy(presetname, _directory);
511 // Unix2Win(presetname);
512 _binf.lParam = (LPARAM)presetname;
514 else _binf.lParam = 0;
515 _binf.lpfn = Dir_CB;
516 // OPEN BROWSER
517 ITEMIDLIST *pidl = SHBrowseForFolder(&_binf);
518 // CANCEL?
519 if ( pidl == NULL ) return(1);
521 // GET THE PATHNAME(S) THE USER SELECTED
522 // TBD: expand NetHood shortcuts from this PIDL??
523 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp
525 TCHAR path[MAX_PATH];
526 if ( SHGetPathFromIDList(pidl, path) ) {
527 // Win2Unix(path);
528 add_pathname(path);
530 FreePIDL(pidl);
531 if ( !strlen(path) ) return(1); // don't return empty pathnames
532 return(0);
535 // RETURNS:
536 // 0 - user picked a file
537 // 1 - user cancelled
538 // -1 - failed; errmsg() has reason
540 int Fl_Native_File_Chooser::show() {
541 if ( _btype == BROWSE_DIRECTORY ||
542 _btype == BROWSE_MULTI_DIRECTORY ||
543 _btype == BROWSE_SAVE_DIRECTORY ) {
544 return(showdir());
545 } else {
546 return(showfile());
550 // RETURN ERROR MESSAGE
551 const char *Fl_Native_File_Chooser::errmsg() const {
552 return(_errmsg ? _errmsg : "No error");
555 // GET FILENAME
556 const char* Fl_Native_File_Chooser::filename() const {
557 if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]);
558 return("");
561 // GET FILENAME FROM LIST OF FILENAMES
562 const char* Fl_Native_File_Chooser::filename(int i) const {
563 if ( _pathnames && i < _tpathnames ) return(_pathnames[i]);
564 return("");
567 // GET TOTAL FILENAMES CHOSEN
568 int Fl_Native_File_Chooser::count() const {
569 return(_tpathnames);
572 // PRESET PATHNAME
573 // Can be NULL if no preset is desired.
575 void Fl_Native_File_Chooser::directory(const char *val) {
576 _directory = strfree(_directory);
577 _directory = strnew(val);
580 // GET PRESET PATHNAME
581 // Can return NULL if none set.
583 const char *Fl_Native_File_Chooser::directory() const {
584 return(_directory);
587 // SET TITLE
588 // Can be NULL if no title desired.
590 void Fl_Native_File_Chooser::title(const char *val) {
591 _title = strfree(_title);
592 _title = strnew(val);
595 // GET TITLE
596 // Can return NULL if none set.
598 const char *Fl_Native_File_Chooser::title() const {
599 return(_title);
602 // SET FILTER
603 // Can be NULL if no filter needed
605 void Fl_Native_File_Chooser::filter(const char *val) {
606 _filter = strfree(_filter);
607 clear_filters();
608 if ( val ) {
609 _filter = strnew(val);
610 parse_filter(_filter);
612 add_filter("All Files", "*.*"); // always include 'all files' option
614 #ifdef DEBUG
615 nullprint(_parsedfilt);
616 #endif /*DEBUG*/
619 // GET FILTER
620 // Can return NULL if none set.
622 const char *Fl_Native_File_Chooser::filter() const {
623 return(_filter);
626 // CLEAR FILTERS
627 void Fl_Native_File_Chooser::clear_filters() {
628 _nfilters = 0;
629 _parsedfilt = strfree(_parsedfilt);
632 // ADD A FILTER
633 void Fl_Native_File_Chooser::add_filter(const char *name_in, // name of filter (optional: can be null)
634 const char *winfilter) { // windows style filter (eg. "*.cxx;*.h")
635 // No name? Make one..
636 char name[1024];
637 if ( !name_in || name_in[0] == '\0' ) {
638 sprintf(name, "%.*s Files", int(sizeof(name)-10), winfilter);
639 } else {
640 sprintf(name, "%.*s", int(sizeof(name)-10), name_in);
642 dnullcat(_parsedfilt, name);
643 dnullcat(_parsedfilt, winfilter);
644 _nfilters++;
645 //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter);
648 // CONVERT FLTK STYLE PATTERN MATCHES TO WINDOWS 'DOUBLENULL' PATTERN
649 // Handles:
650 // IN OUT
651 // ----------- -----------------------------
652 // *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0"
653 // *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0"
654 // *.txt "*.txt Files\0*.txt\0\0"
655 // C Files\t*.[ch] "C Files\0*.c;*.h\0\0"
657 // Example:
658 // IN: "*.{ma,mb}"
659 // OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0"
660 // --------------- --------- --------- ---
661 // | | | |
662 // Title Wildcards Title Wildcards
664 // Parsing Mode:
665 // IN:"C Files\t*.{cxx,h}"
666 // ||||||| |||||||||
667 // mode: nnnnnnn ww{{{{{{{
668 // \_____/ \_______/
669 // Name Wildcard
671 void Fl_Native_File_Chooser::parse_filter(const char *in) {
672 clear_filters();
673 if ( ! in ) return;
675 int has_name = strchr(in, '\t') ? 1 : 0;
677 char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard
678 int nwildcards = 0;
679 char wildcards[MAXFILTERS][1024]; // parsed wildcards (can be several)
680 char wildprefix[512] = "";
681 char name[512] = "";
683 // Init
684 int t;
685 for ( t=0; t<MAXFILTERS; t++ ) {
686 wildcards[t][0] = '\0';
689 // Parse
690 for ( ; 1; in++ ) {
692 //// DEBUG
693 //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n",
694 //// *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]);
696 switch (*in) {
697 case ',':
698 case '|':
699 if ( mode == LCURLY_CHR ) {
700 // create new wildcard, copy in prefix
701 strcat(wildcards[nwildcards++], wildprefix);
702 continue;
703 } else {
704 goto regchar;
706 continue;
708 // FINISHED PARSING A NAME?
709 case '\t':
710 if ( mode != 'n' ) goto regchar;
711 // finish parsing name? switch to wildcard mode
712 mode = 'w';
713 break;
715 // ESCAPE NEXT CHAR
716 case '\\':
717 ++in;
718 goto regchar;
720 // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
721 case '\r':
722 case '\n':
723 case '\0':
725 if ( mode == 'w' ) { // finished parsing wildcard?
726 if ( nwildcards == 0 ) {
727 strcpy(wildcards[nwildcards++], wildprefix);
729 // Append wildcards in Microsoft's "*.one;*.two" format
730 char comp[4096] = "";
731 for ( t=0; t<nwildcards; t++ ) {
732 if ( t != 0 ) strcat(comp, ";");
733 strcat(comp, wildcards[t]);
735 // Add if not empty
736 if ( comp[0] ) {
737 add_filter(name, comp);
740 // RESET
741 for ( t=0; t<MAXFILTERS; t++ ) {
742 wildcards[t][0] = '\0';
744 nwildcards = 0;
745 wildprefix[0] = name[0] = '\0';
746 mode = strchr(in,'\t') ? 'n' : 'w';
747 // DONE?
748 if ( *in == '\0' ) return; // done
749 continue; // not done yet, more filters
752 // STARTING A WILDCARD?
753 case LBRACKET_CHR:
754 case LCURLY_CHR:
755 mode = *in;
756 if ( *in == LCURLY_CHR ) {
757 // create new wildcard
758 strcat(wildcards[nwildcards++], wildprefix);
760 continue;
762 // ENDING A WILDCARD?
763 case RBRACKET_CHR:
764 case RCURLY_CHR:
765 mode = 'w'; // back to wildcard mode
766 continue;
768 // ALL OTHER NON-SPECIAL CHARACTERS
769 default:
770 regchar: // handle regular char
771 switch ( mode ) {
772 case LBRACKET_CHR:
773 // create new wildcard
774 ++nwildcards;
775 // copy in prefix
776 strcpy(wildcards[nwildcards-1], wildprefix);
777 // append search char
778 chrcat(wildcards[nwildcards-1], *in);
779 continue;
781 case LCURLY_CHR:
782 if ( nwildcards > 0 ) {
783 chrcat(wildcards[nwildcards-1], *in);
785 continue;
787 case 'n':
788 chrcat(name, *in);
789 continue;
791 case 'w':
792 chrcat(wildprefix, *in);
793 for ( t=0; t<nwildcards; t++ ) {
794 chrcat(wildcards[t], *in);
796 continue;
798 break;
803 // SET 'CURRENTLY SELECTED FILTER'
804 void Fl_Native_File_Chooser::filter_value(int i) {
805 _ofn.nFilterIndex = i + 1;
808 // RETURN VALUE OF 'CURRENTLY SELECTED FILTER'
809 int Fl_Native_File_Chooser::filter_value() const {
810 return(_ofn.nFilterIndex ? _ofn.nFilterIndex-1 : _nfilters+1);
813 // PRESET FILENAME FOR 'SAVE AS' CHOOSER
814 void Fl_Native_File_Chooser::preset_file(const char* val) {
815 _preset_file = strfree(_preset_file);
816 _preset_file = strnew(val);
819 // GET PRESET FILENAME FOR 'SAVE AS' CHOOSER
820 const char* Fl_Native_File_Chooser::preset_file() const {
821 return(_preset_file);
824 int Fl_Native_File_Chooser::filters() const {
825 return(_nfilters);
828 char *wchartoutf8(LPCWSTR in)
830 static char *out = NULL;
831 static int lchar = 0;
832 if (in == NULL)return NULL;
833 int utf8len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL);
834 if (utf8len > lchar) {
835 lchar = utf8len;
836 out = (char *)realloc(out, lchar * sizeof(char));
838 WideCharToMultiByte(CP_UTF8, 0, in, -1, out, utf8len, NULL, NULL);
839 return out;
842 LPCWSTR utf8towchar(const char *in)
844 static WCHAR *wout = NULL;
845 static int lwout = 0;
846 if (in == NULL)return NULL;
847 int wlen = MultiByteToWideChar(CP_UTF8, 0, in, -1, NULL, 0);
848 if (wlen > lwout) {
849 lwout = wlen;
850 wout = (WCHAR *)realloc(wout, lwout * sizeof(WCHAR));
852 MultiByteToWideChar(CP_UTF8, 0, in, -1, wout, wlen);
853 return wout;
856 #endif /*!FL_DOXYGEN*/
859 // End of "$Id: Fl_Native_File_Chooser_WIN32.cxx 8454 2011-02-20 21:46:11Z manolo $".