diff options
Diffstat (limited to 'src/lib/FL/Fl_Native_File_Chooser_WIN32.cxx')
-rw-r--r-- | src/lib/FL/Fl_Native_File_Chooser_WIN32.cxx | 765 |
1 files changed, 765 insertions, 0 deletions
diff --git a/src/lib/FL/Fl_Native_File_Chooser_WIN32.cxx b/src/lib/FL/Fl_Native_File_Chooser_WIN32.cxx new file mode 100644 index 0000000..226cf7b --- /dev/null +++ b/src/lib/FL/Fl_Native_File_Chooser_WIN32.cxx | |||
@@ -0,0 +1,765 @@ | |||
1 | // | ||
2 | // Fl_Native_File_Chooser_WINDOWS.cxx -- FLTK native OS file chooser widget | ||
3 | // | ||
4 | // Copyright 2004 by Greg Ercolano. | ||
5 | // April 2005 - API changes, improved filter processing by Nathan Vander Wilt | ||
6 | // | ||
7 | // This library is free software; you can redistribute it and/or | ||
8 | // modify it under the terms of the GNU Library General Public | ||
9 | // License as published by the Free Software Foundation; either | ||
10 | // version 2 of the License, or (at your option) any later version. | ||
11 | // | ||
12 | // This library is distributed in the hope that it will be useful, | ||
13 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | // Library General Public License for more details. | ||
16 | // | ||
17 | // You should have received a copy of the GNU Library General Public | ||
18 | // License along with this library; if not, write to the Free Software | ||
19 | // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | ||
20 | // USA. | ||
21 | // | ||
22 | // Please keep code 80 column compliant. | ||
23 | // | ||
24 | // 10 20 30 40 50 60 70 | ||
25 | // | | | | | | | | ||
26 | // 4567890123456789012345678901234567890123456789012345678901234567890123456789 | ||
27 | // | ||
28 | |||
29 | // http://www.codeproject.com/dialog/selectfolder.asp - any application to multi-folder implementation? | ||
30 | |||
31 | #include <FL/Fl_Native_File_Chooser.H> | ||
32 | #include <stdio.h> // debugging | ||
33 | #include "common.cxx" // strnew/strfree/strapp/chrcat | ||
34 | |||
35 | #define LCURLY_CHR '{' | ||
36 | #define RCURLY_CHR '}' | ||
37 | #define LBRACKET_CHR '[' | ||
38 | #define RBRACKET_CHR ']' | ||
39 | #ifndef BIF_NONEWFOLDERBUTTON | ||
40 | #define BIF_NONEWFOLDERBUTTON 0x200 | ||
41 | #endif | ||
42 | // STATIC: PRINT WINDOWS 'DOUBLE NULL' STRING (DEBUG) | ||
43 | /* | ||
44 | static void dnullprint(char *wp) { | ||
45 | if ( ! wp ) return; | ||
46 | for ( int t=0; true; t++ ) { | ||
47 | if ( wp[t] == '\0' && wp[t+1] == '\0' ) { | ||
48 | printf("\\0\\0"); | ||
49 | fflush(stdout); | ||
50 | return; | ||
51 | } else if ( wp[t] == '\0' ) { | ||
52 | printf("\\0"); | ||
53 | } else { | ||
54 | printf("%c",wp[t]); | ||
55 | } | ||
56 | } | ||
57 | } | ||
58 | */ | ||
59 | // RETURN LENGTH OF DOUBLENULL STRING | ||
60 | // Includes single nulls in count, excludes trailing doublenull. | ||
61 | // | ||
62 | // 1234 567 | ||
63 | // |||/\||| | ||
64 | // IN: "one\0two\0\0" | ||
65 | // OUT: 7 | ||
66 | // | ||
67 | static int dnulllen(const char *wp) { | ||
68 | int len = 0; | ||
69 | while ( ! ( *(wp+0) == 0 && *(wp+1) == 0 ) ) | ||
70 | { ++wp; ++len; } | ||
71 | return(len); | ||
72 | } | ||
73 | |||
74 | // STATIC: Append a string to another, leaving terminated with DOUBLE NULL. | ||
75 | // Automatically handles extending length of string. | ||
76 | // wp can be NULL (a new wp will be allocated and initialized). | ||
77 | // string must be NULL terminated. | ||
78 | // The pointer wp may be modified on return. | ||
79 | // | ||
80 | static void dnullcat(char*&wp, const char *string, int n = -1 ) { | ||
81 | //DEBUG printf("DEBUG: dnullcat IN: <"); dnullprint(wp); printf(">\n"); | ||
82 | int inlen = ( n < 0 ) ? strlen(string) : n; | ||
83 | if ( ! wp ) { | ||
84 | wp = new char[inlen + 4]; | ||
85 | *(wp+0) = '\0'; | ||
86 | *(wp+1) = '\0'; | ||
87 | } else { | ||
88 | int wplen = dnulllen(wp); | ||
89 | // Make copy of wp into larger buffer | ||
90 | char *tmp = new char[wplen + inlen + 4]; | ||
91 | memcpy(tmp, wp, wplen+2); // copy of wp plus doublenull | ||
92 | delete [] wp; // delete old wp | ||
93 | wp = tmp; // use new copy | ||
94 | //DEBUG printf("DEBUG: dnullcat COPY: <"); dnullprint(wp); printf("> (wplen=%d)\n", wplen); | ||
95 | } | ||
96 | |||
97 | // Find end of double null string | ||
98 | // *wp2 is left pointing at second null. | ||
99 | // | ||
100 | char *wp2 = wp; | ||
101 | if ( *(wp2+0) != '\0' && *(wp2+1) != '\0' ) { | ||
102 | for ( ; 1; wp2++ ) | ||
103 | if ( *(wp2+0) == '\0' && *(wp2+1) == '\0' ) | ||
104 | { wp2++; break; } | ||
105 | } | ||
106 | |||
107 | if ( n == -1 ) n = strlen(string); | ||
108 | strncpy(wp2, string, n); | ||
109 | |||
110 | // Leave string double-null terminated | ||
111 | *(wp2+n+0) = '\0'; | ||
112 | *(wp2+n+1) = '\0'; | ||
113 | //DEBUG printf("DEBUG: dnullcat OUT: <"); dnullprint(wp); printf(">\n\n"); | ||
114 | } | ||
115 | |||
116 | // CTOR | ||
117 | Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) { | ||
118 | _btype = val; | ||
119 | _options = NO_OPTIONS; | ||
120 | memset((void*)&_ofn, 0, sizeof(OPENFILENAME)); | ||
121 | _ofn.lStructSize = sizeof(OPENFILENAME); | ||
122 | _ofn.hwndOwner = NULL; | ||
123 | memset((void*)&_binf, 0, sizeof(BROWSEINFO)); | ||
124 | _pathnames = NULL; | ||
125 | _tpathnames = 0; | ||
126 | _directory = NULL; | ||
127 | _title = NULL; | ||
128 | _filter = NULL; | ||
129 | _parsedfilt = NULL; | ||
130 | _nfilters = 0; | ||
131 | _preset_file = NULL; | ||
132 | _errmsg = NULL; | ||
133 | } | ||
134 | |||
135 | // DTOR | ||
136 | Fl_Native_File_Chooser::~Fl_Native_File_Chooser() { | ||
137 | //_pathnames // managed by clear_pathnames() | ||
138 | //_tpathnames // managed by clear_pathnames() | ||
139 | _directory = strfree(_directory); | ||
140 | _title = strfree(_title); | ||
141 | _filter = strfree(_filter); | ||
142 | //_parsedfilt // managed by clear_filters() | ||
143 | //_nfilters // managed by clear_filters() | ||
144 | _preset_file = strfree(_preset_file); | ||
145 | _errmsg = strfree(_errmsg); | ||
146 | clear_filters(); | ||
147 | clear_pathnames(); | ||
148 | ClearOFN(); | ||
149 | ClearBINF(); | ||
150 | } | ||
151 | |||
152 | // SET TYPE OF BROWSER | ||
153 | void Fl_Native_File_Chooser::type(int val) { | ||
154 | _btype = val; | ||
155 | } | ||
156 | |||
157 | // GET TYPE OF BROWSER | ||
158 | int Fl_Native_File_Chooser::type() const { | ||
159 | return( _btype ); | ||
160 | } | ||
161 | |||
162 | // SET OPTIONS | ||
163 | void Fl_Native_File_Chooser::options(int val) { | ||
164 | _options = val; | ||
165 | } | ||
166 | |||
167 | // GET OPTIONS | ||
168 | int Fl_Native_File_Chooser::options() const { | ||
169 | return(_options); | ||
170 | } | ||
171 | |||
172 | // PRIVATE: SET ERROR MESSAGE | ||
173 | void Fl_Native_File_Chooser::errmsg(const char *val) { | ||
174 | _errmsg = strfree(_errmsg); | ||
175 | _errmsg = strnew(val); | ||
176 | } | ||
177 | |||
178 | // FREE PATHNAMES ARRAY, IF IT HAS ANY CONTENTS | ||
179 | void Fl_Native_File_Chooser::clear_pathnames() { | ||
180 | if ( _pathnames ) { | ||
181 | while ( --_tpathnames >= 0 ) { | ||
182 | _pathnames[_tpathnames] = strfree(_pathnames[_tpathnames]); | ||
183 | } | ||
184 | delete [] _pathnames; | ||
185 | _pathnames = NULL; | ||
186 | } | ||
187 | _tpathnames = 0; | ||
188 | } | ||
189 | |||
190 | // SET A SINGLE PATHNAME | ||
191 | void Fl_Native_File_Chooser::set_single_pathname(const char *s) { | ||
192 | clear_pathnames(); | ||
193 | _pathnames = new char*[1]; | ||
194 | _pathnames[0] = strnew(s); | ||
195 | _tpathnames = 1; | ||
196 | } | ||
197 | |||
198 | // ADD PATHNAME TO EXISTING ARRAY | ||
199 | void Fl_Native_File_Chooser::add_pathname(const char *s) { | ||
200 | if ( ! _pathnames ) { | ||
201 | // Create first element in array | ||
202 | ++_tpathnames; | ||
203 | _pathnames = new char*[_tpathnames]; | ||
204 | } else { | ||
205 | // Grow array by 1 | ||
206 | char **tmp = new char*[_tpathnames+1]; // create new buffer | ||
207 | memcpy((void*)tmp, (void*)_pathnames, sizeof(char*)*_tpathnames); // copy old | ||
208 | delete [] _pathnames; // delete old | ||
209 | _pathnames = tmp; // use new | ||
210 | ++_tpathnames; | ||
211 | } | ||
212 | _pathnames[_tpathnames-1] = strnew(s); | ||
213 | } | ||
214 | |||
215 | // FREE A PIDL (Pointer to IDentity List) | ||
216 | void Fl_Native_File_Chooser::FreePIDL(ITEMIDLIST *pidl) { | ||
217 | IMalloc *imalloc = NULL; | ||
218 | if ( SUCCEEDED(SHGetMalloc(&imalloc)) ) | ||
219 | { imalloc->Free(pidl); imalloc->Release(); imalloc = NULL; } | ||
220 | } | ||
221 | |||
222 | // CLEAR MICROSOFT OFN (OPEN FILE NAME) CLASS | ||
223 | void Fl_Native_File_Chooser::ClearOFN() { | ||
224 | int temp; | ||
225 | // Free any previously allocated lpstrFile before zeroing out _ofn | ||
226 | if ( _ofn.lpstrFile ) | ||
227 | { _ofn.lpstrFile = strfree((char*)_ofn.lpstrFile); } | ||
228 | if ( _ofn.lpstrInitialDir ) | ||
229 | { _ofn.lpstrInitialDir = (LPCSTR)strfree((char*)_ofn.lpstrInitialDir); } | ||
230 | if ( _ofn.lpstrFile ) | ||
231 | { _ofn.lpstrFile = strfree(_ofn.lpstrFile); } | ||
232 | _ofn.lpstrFilter = NULL; // (deleted elsewhere) | ||
233 | temp = _ofn.nFilterIndex; // keep the filter_value | ||
234 | memset((void*)&_ofn, 0, sizeof(_ofn)); | ||
235 | _ofn.lStructSize = sizeof(OPENFILENAME); | ||
236 | _ofn.nFilterIndex = temp; | ||
237 | } | ||
238 | |||
239 | // CLEAR MICROSOFT BINF (BROWSER INFO) CLASS | ||
240 | void Fl_Native_File_Chooser::ClearBINF() { | ||
241 | if ( _binf.pidlRoot ) { | ||
242 | FreePIDL((ITEMIDLIST*)_binf.pidlRoot); | ||
243 | _binf.pidlRoot = NULL; | ||
244 | } | ||
245 | memset((void*)&_binf, 0, sizeof(_binf)); | ||
246 | } | ||
247 | |||
248 | // CONVERT WINDOWS BACKSLASHES TO UNIX FRONTSLASHES | ||
249 | void Fl_Native_File_Chooser::Win2Unix(char *s) { | ||
250 | for ( ; *s; s++ ) | ||
251 | if ( *s == '\\' ) *s = '/'; | ||
252 | } | ||
253 | |||
254 | // CONVERT UNIX FRONTSLASHES TO WINDOWS BACKSLASHES | ||
255 | void Fl_Native_File_Chooser::Unix2Win(char *s) { | ||
256 | for ( ; *s; s++ ) | ||
257 | if ( *s == '/' ) *s = '\\'; | ||
258 | } | ||
259 | |||
260 | // SHOW FILE BROWSER | ||
261 | int Fl_Native_File_Chooser::showfile() { | ||
262 | ClearOFN(); | ||
263 | clear_pathnames(); | ||
264 | size_t fsize = 2048; | ||
265 | _ofn.Flags |= OFN_NOVALIDATE; // prevent disabling of front slashes | ||
266 | _ofn.Flags |= OFN_HIDEREADONLY; // hide goofy readonly flag | ||
267 | // USE NEW BROWSER | ||
268 | _ofn.Flags |= OFN_EXPLORER; // use newer explorer windows | ||
269 | // // USE OLD BROWSER | ||
270 | // _ofn.lpfnHook = MyHook; | ||
271 | // _ofn.Flags |= OFN_ENABLEHOOK; | ||
272 | _ofn.Flags |= OFN_ENABLESIZING; // allow window to be resized (hey, why not?) | ||
273 | |||
274 | switch ( _btype ) { | ||
275 | case BROWSE_DIRECTORY: | ||
276 | case BROWSE_MULTI_DIRECTORY: | ||
277 | case BROWSE_SAVE_DIRECTORY: | ||
278 | abort(); // never happens: handled by showdir() | ||
279 | case BROWSE_FILE: | ||
280 | fsize = 65536; // XXX: there must be a better way | ||
281 | break; | ||
282 | case BROWSE_MULTI_FILE: | ||
283 | _ofn.Flags |= OFN_ALLOWMULTISELECT; | ||
284 | fsize = 65536; // XXX: there must be a better way | ||
285 | break; | ||
286 | case BROWSE_SAVE_FILE: | ||
287 | if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) { | ||
288 | _ofn.Flags |= OFN_OVERWRITEPROMPT; | ||
289 | } | ||
290 | break; | ||
291 | } | ||
292 | // SPACE FOR RETURNED FILENAME | ||
293 | _ofn.lpstrFile = new char[fsize]; | ||
294 | _ofn.nMaxFile = fsize-1; | ||
295 | _ofn.lpstrFile[0] = '\0'; | ||
296 | _ofn.lpstrFile[1] = '\0'; | ||
297 | // PARENT WINDOW | ||
298 | _ofn.hwndOwner = GetForegroundWindow(); | ||
299 | // DIALOG TITLE | ||
300 | _ofn.lpstrTitle = _title ? _title : NULL; | ||
301 | // FILTER | ||
302 | _ofn.lpstrFilter = _parsedfilt ? _parsedfilt : NULL; | ||
303 | // PRESET FILE | ||
304 | // If set, supercedes _directory. See KB Q86920 for details | ||
305 | // | ||
306 | if ( _preset_file ) { | ||
307 | int len = strlen(_preset_file); | ||
308 | char *strfile = new char[MAX_PATH]; // as per KB 222003 >8( | ||
309 | strcpy(strfile, _preset_file); | ||
310 | strfile[len+0] = '\0'; // (multiselect needs dnull) | ||
311 | strfile[len+1] = '\0'; | ||
312 | Unix2Win(strfile); | ||
313 | _ofn.lpstrFile = strfile; | ||
314 | _ofn.nMaxFile = MAX_PATH; // as per KB 222003 >8( | ||
315 | } | ||
316 | if ( _directory ) { | ||
317 | // PRESET DIR | ||
318 | // XXX: See KB Q86920 for doc bug: | ||
319 | // http://support.microsoft.com/default.aspx?scid=kb;en-us;86920 | ||
320 | // | ||
321 | if ( _directory ) { | ||
322 | _ofn.lpstrInitialDir = strnew(_directory); | ||
323 | Unix2Win((char*)_ofn.lpstrInitialDir); | ||
324 | } | ||
325 | } | ||
326 | // OPEN THE DIALOG WINDOW | ||
327 | int err; | ||
328 | if ( _btype == BROWSE_SAVE_FILE ) { | ||
329 | err = GetSaveFileName(&_ofn); | ||
330 | } else { | ||
331 | err = GetOpenFileName(&_ofn); | ||
332 | } | ||
333 | if ( err == 0 ) { | ||
334 | // EXTENDED ERROR CHECK | ||
335 | int err = CommDlgExtendedError(); | ||
336 | // CANCEL? | ||
337 | if ( err == 0 ) | ||
338 | return(1); // user hit 'cancel' | ||
339 | // AN ERROR OCCURRED | ||
340 | char msg[80]; | ||
341 | sprintf(msg, "CommDlgExtendedError() code=%d", err); | ||
342 | errmsg(msg); | ||
343 | return(-1); | ||
344 | } | ||
345 | // PREPARE PATHNAMES FOR RETURN | ||
346 | switch ( _btype ) { | ||
347 | case BROWSE_FILE: | ||
348 | case BROWSE_SAVE_FILE: | ||
349 | set_single_pathname(_ofn.lpstrFile); | ||
350 | Win2Unix(_pathnames[_tpathnames-1]); | ||
351 | break; | ||
352 | case BROWSE_MULTI_FILE: { | ||
353 | // EXTRACT MULTIPLE FILENAMES | ||
354 | const char *dirname = _ofn.lpstrFile; | ||
355 | int dirlen = strlen(dirname); | ||
356 | if ( dirlen > 0 ) { | ||
357 | char pathname[2048]; | ||
358 | |||
359 | // WALK STRING SEARCHING FOR 'DOUBLE-NULL' | ||
360 | // eg. "/dir/name\0foo1\0foo2\0foo3\0\0" | ||
361 | // | ||
362 | for ( const char *s = _ofn.lpstrFile + dirlen + 1; | ||
363 | *s; s+= (strlen(s)+1)) { | ||
364 | strcpy(pathname, dirname); | ||
365 | strcat(pathname, "\\"); | ||
366 | strcat(pathname, s); | ||
367 | add_pathname(pathname); | ||
368 | Win2Unix(_pathnames[_tpathnames-1]); | ||
369 | } | ||
370 | } | ||
371 | // XXX | ||
372 | // Work around problem where pasted forward-slash pathname | ||
373 | // into the file browser causes new "Explorer" interface | ||
374 | // not to grok forward slashes, passing back as a 'filename'..! | ||
375 | // | ||
376 | if ( _tpathnames == 0 ) { | ||
377 | add_pathname(dirname); | ||
378 | Win2Unix(_pathnames[_tpathnames-1]); | ||
379 | } | ||
380 | break; | ||
381 | } | ||
382 | case BROWSE_DIRECTORY: | ||
383 | case BROWSE_MULTI_DIRECTORY: | ||
384 | case BROWSE_SAVE_DIRECTORY: | ||
385 | abort(); // not here | ||
386 | } | ||
387 | return(0); | ||
388 | } | ||
389 | |||
390 | // Used by SHBrowseForFolder(), sets initial selected dir. | ||
391 | // Ref: Usenet: microsoft.public.vc.mfc, Dec 8 2000, 1:38p David Lowndes | ||
392 | // Subject: How to specify to select an initial folder .." | ||
393 | // | ||
394 | int CALLBACK Fl_Native_File_Chooser::Dir_CB(HWND win, UINT msg, LPARAM param, LPARAM data) { | ||
395 | switch (msg) { | ||
396 | case BFFM_INITIALIZED: | ||
397 | if (data) ::SendMessage(win, BFFM_SETSELECTION, TRUE, data); | ||
398 | break; | ||
399 | case BFFM_SELCHANGED: | ||
400 | TCHAR path[MAX_PATH]; | ||
401 | if ( SHGetPathFromIDList((ITEMIDLIST*)param, path) ) { | ||
402 | ::SendMessage(win, BFFM_ENABLEOK, 0, 1); | ||
403 | } else { | ||
404 | //disable ok button if not a path | ||
405 | ::SendMessage(win, BFFM_ENABLEOK, 0, 0); | ||
406 | } | ||
407 | break; | ||
408 | case BFFM_VALIDATEFAILED: | ||
409 | // we could pop up an annoying message here. | ||
410 | // also needs set ulFlags |= BIF_VALIDATE | ||
411 | break; | ||
412 | default: | ||
413 | break; | ||
414 | } | ||
415 | return(0); | ||
416 | } | ||
417 | |||
418 | // SHOW DIRECTORY BROWSER | ||
419 | int Fl_Native_File_Chooser::showdir() { | ||
420 | OleInitialize(NULL); // init needed by BIF_USENEWUI | ||
421 | ClearBINF(); | ||
422 | clear_pathnames(); | ||
423 | // PARENT WINDOW | ||
424 | _binf.hwndOwner = GetForegroundWindow(); | ||
425 | // DIALOG TITLE | ||
426 | _binf.lpszTitle = _title ? _title : NULL; | ||
427 | // FLAGS | ||
428 | _binf.ulFlags =0; // initialize | ||
429 | |||
430 | // TBD: make sure matches to runtime system, if need be. | ||
431 | //( what if _WIN32_IE doesn't match system? does the program not run? ) | ||
432 | // TBD: match all 3 types of directories | ||
433 | |||
434 | #if defined(BIF_NONEWFOLDERBUTTON) // Version 6.0 | ||
435 | if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags &= !BIF_NONEWFOLDERBUTTON; | ||
436 | _binf.ulFlags |= BIF_USENEWUI | BIF_SHAREABLE | BIF_RETURNONLYFSDIRS; | ||
437 | #elif defined(BIF_USENEWUI) // Version 5.0 | ||
438 | if ( _btype == BROWSE_DIRECTORY ) _binf.ulFlags |= BIF_EDITBOX; | ||
439 | else if ( _btype == BROWSE_SAVE_DIRECTORY ) _binf.ulFlags |= BIF_USENEWUI; | ||
440 | _binf.ulFlags |= BIF_SHAREABLE | BIF_RETURNONLYFSDIRS; | ||
441 | #elif defined(BIF_EDITBOX) // Version 4.71 | ||
442 | _binf.ulFlags |= BIF_RETURNONLYFSDIRS | BIF_EDITBOX; | ||
443 | #else // Version Old | ||
444 | _binf.ulFlags |= BIF_RETURNONLYFSDIRS; | ||
445 | #endif | ||
446 | |||
447 | |||
448 | // BUFFER | ||
449 | char displayname[MAX_PATH]; | ||
450 | _binf.pszDisplayName = displayname; | ||
451 | // PRESET DIR | ||
452 | char presetname[MAX_PATH]; | ||
453 | if ( _directory ) { | ||
454 | strcpy(presetname, _directory); | ||
455 | Unix2Win(presetname); | ||
456 | _binf.lParam = (LPARAM)presetname; | ||
457 | } | ||
458 | else _binf.lParam = 0; | ||
459 | _binf.lpfn = Dir_CB; | ||
460 | // OPEN BROWSER | ||
461 | ITEMIDLIST *pidl = SHBrowseForFolder(&_binf); | ||
462 | // CANCEL? | ||
463 | if ( pidl == NULL ) return(1); | ||
464 | |||
465 | // GET THE PATHNAME(S) THE USER SELECTED | ||
466 | // TBD: expand NetHood shortcuts from this PIDL?? | ||
467 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/shell/reference/functions/shbrowseforfolder.asp | ||
468 | |||
469 | TCHAR path[MAX_PATH]; | ||
470 | if ( SHGetPathFromIDList(pidl, path) ) | ||
471 | { Win2Unix(path); add_pathname(path); } | ||
472 | FreePIDL(pidl); | ||
473 | if ( !strlen(path) ) return(1); // don't return empty pathnames | ||
474 | return(0); | ||
475 | } | ||
476 | |||
477 | // RETURNS: | ||
478 | // 0 - user picked a file | ||
479 | // 1 - user cancelled | ||
480 | // -1 - failed; errmsg() has reason | ||
481 | // | ||
482 | int Fl_Native_File_Chooser::show() { | ||
483 | if ( _btype == BROWSE_DIRECTORY || | ||
484 | _btype == BROWSE_MULTI_DIRECTORY || | ||
485 | _btype == BROWSE_SAVE_DIRECTORY ) | ||
486 | return(showdir()); | ||
487 | else | ||
488 | return(showfile()); | ||
489 | } | ||
490 | |||
491 | // RETURN ERROR MESSAGE | ||
492 | const char *Fl_Native_File_Chooser::errmsg() const { | ||
493 | return(_errmsg ? _errmsg : "No error"); | ||
494 | } | ||
495 | |||
496 | // GET FILENAME | ||
497 | const char* Fl_Native_File_Chooser::filename() const { | ||
498 | if ( _pathnames && _tpathnames > 0 ) return(_pathnames[0]); | ||
499 | return(""); | ||
500 | } | ||
501 | |||
502 | // GET FILENAME FROM LIST OF FILENAMES | ||
503 | const char* Fl_Native_File_Chooser::filename(int i) const { | ||
504 | if ( _pathnames && i < _tpathnames ) return(_pathnames[i]); | ||
505 | return(""); | ||
506 | } | ||
507 | |||
508 | // GET TOTAL FILENAMES CHOSEN | ||
509 | int Fl_Native_File_Chooser::count() const { | ||
510 | return(_tpathnames); | ||
511 | } | ||
512 | |||
513 | // PRESET PATHNAME | ||
514 | // Can be NULL if no preset is desired. | ||
515 | // | ||
516 | void Fl_Native_File_Chooser::directory(const char *val) { | ||
517 | _directory = strfree(_directory); | ||
518 | _directory = strnew(val); | ||
519 | } | ||
520 | |||
521 | // GET PRESET PATHNAME | ||
522 | // Can return NULL if none set. | ||
523 | // | ||
524 | const char *Fl_Native_File_Chooser::directory() const { | ||
525 | return(_directory); | ||
526 | } | ||
527 | |||
528 | // SET TITLE | ||
529 | // Can be NULL if no title desired. | ||
530 | // | ||
531 | void Fl_Native_File_Chooser::title(const char *val) { | ||
532 | _title = strfree(_title); | ||
533 | _title = strnew(val); | ||
534 | } | ||
535 | |||
536 | // GET TITLE | ||
537 | // Can return NULL if none set. | ||
538 | // | ||
539 | const char *Fl_Native_File_Chooser::title() const { | ||
540 | return(_title); | ||
541 | } | ||
542 | |||
543 | // SET FILTER | ||
544 | // Can be NULL if no filter needed | ||
545 | // | ||
546 | void Fl_Native_File_Chooser::filter(const char *val) { | ||
547 | _filter = strfree(_filter); | ||
548 | clear_filters(); | ||
549 | if ( val ) { | ||
550 | _filter = strnew(val); | ||
551 | parse_filter(_filter); | ||
552 | } | ||
553 | add_filter("All Files", "*.*"); // always include 'all files' option | ||
554 | |||
555 | #ifdef DEBUG | ||
556 | nullprint(_parsedfilt); | ||
557 | #endif /*DEBUG*/ | ||
558 | } | ||
559 | |||
560 | // GET FILTER | ||
561 | // Can return NULL if none set. | ||
562 | // | ||
563 | const char *Fl_Native_File_Chooser::filter() const { | ||
564 | return(_filter); | ||
565 | } | ||
566 | |||
567 | // CLEAR FILTERS | ||
568 | void Fl_Native_File_Chooser::clear_filters() { | ||
569 | _nfilters = 0; | ||
570 | _parsedfilt = strfree(_parsedfilt); | ||
571 | } | ||
572 | |||
573 | // ADD A FILTER | ||
574 | void Fl_Native_File_Chooser::add_filter( | ||
575 | const char *name_in, // name of filter (optional: can be null) | ||
576 | const char *winfilter // windows style filter (eg. "*.cxx;*.h") | ||
577 | ) { | ||
578 | // No name? Make one.. | ||
579 | char name[1024]; | ||
580 | if ( !name_in || name_in[0] == '\0' ) { | ||
581 | sprintf(name, "%.*s Files", sizeof(name)-10, winfilter); | ||
582 | } else { | ||
583 | sprintf(name, "%.*s", sizeof(name)-10, name_in); | ||
584 | } | ||
585 | dnullcat(_parsedfilt, name); | ||
586 | dnullcat(_parsedfilt, winfilter); | ||
587 | //DEBUG printf("DEBUG: ADD FILTER name=<%s> winfilter=<%s>\n", name, winfilter); | ||
588 | } | ||
589 | |||
590 | // CONVERT FLTK STYLE PATTERN MATCHES TO WINDOWS 'DOUBLENULL' PATTERN | ||
591 | // Handles: | ||
592 | // IN OUT | ||
593 | // ----------- ----------------------------- | ||
594 | // *.{ma,mb} "*.{ma,mb} Files\0*.ma;*.mb\0\0" | ||
595 | // *.[abc] "*.[abc] Files\0*.a;*.b;*.c\0\0" | ||
596 | // *.txt "*.txt Files\0*.txt\0\0" | ||
597 | // C Files\t*.[ch] "C Files\0*.c;*.h\0\0" | ||
598 | // | ||
599 | // Example: | ||
600 | // IN: "*.{ma,mb}" | ||
601 | // OUT: "*.ma;*.mb Files\0*.ma;*.mb\0All Files\0*.*\0\0" | ||
602 | // --------------- --------- --------- --- | ||
603 | // | | | | | ||
604 | // Title Wildcards Title Wildcards | ||
605 | // | ||
606 | // Parsing Mode: | ||
607 | // IN:"C Files\t*.{cxx,h}" | ||
608 | // ||||||| ||||||||| | ||
609 | // mode: nnnnnnn ww{{{{{{{ | ||
610 | // \_____/ \_______/ | ||
611 | // Name Wildcard | ||
612 | // | ||
613 | void Fl_Native_File_Chooser::parse_filter(const char *in) { | ||
614 | clear_filters(); | ||
615 | if ( ! in ) return; | ||
616 | |||
617 | int has_name = strchr(in, '\t') ? 1 : 0; | ||
618 | //const char *savein = in; | ||
619 | |||
620 | char mode = has_name ? 'n' : 'w'; // parse mode: n=name, w=wildcard | ||
621 | int nwildcards = 0; | ||
622 | char wildcards[MAXFILTERS][1024]; // parsed wildcards (can be several) | ||
623 | char wildprefix[512] = ""; | ||
624 | char name[512] = ""; | ||
625 | |||
626 | // Init | ||
627 | int t; | ||
628 | for ( t=0; t<MAXFILTERS; t++ ) { | ||
629 | wildcards[t][0] = '\0'; | ||
630 | } | ||
631 | |||
632 | // Parse | ||
633 | for ( ; 1; in++ ) { | ||
634 | |||
635 | //// DEBUG | ||
636 | //// printf("WORKING ON '%c': mode=<%c> name=<%s> wildprefix=<%s> nwildcards=%d wildcards[n]=<%s>\n", | ||
637 | //// *in, mode, name, wildprefix, nwildcards, wildcards[nwildcards]); | ||
638 | |||
639 | switch (*in) { | ||
640 | case ',': | ||
641 | case '|': | ||
642 | if ( mode == LCURLY_CHR ) { | ||
643 | // create new wildcard, copy in prefix | ||
644 | strcat(wildcards[nwildcards++], wildprefix); | ||
645 | continue; | ||
646 | } else { | ||
647 | goto regchar; | ||
648 | } | ||
649 | continue; | ||
650 | |||
651 | // FINISHED PARSING A NAME? | ||
652 | case '\t': | ||
653 | if ( mode != 'n' ) goto regchar; | ||
654 | // finish parsing name? switch to wildcard mode | ||
655 | mode = 'w'; | ||
656 | break; | ||
657 | |||
658 | // ESCAPE NEXT CHAR | ||
659 | case '\\': | ||
660 | ++in; | ||
661 | goto regchar; | ||
662 | |||
663 | // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS? | ||
664 | case '\r': | ||
665 | case '\n': | ||
666 | case '\0': | ||
667 | { | ||
668 | if ( mode == 'w' ) { // finished parsing wildcard? | ||
669 | if ( nwildcards == 0 ) { | ||
670 | strcpy(wildcards[nwildcards++], wildprefix); | ||
671 | } | ||
672 | // Append wildcards in Microsoft's "*.one;*.two" format | ||
673 | char comp[4096] = ""; | ||
674 | for ( t=0; t<nwildcards; t++ ) { | ||
675 | if ( t != 0 ) strcat(comp, ";"); | ||
676 | strcat(comp, wildcards[t]); | ||
677 | } | ||
678 | // Add if not empty | ||
679 | if ( comp[0] ) { | ||
680 | add_filter(name, comp); | ||
681 | } | ||
682 | } | ||
683 | // RESET | ||
684 | for ( t=0; t<MAXFILTERS; t++ ) { | ||
685 | wildcards[t][0] = '\0'; | ||
686 | } | ||
687 | nwildcards = 0; | ||
688 | wildprefix[0] = name[0] = '\0'; | ||
689 | mode = strchr(in,'\t') ? 'n' : 'w'; | ||
690 | // DONE? | ||
691 | if ( *in == '\0' ) return; // done | ||
692 | continue; // not done yet, more filters | ||
693 | } | ||
694 | |||
695 | // STARTING A WILDCARD? | ||
696 | case LBRACKET_CHR: | ||
697 | case LCURLY_CHR: | ||
698 | mode = *in; | ||
699 | if ( *in == LCURLY_CHR ) { | ||
700 | // create new wildcard | ||
701 | strcat(wildcards[nwildcards++], wildprefix); | ||
702 | } | ||
703 | continue; | ||
704 | |||
705 | // ENDING A WILDCARD? | ||
706 | case RBRACKET_CHR: | ||
707 | case RCURLY_CHR: | ||
708 | mode = 'w'; // back to wildcard mode | ||
709 | continue; | ||
710 | |||
711 | // ALL OTHER NON-SPECIAL CHARACTERS | ||
712 | default: | ||
713 | regchar: // handle regular char | ||
714 | switch ( mode ) { | ||
715 | case LBRACKET_CHR: | ||
716 | // create new wildcard | ||
717 | ++nwildcards; | ||
718 | // copy in prefix | ||
719 | strcpy(wildcards[nwildcards-1], wildprefix); | ||
720 | // append search char | ||
721 | chrcat(wildcards[nwildcards-1], *in); | ||
722 | continue; | ||
723 | |||
724 | case LCURLY_CHR: | ||
725 | if ( nwildcards > 0 ) { | ||
726 | chrcat(wildcards[nwildcards-1], *in); | ||
727 | } | ||
728 | continue; | ||
729 | |||
730 | case 'n': | ||
731 | chrcat(name, *in); | ||
732 | continue; | ||
733 | |||
734 | case 'w': | ||
735 | chrcat(wildprefix, *in); | ||
736 | for ( t=0; t<nwildcards; t++ ) { | ||
737 | chrcat(wildcards[t], *in); | ||
738 | } | ||
739 | continue; | ||
740 | } | ||
741 | break; | ||
742 | } | ||
743 | } | ||
744 | } | ||
745 | |||
746 | // SET 'CURRENTLY SELECTED FILTER' | ||
747 | void Fl_Native_File_Chooser::filter_value(int i) { | ||
748 | _ofn.nFilterIndex = i + 1; | ||
749 | } | ||
750 | |||
751 | // RETURN VALUE OF 'CURRENTLY SELECTED FILTER' | ||
752 | int Fl_Native_File_Chooser::filter_value() const { | ||
753 | return(_ofn.nFilterIndex ? _ofn.nFilterIndex-1 : _nfilters+1); | ||
754 | } | ||
755 | |||
756 | // PRESET FILENAME FOR 'SAVE AS' CHOOSER | ||
757 | void Fl_Native_File_Chooser::preset_file(const char* val) { | ||
758 | _preset_file = strfree(_preset_file); | ||
759 | _preset_file = strnew(val); | ||
760 | } | ||
761 | |||
762 | // GET PRESET FILENAME FOR 'SAVE AS' CHOOSER | ||
763 | const char* Fl_Native_File_Chooser::preset_file() const { | ||
764 | return(_preset_file); | ||
765 | } | ||