2 // "$Id: Fl_File_Chooser2.cxx 8785 2011-06-06 16:11:22Z manolo $"
4 // More Fl_File_Chooser routines.
6 // Copyright 1999-2011 by Michael Sweet.
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 // fabien: ATTENTION: Only Out Of Source Gen. because cxx/H files are autogenerated by fluid.
29 /** \defgroup group_comdlg Common Dialogs classes and functions
32 /** \class Fl_File_Chooser
33 The Fl_File_Chooser widget displays a standard file selection
34 dialog that supports various selection modes.
36 \image html Fl_File_Chooser.jpg
37 \image latex Fl_File_Chooser.jpg "Fl_File_Chooser" width=12cm
39 The Fl_File_Chooser class also exports several static values
40 that may be used to localize or customize the appearance of all file chooser
43 <CENTER><TABLE BORDER="1">
46 <TH>Default value</TH>
49 <TD>add_favorites_label</TD>
50 <TD>"Add to Favorites"</TD>
53 <TD>all_files_label</TD>
54 <TD>"All Files (*)"</TD>
57 <TD>custom_filter_label</TD>
58 <TD>"Custom Filter"</TD>
61 <TD>existing_file_label</TD>
62 <TD>"Please choose an existing file!"</TD>
65 <TD>favorites_label</TD>
69 <TD>filename_label</TD>
73 <TD>filesystems_label</TD>
74 <TD>"My Computer" (WIN32)<BR>
75 "File Systems" (all others)</TD>
79 <TD>"Show hidden files:"</TD>
82 <TD>manage_favorites_label</TD>
83 <TD>"Manage Favorites"</TD>
86 <TD>new_directory_label</TD>
87 <TD>"New Directory?"</TD>
90 <TD>new_directory_tooltip</TD>
91 <TD>"Create a new directory."</TD>
94 <TD>preview_label</TD>
107 <TD>fl_numericsort</TD>
111 The Fl_File_Chooser::sort member specifies the sort function that is
112 used when loading the contents of a directory and can be customized
115 The Fl_File_Chooser class also exports the Fl_File_Chooser::newButton
116 and Fl_File_Chooser::previewButton widgets so that application developers
117 can control their appearance and use. For more complex customization,
118 consider copying the FLTK file chooser code and changing it accordingly.
122 /** \fn Fl_File_Chooser::Fl_File_Chooser(const char *pathname, const char *pattern, int type, const char *title)
123 The constructor creates the Fl_File_Chooser dialog shown.
124 The pathname argument can be a directory name or a
125 complete file name (in which case the corresponding file is highlighted
126 in the list and in the filename input field.)
128 The pattern argument can be a NULL
129 string or "*" to list all files, or it can be a
130 series of descriptions and filter strings separated by tab
131 characters (\\t). The format of filters is either
132 "Description text (patterns)" or just "patterns". A file chooser
133 that provides filters for HTML and image files might look like:
136 "HTML Files (*.html)\tImage Files (*.{bmp,gif,jpg,png})"
139 The file chooser will automatically add the "All Files (*)"
140 pattern to the end of the string you pass if you do not provide
141 one. The first filter in the string is the default filter.
143 See the FLTK documentation on fl_filename_match()
144 for the kinds of pattern strings that are supported.
146 The type argument can be one of the following:
148 \li \c SINGLE - allows the user to select a single, existing file.
149 \li \c MULTI - allows the user to select one or more existing files.
150 \li \c CREATE - allows the user to select a single, existing file or
151 specify a new filename.
152 \li \c DIRECTORY - allows the user to select a single, existing directory.
154 The title argument is used to set the title bar text for the
155 Fl_File_Chooser window.
158 /** \var Fl_File_Chooser::newButton
159 The "new directory" button is exported so that application developers
160 can control the appearance and use.
163 /** \var Fl_File_Chooser::previewButton
164 The "preview" button is exported so that application developers can
165 control the appearance and use.
168 /** \fn Fl_File_Chooser::~Fl_File_Chooser()
169 Destroys the widget and frees all memory used by it.*/
171 /** \fn void Fl_File_Chooser::color(Fl_Color c)
172 Sets the background color of the Fl_File_Browser list.*/
174 /** \fn Fl_Color Fl_File_Chooser::color()
175 Gets the background color of the Fl_File_Browser list.*/
177 /** \fn int Fl_File_Chooser::count()
178 Returns the number of selected files.*/
180 /** \fn void Fl_File_Chooser::directory(const char *pathname)
181 Sets the current directory.*/
183 /** \fn const char *Fl_File_Chooser::directory()
184 Gets the current directory.*/
186 /** \fn void Fl_File_Chooser::filter(const char *pattern)
187 Sets or gets the current filename filter patterns. The filter
188 patterns use fl_filename_match().
189 Multiple patterns can be used by separating them with tabs, like
190 <tt>"*.jpg\t*.png\t*.gif\t*"</tt>. In addition, you can provide
191 human-readable labels with the patterns inside parenthesis, like
192 <tt>"JPEG Files (*.jpg)\tPNG Files (*.png)\tGIF Files (*.gif)\tAll Files (*)"
195 Use filter(NULL) to show all files.
198 /** \fn const char *Fl_File_Chooser::filter()
199 See void filter(const char *pattern)*/
201 /** \fn void Fl_File_Chooser::filter_value(int f)
202 Sets the current filename filter selection.*/
204 /** \fn int Fl_File_Chooser::filter_value()
205 Gets the current filename filter selection.*/
207 /** \fn void Fl_File_Chooser::hide()
208 Hides the Fl_File_Chooser window.*/
210 /** \fn void Fl_File_Chooser::iconsize(uchar s)
211 Sets the size of the icons in the Fl_File_Browser. By
212 default the icon size is set to 1.5 times the textsize().
215 /** \fn uchar Fl_File_Chooser::iconsize()
216 Gets the size of the icons in the Fl_File_Browser. By
217 default the icon size is set to 1.5 times the textsize().
220 /** \fn void Fl_File_Chooser::label(const char *l)
221 Sets the title bar text for the Fl_File_Chooser.*/
223 /** \fn const char *Fl_File_Chooser::label()
224 Gets the title bar text for the Fl_File_Chooser.*/
226 /** \fn void Fl_File_Chooser::ok_label(const char *l)
227 Sets the label for the "ok" button in the Fl_File_Chooser.
230 /** \fn const char *Fl_File_Chooser::ok_label()
231 Gets the label for the "ok" button in the Fl_File_Chooser.
234 /** \fn int Fl_File_Chooser::preview() const
235 Returns the current state of the preview box. */
237 /** \fn void Fl_File_Chooser::rescan()
238 Reloads the current directory in the Fl_File_Browser.*/
240 /** \fn void Fl_File_Chooser::show()
241 Shows the Fl_File_Chooser window.*/
243 /** \fn void Fl_File_Chooser::textcolor(Fl_Color c)
244 Sets the current Fl_File_Browser text color.*/
246 /** \fn Fl_Color Fl_File_Chooser::textcolor()
247 Gets the current Fl_File_Browser text color.*/
249 /** \fn void Fl_File_Chooser::textfont(Fl_Font f)
250 Sets the current Fl_File_Browser text font.*/
252 /** \fn Fl_Font Fl_File_Chooser::textfont()
253 Gets the current Fl_File_Browser text font.*/
255 /** \fn void Fl_File_Chooser::textsize(Fl_Fontsize s)
256 Sets the current Fl_File_Browser text size.*/
258 /** \fn Fl_Fontsize Fl_File_Chooser::textsize()
259 Gets the current Fl_File_Browser text size.*/
261 /** \fn void Fl_File_Chooser::type(int t)
262 Sets the current type of Fl_File_Chooser.*/
264 /** \fn int Fl_File_Chooser::type()
265 Gets the current type of Fl_File_Chooser.*/
267 /** \fn void Fl_File_Chooser::value(const char *pathname)
268 Sets the current value of the selected file.
271 /** \fn const char *Fl_File_Chooser::value(int f)
272 Gets the current value of the selected file(s).
273 \p f is a \c 1-based index into a list of
274 file names. The number of selected files is returned by
275 Fl_File_Chooser::count().
277 This sample code loops through all selected files:
279 // Get list of filenames user selected from a MULTI chooser
280 for ( int t=1; t<=chooser->count(); t++ ) {
281 const char *filename = chooser->value(t);
287 /** \fn int Fl_File_Chooser::visible()
288 Returns 1 if the Fl_File_Chooser window is visible.*/
290 /** \fn Fl_Widget* Fl_File_Chooser::add_extra(Fl_Widget*)
291 Adds extra widget at the bottom of Fl_File_Chooser window.
292 Returns pointer for previous extra widget or NULL if not set previously.
293 If argument is NULL only remove previous extra widget.
295 \note Fl_File_Chooser does \b not delete extra widget in destructor!
296 To prevent memory leakage, don't forget to delete unused extra widgets
298 /** \fn int Fl_File_Chooser::shown()
299 Returns non-zero if the file chooser main window show() has been called (but not hide()
300 see Fl_Window::shown()
303 /** \fn void Fl_File_Chooser::callback(void (*cb)(Fl_File_Chooser *, void *), void *d = 0)
304 Sets the file chooser callback cb and associated data d */
306 /** \fn void Fl_File_Chooser::user_data(void *d)
307 Sets the file chooser user data d */
309 /** \fn void * Fl_File_Chooser::user_data() const
310 Gets the file chooser user data d */
312 /** \fn Fl_File_Browser* Fl_File_Chooser::browser()
313 returns a pointer to the underlying Fl_File_Browser object */
314 // *** END OF OUT OF SOURCE DOC ***
318 // Fl_File_Chooser::count() - Return the number of selected files.
319 // Fl_File_Chooser::directory() - Set the directory in the file chooser.
320 // Fl_File_Chooser::filter() - Set the filter(s) for the chooser.
321 // Fl_File_Chooser::newdir() - Make a new directory.
322 // Fl_File_Chooser::value() - Return a selected filename.
323 // Fl_File_Chooser::rescan() - Rescan the current directory.
324 // Fl_File_Chooser::favoritesButtonCB() - Handle favorites selections.
325 // Fl_File_Chooser::fileListCB() - Handle clicks (and double-clicks)
326 // in the Fl_File_Browser.
327 // Fl_File_Chooser::fileNameCB() - Handle text entry in the FileBrowser.
328 // Fl_File_Chooser::showChoiceCB() - Handle show selections.
329 // compare_dirnames() - Compare two directory names.
330 // quote_pathname() - Quote a pathname for a menu.
331 // unquote_pathname() - Unquote a pathname from a menu.
333 // Fl_File_Chooser::add_extra() - add extra widget at the bottom, return pointer
334 // to previous extra widget or NULL if none,
335 // If argument is NULL extra widget removed.
336 // NOTE! file chooser does't delete extra widget in
337 // destructor! To prevent memory leakage don't forget
338 // delete unused extra widgets by yourself.
342 // Include necessary headers.
345 #include <FL/Fl_File_Chooser.H>
346 #include <FL/filename.H>
347 #include <FL/fl_ask.H>
349 #include <FL/Fl_Shared_Image.H>
350 #include <FL/fl_draw.H>
354 #include "flstring.h"
356 #include <sys/types.h>
357 #include <sys/stat.h>
359 #if defined(WIN32) && ! defined (__CYGWIN__)
362 // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
363 // on Windows, which is supposed to be POSIX compliant...
364 # define access _access
365 # define mkdir _mkdir
366 // Apparently Borland C++ defines DIRECTORY in <direct.h>, which
367 // interfers with the Fl_File_Icon enumeration of the same name.
378 // File chooser label strings and sort function...
381 Fl_Preferences
Fl_File_Chooser::prefs_(Fl_Preferences::USER
, "fltk.org", "filechooser");
383 const char *Fl_File_Chooser::add_favorites_label
= "Add to Favorites";
384 const char *Fl_File_Chooser::all_files_label
= "All Files (*)";
385 const char *Fl_File_Chooser::custom_filter_label
= "Custom Filter";
386 const char *Fl_File_Chooser::existing_file_label
= "Please choose an existing file!";
387 const char *Fl_File_Chooser::favorites_label
= "Favorites";
388 const char *Fl_File_Chooser::filename_label
= "Filename:";
390 const char *Fl_File_Chooser::filesystems_label
= "My Computer";
392 const char *Fl_File_Chooser::filesystems_label
= "File Systems";
394 const char *Fl_File_Chooser::manage_favorites_label
= "Manage Favorites";
395 const char *Fl_File_Chooser::new_directory_label
= "New Directory?";
396 const char *Fl_File_Chooser::new_directory_tooltip
= "Create a new directory.";
397 const char *Fl_File_Chooser::preview_label
= "Preview";
398 const char *Fl_File_Chooser::save_label
= "Save";
399 const char *Fl_File_Chooser::show_label
= "Show:";
400 const char *Fl_File_Chooser::hidden_label
= "Show hidden files";
401 Fl_File_Sort_F
*Fl_File_Chooser::sort
= fl_numericsort
;
405 // Local functions...
408 static int compare_dirnames(const char *a
, const char *b
);
409 static void quote_pathname(char *, const char *, int);
410 static void unquote_pathname(char *, const char *, int);
414 // 'Fl_File_Chooser::count()' - Return the number of selected files.
417 int // O - Number of selected files
418 Fl_File_Chooser::count() {
419 int i
; // Looping var
420 int fcount
; // Number of selected files
421 const char *filename
; // Filename in input field or list
424 filename
= fileName
->value();
426 if (!(type_
& MULTI
)) {
427 // Check to see if the file name input field is blank...
428 if (!filename
|| !filename
[0]) return 0;
432 for (i
= 1, fcount
= 0; i
<= fileList
->size(); i
++)
433 if (fileList
->selected(i
)) {
434 // See if this file is a directory...
435 // matt: why would we do that? It is perfectly legal to select multiple
436 // directories in a DIR chooser. They are visually selected and value(i)
437 // returns all of them as expected
438 //filename = (char *)fileList->text(i);
440 //if (filename[strlen(filename) - 1] != '/')
444 if (fcount
) return fcount
;
445 else if (!filename
|| !filename
[0]) return 0;
451 // 'Fl_File_Chooser::directory()' - Set the directory in the file chooser.
455 Fl_File_Chooser::directory(const char *d
)// I - Directory to change to
457 char *dirptr
; // Pointer into directory
460 // printf("Fl_File_Chooser::directory(\"%s\")\n", d == NULL ? "(null)" : d);
462 // NULL == current directory
467 // See if the filename contains backslashes...
468 char *slash
; // Pointer to slashes
469 char fixpath
[FL_PATH_MAX
]; // Path with slashes converted
470 if (strchr(d
, '\\')) {
471 // Convert backslashes to slashes...
472 strlcpy(fixpath
, d
, sizeof(fixpath
));
474 for (slash
= strchr(fixpath
, '\\'); slash
; slash
= strchr(slash
+ 1, '\\'))
483 // Make the directory absolute...
484 #if (defined(WIN32) && ! defined(__CYGWIN__))|| defined(__EMX__)
485 if (d
[0] != '/' && d
[0] != '\\' && d
[1] != ':')
487 if (d
[0] != '/' && d
[0] != '\\')
488 #endif /* WIN32 || __EMX__ */
489 fl_filename_absolute(directory_
, d
);
491 strlcpy(directory_
, d
, sizeof(directory_
));
493 // Strip any trailing slash...
494 dirptr
= directory_
+ strlen(directory_
) - 1;
495 if ((*dirptr
== '/' || *dirptr
== '\\') && dirptr
> directory_
)
498 // See if we have a trailing .. or . in the filename...
499 dirptr
= directory_
+ strlen(directory_
) - 3;
500 if (dirptr
>= directory_
&& strcmp(dirptr
, "/..") == 0) {
501 // Yes, we have "..", so strip the trailing path...
503 while (dirptr
> directory_
) {
504 if (*dirptr
== '/') break;
508 if (dirptr
>= directory_
&& *dirptr
== '/')
510 } else if ((dirptr
+ 1) >= directory_
&& strcmp(dirptr
+ 1, "/.") == 0) {
511 // Strip trailing "."...
516 directory_
[0] = '\0';
519 // Rescan the directory...
526 // 'Fl_File_Chooser::favoritesButtonCB()' - Handle favorites selections.
530 Fl_File_Chooser::favoritesButtonCB()
532 int v
; // Current selection
533 char pathname
[FL_PATH_MAX
], // Pathname
534 menuname
[FL_PATH_MAX
]; // Menu name
537 v
= favoritesButton
->value();
540 // Add current directory to favorites...
541 if (getenv("HOME")) v
= favoritesButton
->size() - 5;
542 else v
= favoritesButton
->size() - 4;
544 sprintf(menuname
, "favorite%02d", v
);
546 prefs_
.set(menuname
, directory_
);
549 quote_pathname(menuname
, directory_
, sizeof(menuname
));
550 favoritesButton
->add(menuname
);
552 if (favoritesButton
->size() > 104) {
553 ((Fl_Menu_Item
*)favoritesButton
->menu())[0].deactivate();
556 // Manage favorites...
559 // Filesystems/My Computer
562 unquote_pathname(pathname
, favoritesButton
->text(v
), sizeof(pathname
));
569 // 'Fl_File_Chooser::favoritesCB()' - Handle favorites dialog.
573 Fl_File_Chooser::favoritesCB(Fl_Widget
*w
)
576 int i
; // Looping var
577 char name
[32], // Preference name
578 pathname
[1024]; // Directory in list
582 // Load the favorites list...
586 for (i
= 0; i
< 100; i
++) {
587 // Get favorite directory 0 to 99...
588 sprintf(name
, "favorite%02d", i
);
590 prefs_
.get(name
, pathname
, "", sizeof(pathname
));
592 // Stop on the first empty favorite...
593 if (!pathname
[0]) break;
595 // Add the favorite to the list...
596 favList
->add(pathname
,
597 Fl_File_Icon::find(pathname
, Fl_File_Icon::DIRECTORY
));
600 favUpButton
->deactivate();
601 favDeleteButton
->deactivate();
602 favDownButton
->deactivate();
603 favOkButton
->deactivate();
605 favWindow
->hotspot(favList
);
607 } else if (w
== favList
) {
608 i
= favList
->value();
610 if (i
> 1) favUpButton
->activate();
611 else favUpButton
->deactivate();
613 favDeleteButton
->activate();
615 if (i
< favList
->size()) favDownButton
->activate();
616 else favDownButton
->deactivate();
618 favUpButton
->deactivate();
619 favDeleteButton
->deactivate();
620 favDownButton
->deactivate();
622 } else if (w
== favUpButton
) {
623 i
= favList
->value();
625 favList
->insert(i
- 1, favList
->text(i
), favList
->data(i
));
626 favList
->remove(i
+ 1);
627 favList
->select(i
- 1);
629 if (i
== 2) favUpButton
->deactivate();
631 favDownButton
->activate();
633 favOkButton
->activate();
634 } else if (w
== favDeleteButton
) {
635 i
= favList
->value();
639 if (i
> favList
->size()) i
--;
642 if (i
< favList
->size()) favDownButton
->activate();
643 else favDownButton
->deactivate();
645 if (i
> 1) favUpButton
->activate();
646 else favUpButton
->deactivate();
648 if (!i
) favDeleteButton
->deactivate();
650 favOkButton
->activate();
651 } else if (w
== favDownButton
) {
652 i
= favList
->value();
654 favList
->insert(i
+ 2, favList
->text(i
), favList
->data(i
));
656 favList
->select(i
+ 1);
658 if ((i
+ 1) == favList
->size()) favDownButton
->deactivate();
660 favUpButton
->activate();
662 favOkButton
->activate();
663 } else if (w
== favOkButton
) {
664 // Copy the new list over...
665 for (i
= 0; i
< favList
->size(); i
++) {
666 // Set favorite directory 0 to 99...
667 sprintf(name
, "favorite%02d", i
);
669 prefs_
.set(name
, favList
->text(i
+ 1));
672 // Clear old entries as necessary...
673 for (; i
< 100; i
++) {
674 // Clear favorite directory 0 to 99...
675 sprintf(name
, "favorite%02d", i
);
677 prefs_
.get(name
, pathname
, "", sizeof(pathname
));
679 if (pathname
[0]) prefs_
.set(name
, "");
692 // 'Fl_File_Chooser::fileListCB()' - Handle clicks (and double-clicks) in the
697 Fl_File_Chooser::fileListCB()
699 char *filename
, // New filename
700 pathname
[FL_PATH_MAX
]; // Full pathname to file
703 filename
= (char *)fileList
->text(fileList
->value());
707 if (!directory_
[0]) {
708 strlcpy(pathname
, filename
, sizeof(pathname
));
709 } else if (strcmp(directory_
, "/") == 0) {
710 snprintf(pathname
, sizeof(pathname
), "/%s", filename
);
712 snprintf(pathname
, sizeof(pathname
), "%s/%s", directory_
, filename
);
715 if (Fl::event_clicks()) {
716 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
717 if ((strlen(pathname
) == 2 && pathname
[1] == ':') ||
718 _fl_filename_isdir_quick(pathname
))
720 if (_fl_filename_isdir_quick(pathname
))
721 #endif /* WIN32 || __EMX__ */
723 // Change directories...
726 // Reset the click count so that a click in the same spot won't
727 // be treated as a triple-click. We use a value of -1 because
728 // the next click will increment click count to 0, which is what
730 Fl::event_clicks(-1);
734 // Hide the window - picked the file...
736 if (callback_
) (*callback_
)(this, data_
);
741 // Check if the user clicks on a directory when picking files;
742 // if so, make sure only that item is selected...
743 filename
= pathname
+ strlen(pathname
) - 1;
745 if ((type_
& MULTI
) && !(type_
& DIRECTORY
)) {
746 if (*filename
== '/') {
747 // Clicked on a directory, deselect everything else...
748 int i
= fileList
->value();
749 fileList
->deselect();
752 // Clicked on a file - see if there are other directories selected...
755 for (i
= 1; i
<= fileList
->size(); i
++) {
756 if (i
!= fileList
->value() && fileList
->selected(i
)) {
757 temp
= fileList
->text(i
);
758 temp
+= strlen(temp
) - 1;
759 if (*temp
== '/') break; // Yes, selected directory
763 if (i
<= fileList
->size()) {
764 i
= fileList
->value();
765 fileList
->deselect();
770 // Strip any trailing slash from the directory name...
771 if (*filename
== '/') *filename
= '\0';
773 // puts("Setting fileName from fileListCB...");
774 fileName
->value(pathname
);
776 // Update the preview box...
777 Fl::remove_timeout((Fl_Timeout_Handler
)previewCB
, this);
778 Fl::add_timeout(1.0, (Fl_Timeout_Handler
)previewCB
, this);
780 // Do any callback that is registered...
781 if (callback_
) (*callback_
)(this, data_
);
783 // Activate the OK button as needed...
784 if (!_fl_filename_isdir_quick(pathname
) || (type_
& DIRECTORY
))
785 okButton
->activate();
787 okButton
->deactivate();
793 // 'Fl_File_Chooser::fileNameCB()' - Handle text entry in the FileBrowser.
797 Fl_File_Chooser::fileNameCB()
799 char *filename
, // New filename
800 *slash
, // Pointer to trailing slash
801 pathname
[FL_PATH_MAX
], // Full pathname to file
802 matchname
[FL_PATH_MAX
]; // Matching filename
803 int i
, // Looping var
804 min_match
, // Minimum number of matching chars
805 max_match
, // Maximum number of matching chars
806 num_files
, // Number of files in directory
807 first_line
; // First matching line
808 const char *file
; // File from directory
810 // puts("fileNameCB()");
811 // printf("Event: %s\n", fl_eventnames[Fl::event()]);
813 // Get the filename from the text field...
814 filename
= (char *)fileName
->value();
816 if (!filename
|| !filename
[0]) {
817 okButton
->deactivate();
821 // Expand ~ and $ variables as needed...
822 if (strchr(filename
, '~') || strchr(filename
, '$')) {
823 fl_filename_expand(pathname
, sizeof(pathname
), filename
);
828 // Make sure we have an absolute path...
829 #if (defined(WIN32) && !defined(__CYGWIN__)) || defined(__EMX__)
830 if (directory_
[0] != '\0' && filename
[0] != '/' &&
831 filename
[0] != '\\' &&
832 !(isalpha(filename
[0] & 255) && (!filename
[1] || filename
[1] == ':'))) {
834 if (directory_
[0] != '\0' && filename
[0] != '/') {
835 #endif /* WIN32 || __EMX__ */
836 fl_filename_absolute(pathname
, sizeof(pathname
), filename
);
838 fileName
->mark(fileName
->position()); // no selection after expansion
839 } else if (filename
!= pathname
) {
840 // Finally, make sure that we have a writable copy...
841 strlcpy(pathname
, filename
, sizeof(pathname
));
846 // Now process things according to the key pressed...
847 if (Fl::event_key() == FL_Enter
|| Fl::event_key() == FL_KP_Enter
) {
848 // Enter pressed - select or change directory...
849 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
850 if ((isalpha(pathname
[0] & 255) && pathname
[1] == ':' && !pathname
[2]) ||
851 (_fl_filename_isdir_quick(pathname
) &&
852 compare_dirnames(pathname
, directory_
))) {
854 if (_fl_filename_isdir_quick(pathname
) &&
855 compare_dirnames(pathname
, directory_
)) {
856 #endif /* WIN32 || __EMX__ */
858 } else if ((type_
& CREATE
) || access(pathname
, 0) == 0) {
859 if (!_fl_filename_isdir_quick(pathname
) || (type_
& DIRECTORY
)) {
860 // Update the preview box...
863 // Do any callback that is registered...
864 if (callback_
) (*callback_
)(this, data_
);
866 // Hide the window to signal things are done...
870 // File doesn't exist, so beep at and alert the user...
871 fl_alert("%s",existing_file_label
);
874 else if (Fl::event_key() != FL_Delete
&&
875 Fl::event_key() != FL_BackSpace
) {
876 // Check to see if the user has entered a directory...
877 if ((slash
= strrchr(pathname
, '/')) == NULL
)
878 slash
= strrchr(pathname
, '\\');
882 // Yes, change directories if necessary...
886 #if defined(WIN32) || defined(__EMX__)
887 if (strcasecmp(pathname
, directory_
) &&
888 (pathname
[0] || strcasecmp("/", directory_
))) {
890 if (strcmp(pathname
, directory_
) &&
891 (pathname
[0] || strcasecmp("/", directory_
))) {
892 #endif // WIN32 || __EMX__
893 int p
= fileName
->position();
894 int m
= fileName
->mark();
899 char tempname
[FL_PATH_MAX
];
901 snprintf(tempname
, sizeof(tempname
), "%s/%s", directory_
, filename
);
902 fileName
->value(tempname
);
903 strlcpy(pathname
, tempname
, sizeof(pathname
));
906 fileName
->position(p
, m
);
909 // Other key pressed - do filename completion as possible...
910 num_files
= fileList
->size();
911 min_match
= strlen(filename
);
912 max_match
= min_match
+ 1;
915 for (i
= 1; i
<= num_files
&& max_match
> min_match
; i
++) {
916 file
= fileList
->text(i
);
918 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
919 if (strncasecmp(filename
, file
, min_match
) == 0) {
921 if (strncmp(filename
, file
, min_match
) == 0) {
922 #endif // WIN32 || __EMX__
923 // OK, this one matches; check against the previous match
925 // First match; copy stuff over...
926 strlcpy(matchname
, file
, sizeof(matchname
));
927 max_match
= strlen(matchname
);
929 // Strip trailing /, if any...
930 if (matchname
[max_match
- 1] == '/') {
932 matchname
[max_match
] = '\0';
935 // And then make sure that the item is visible
936 fileList
->topline(i
);
939 // Succeeding match; compare to find maximum string match...
940 while (max_match
> min_match
)
941 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
942 if (strncasecmp(file
, matchname
, max_match
) == 0)
944 if (strncmp(file
, matchname
, max_match
) == 0)
945 #endif // WIN32 || __EMX__
950 // Truncate the string as needed...
951 matchname
[max_match
] = '\0';
956 // If we have any matches, add them to the input field...
957 if (first_line
> 0 && min_match
== max_match
&&
958 max_match
== (int)strlen(fileList
->text(first_line
))) {
959 // This is the only possible match...
960 fileList
->deselect(0);
961 fileList
->select(first_line
);
963 } else if (max_match
> min_match
&& first_line
) {
964 // Add the matching portion...
965 fileName
->replace(filename
- pathname
, filename
- pathname
+ min_match
,
968 // Highlight it with the cursor at the end of the selection so
969 // s/he can press the right arrow to accept the selection
970 // (Tab and End also do this for both cases.)
971 fileName
->position(filename
- pathname
+ max_match
,
972 filename
- pathname
+ min_match
);
973 } else if (max_match
== 0) {
974 fileList
->deselect(0);
978 // See if we need to enable the OK button...
979 if (((type_
& CREATE
) || !access(fileName
->value(), 0)) &&
980 (!fl_filename_isdir(fileName
->value()) || (type_
& DIRECTORY
))) {
981 okButton
->activate();
983 okButton
->deactivate();
986 // FL_Delete or FL_BackSpace
987 fileList
->deselect(0);
989 if (((type_
& CREATE
) || !access(fileName
->value(), 0)) &&
990 (!fl_filename_isdir(fileName
->value()) || (type_
& DIRECTORY
))) {
991 okButton
->activate();
993 okButton
->deactivate();
1000 // 'Fl_File_Chooser::filter()' - Set the filter(s) for the chooser.
1004 Fl_File_Chooser::filter(const char *p
) // I - Pattern(s)
1006 char *copyp
, // Copy of pattern
1007 *start
, // Start of pattern
1008 *end
; // End of pattern
1009 int allfiles
; // Do we have a "*" pattern?
1010 char temp
[FL_PATH_MAX
]; // Temporary pattern string
1013 // Make sure we have a pattern...
1014 if (!p
|| !*p
) p
= "*";
1016 // Copy the pattern string...
1019 // Separate the pattern string as necessary...
1020 showChoice
->clear();
1022 for (start
= copyp
, allfiles
= 0; start
&& *start
; start
= end
) {
1023 end
= strchr(start
, '\t');
1024 if (end
) *end
++ = '\0';
1026 if (strcmp(start
, "*") == 0) {
1027 showChoice
->add(all_files_label
);
1030 quote_pathname(temp
, start
, sizeof(temp
));
1031 showChoice
->add(temp
);
1032 if (strstr(start
, "(*)") != NULL
) allfiles
= 1;
1038 if (!allfiles
) showChoice
->add(all_files_label
);
1040 showChoice
->add(custom_filter_label
);
1042 showChoice
->value(0);
1048 // 'Fl_File_Chooser::newdir()' - Make a new directory.
1052 Fl_File_Chooser::newdir()
1054 const char *dir
; // New directory name
1055 char pathname
[FL_PATH_MAX
]; // Full path of directory
1058 // Get a directory name from the user
1059 if ((dir
= fl_input("%s", NULL
, new_directory_label
)) == NULL
)
1062 // Make it relative to the current directory as needed...
1063 #if (defined(WIN32) && ! defined (__CYGWIN__)) || defined(__EMX__)
1064 if (dir
[0] != '/' && dir
[0] != '\\' && dir
[1] != ':')
1066 if (dir
[0] != '/' && dir
[0] != '\\')
1067 #endif /* WIN32 || __EMX__ */
1068 snprintf(pathname
, sizeof(pathname
), "%s/%s", directory_
, dir
);
1070 strlcpy(pathname
, dir
, sizeof(pathname
));
1072 // Create the directory; ignore EEXIST errors...
1073 #if defined(WIN32) && ! defined (__CYGWIN__)
1074 if (mkdir(pathname
))
1076 if (mkdir(pathname
, 0777))
1078 if (errno
!= EEXIST
)
1080 fl_alert("%s", strerror(errno
));
1084 // Show the new directory...
1085 directory(pathname
);
1090 /** Enable or disable the preview tile. 1 = enable preview, 0 = disable preview. */
1091 void Fl_File_Chooser::preview(int e
)
1093 previewButton
->value(e
);
1094 prefs_
.set("preview", e
);
1097 Fl_Group
*p
= previewBox
->parent();
1099 int w
= p
->w() * 2 / 3;
1100 fileList
->resize(fileList
->x(), fileList
->y(),
1102 previewBox
->resize(fileList
->x()+w
, previewBox
->y(),
1103 p
->w()-w
, previewBox
->h());
1107 fileList
->resize(fileList
->x(), fileList
->y(),
1108 p
->w(), fileList
->h());
1109 previewBox
->resize(p
->x()+p
->w(), previewBox
->y(),
1110 0, previewBox
->h());
1115 fileList
->parent()->redraw();
1120 // 'Fl_File_Chooser::previewCB()' - Timeout handler for the preview box.
1124 Fl_File_Chooser::previewCB(Fl_File_Chooser
*fc
) { // I - File chooser
1125 fc
->update_preview();
1130 // 'Fl_File_Chooser::rescan()' - Rescan the current directory.
1134 Fl_File_Chooser::rescan()
1136 char pathname
[FL_PATH_MAX
]; // New pathname for filename field
1139 // Clear the current filename
1140 strlcpy(pathname
, directory_
, sizeof(pathname
));
1141 if (pathname
[0] && pathname
[strlen(pathname
) - 1] != '/') {
1142 strlcat(pathname
, "/", sizeof(pathname
));
1144 // puts("Setting fileName in rescan()");
1145 fileName
->value(pathname
);
1147 if (type_
& DIRECTORY
)
1148 okButton
->activate();
1150 okButton
->deactivate();
1152 // Build the file list...
1153 fileList
->load(directory_
, sort
);
1155 if (!showHiddenButton
->value()) remove_hidden_files();
1157 // Update the preview box...
1163 Rescan the current directory without clearing the filename,
1164 then select the file if it is in the list
1166 void Fl_File_Chooser::rescan_keep_filename()
1168 // if no filename was set, this is likely a diretory browser
1169 const char *fn
= fileName
->value();
1170 if (!fn
|| !*fn
|| fn
[strlen(fn
) - 1]=='/') {
1176 char pathname
[FL_PATH_MAX
]; // New pathname for filename field
1177 strlcpy(pathname
, fn
, sizeof(pathname
));
1179 // Build the file list...
1180 fileList
->load(directory_
, sort
);
1182 if (!showHiddenButton
->value()) remove_hidden_files();
1184 // Update the preview box...
1187 // and select the chosen file
1189 char *slash
= strrchr(pathname
, '/');
1194 for (i
= 1; i
<= fileList
->size(); i
++)
1195 #if defined(WIN32) || defined(__EMX__)
1196 if (strcasecmp(fileList
->text(i
), slash
) == 0) {
1198 if (strcmp(fileList
->text(i
), slash
) == 0) {
1199 #endif // WIN32 || __EMX__
1200 fileList
->topline(i
);
1201 fileList
->select(i
);
1206 // update OK button activity
1207 if (found
|| type_
& CREATE
)
1208 okButton
->activate();
1210 okButton
->deactivate();
1215 // 'Fl_File_Chooser::showChoiceCB()' - Handle show selections.
1219 Fl_File_Chooser::showChoiceCB()
1221 const char *item
, // Selected item
1222 *patstart
; // Start of pattern
1223 char *patend
; // End of pattern
1224 char temp
[FL_PATH_MAX
]; // Temporary string for pattern
1227 item
= showChoice
->text(showChoice
->value());
1229 if (strcmp(item
, custom_filter_label
) == 0) {
1230 if ((item
= fl_input("%s", pattern_
, custom_filter_label
)) != NULL
) {
1231 strlcpy(pattern_
, item
, sizeof(pattern_
));
1233 quote_pathname(temp
, item
, sizeof(temp
));
1234 showChoice
->add(temp
);
1235 showChoice
->value(showChoice
->size() - 2);
1237 } else if ((patstart
= strchr(item
, '(')) == NULL
) {
1238 strlcpy(pattern_
, item
, sizeof(pattern_
));
1240 strlcpy(pattern_
, patstart
+ 1, sizeof(pattern_
));
1241 if ((patend
= strrchr(pattern_
, ')')) != NULL
) *patend
= '\0';
1244 fileList
->filter(pattern_
);
1247 // Rescan the directory...
1248 rescan_keep_filename();
1254 // 'Fl_File_Chooser::update_favorites()' - Update the favorites menu.
1258 Fl_File_Chooser::update_favorites()
1260 int i
; // Looping var
1261 char pathname
[FL_PATH_MAX
], // Pathname
1262 menuname
[2048]; // Menu name
1263 const char *home
; // Home directory
1266 favoritesButton
->clear();
1267 favoritesButton
->add("bla");
1268 favoritesButton
->clear();
1269 favoritesButton
->add(add_favorites_label
, FL_ALT
+ 'a', 0);
1270 favoritesButton
->add(manage_favorites_label
, FL_ALT
+ 'm', 0, 0, FL_MENU_DIVIDER
);
1271 favoritesButton
->add(filesystems_label
, FL_ALT
+ 'f', 0);
1273 if ((home
= getenv("HOME")) != NULL
) {
1274 quote_pathname(menuname
, home
, sizeof(menuname
));
1275 favoritesButton
->add(menuname
, FL_ALT
+ 'h', 0);
1278 for (i
= 0; i
< 100; i
++) {
1279 sprintf(menuname
, "favorite%02d", i
);
1280 prefs_
.get(menuname
, pathname
, "", sizeof(pathname
));
1281 if (!pathname
[0]) break;
1283 quote_pathname(menuname
, pathname
, sizeof(menuname
));
1285 if (i
< 10) favoritesButton
->add(menuname
, FL_ALT
+ '0' + i
, 0);
1286 else favoritesButton
->add(menuname
);
1289 if (i
== 100) ((Fl_Menu_Item
*)favoritesButton
->menu())[0].deactivate();
1294 // 'Fl_File_Chooser::update_preview()' - Update the preview box...
1298 Fl_File_Chooser::update_preview()
1300 const char *filename
; // Current filename
1301 const char *newlabel
= 0; // New label text
1302 Fl_Shared_Image
*image
= 0, // New image
1303 *oldimage
; // Old image
1304 int pbw
, pbh
; // Width and height of preview box
1305 int w
, h
; // Width and height of preview image
1306 int set
= 0; // Set this flag as soon as a decent preview is found
1308 if (!previewButton
->value()) return;
1311 if (filename
== NULL
) {
1312 // no file name at all, so we have an empty preview
1314 } else if (fl_filename_isdir(filename
)) {
1315 // filename is a directory, show a folder icon
1316 newlabel
= "@fileopen";
1320 if (fl_stat(filename
, &s
)==0) {
1321 if ((s
.st_mode
&S_IFMT
)!=S_IFREG
) {
1322 // this is no regular file, probably some kind of device
1323 newlabel
= "@-3refresh"; // a cross
1325 } else if (s
.st_size
==0) {
1326 // this file is emty
1327 newlabel
= "<empty file>";
1330 // if this file is an image, try to load it
1331 window
->cursor(FL_CURSOR_WAIT
);
1334 image
= Fl_Shared_Image::get(filename
);
1337 window
->cursor(FL_CURSOR_DEFAULT
);
1345 oldimage
= (Fl_Shared_Image
*)previewBox
->image();
1347 if (oldimage
) oldimage
->release();
1349 previewBox
->image(0);
1356 if (filename
) fp
= fl_fopen(filename
, "rb");
1360 // Try reading the first 1k of data for a label...
1361 bytes
= fread(preview_text_
, 1, sizeof(preview_text_
) - 1, fp
);
1362 preview_text_
[bytes
] = '\0';
1365 // Assume we can't read any data...
1366 preview_text_
[0] = '\0';
1369 window
->cursor(FL_CURSOR_DEFAULT
);
1372 // Scan the buffer for printable UTF8 chars...
1373 for (ptr
= preview_text_
; *ptr
; ptr
++) {
1374 uchar c
= uchar(*ptr
);
1375 if ( (c
&0x80)==0 ) {
1376 if (!isprint(c
&255) && !isspace(c
&255)) break;
1377 } else if ( (c
&0xe0)==0xc0 ) {
1378 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1380 } else if ( (c
&0xf0)==0xe0 ) {
1381 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1383 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1385 } else if ( (c
&0xf8)==0xf0 ) {
1386 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1388 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1390 if (ptr
[1] && (ptr
[1]&0xc0)!=0x80) break;
1394 // *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
1397 // Scan the buffer for printable characters in 8 bit
1398 if (*ptr
|| ptr
== preview_text_
) {
1399 for (ptr
= preview_text_
;
1400 *ptr
&& (isprint(*ptr
& 255) || isspace(*ptr
& 255));
1404 if (*ptr
|| ptr
== preview_text_
) {
1405 // Non-printable file, just show a big ?...
1406 previewBox
->label(filename
? "?" : 0);
1407 previewBox
->align(FL_ALIGN_CLIP
);
1408 previewBox
->labelsize(75);
1409 previewBox
->labelfont(FL_HELVETICA
);
1411 // Show the first 1k of text...
1412 int size
= previewBox
->h() / 20;
1413 if (size
< 6) size
= 6;
1414 else if (size
> FL_NORMAL_SIZE
) size
= FL_NORMAL_SIZE
;
1416 previewBox
->label(preview_text_
);
1417 previewBox
->align((Fl_Align
)(FL_ALIGN_CLIP
| FL_ALIGN_INSIDE
|
1418 FL_ALIGN_LEFT
| FL_ALIGN_TOP
));
1419 previewBox
->labelsize(size
);
1420 previewBox
->labelfont(FL_COURIER
);
1423 pbw
= previewBox
->w() - 20;
1424 pbh
= previewBox
->h() - 20;
1426 if (image
->w() > pbw
|| image
->h() > pbh
) {
1428 h
= w
* image
->h() / image
->w();
1432 w
= h
* image
->w() / image
->h();
1435 oldimage
= (Fl_Shared_Image
*)image
->copy(w
, h
);
1436 previewBox
->image((Fl_Image
*)oldimage
);
1440 previewBox
->image((Fl_Image
*)image
);
1443 previewBox
->align(FL_ALIGN_CLIP
);
1444 previewBox
->label(0);
1445 } else if (newlabel
) {
1446 previewBox
->label(newlabel
);
1447 previewBox
->align(FL_ALIGN_CLIP
);
1448 previewBox
->labelsize(newlabel
[0]=='@'?75:12);
1449 previewBox
->labelfont(FL_HELVETICA
);
1452 previewBox
->redraw();
1457 // 'Fl_File_Chooser::value()' - Return a selected filename.
1460 const char * // O - Filename or NULL
1461 Fl_File_Chooser::value(int f
) // I - File number
1463 int i
; // Looping var
1464 int fcount
; // Number of selected files
1465 const char *name
; // Current filename
1466 static char pathname
[FL_PATH_MAX
]; // Filename + directory
1469 name
= fileName
->value();
1471 if (!(type_
& MULTI
)) {
1472 // Return the filename in the filename field...
1473 if (!name
|| !name
[0]) return NULL
;
1477 // Return a filename from the list...
1478 for (i
= 1, fcount
= 0; i
<= fileList
->size(); i
++)
1479 if (fileList
->selected(i
)) {
1480 // See if this file is a selected file/directory...
1481 name
= fileList
->text(i
);
1486 if (directory_
[0]) {
1487 snprintf(pathname
, sizeof(pathname
), "%s/%s", directory_
, name
);
1489 strlcpy(pathname
, name
, sizeof(pathname
));
1496 // If nothing is selected, use the filename field...
1497 if (!name
|| !name
[0]) return NULL
;
1503 // 'Fl_File_Chooser::value()' - Set the current filename.
1507 Fl_File_Chooser::value(const char *filename
)
1508 // I - Filename + directory
1510 int i
, // Looping var
1511 fcount
; // Number of items in list
1512 char *slash
; // Directory separator
1513 char pathname
[FL_PATH_MAX
]; // Local copy of filename
1516 // printf("Fl_File_Chooser::value(\"%s\")\n", filename == NULL ? "(null)" : filename);
1518 // See if the filename is the "My System" directory...
1519 if (filename
== NULL
|| !filename
[0]) {
1520 // Yes, just change the current directory...
1521 directory(filename
);
1522 fileName
->value("");
1523 okButton
->deactivate();
1528 // See if the filename contains backslashes...
1529 char fixpath
[FL_PATH_MAX
]; // Path with slashes converted
1530 if (strchr(filename
, '\\')) {
1531 // Convert backslashes to slashes...
1532 strlcpy(fixpath
, filename
, sizeof(fixpath
));
1534 for (slash
= strchr(fixpath
, '\\'); slash
; slash
= strchr(slash
+ 1, '\\'))
1541 // See if there is a directory in there...
1542 fl_filename_absolute(pathname
, sizeof(pathname
), filename
);
1544 if ((slash
= strrchr(pathname
, '/')) != NULL
) {
1545 // Yes, change the display to the directory...
1546 if (!fl_filename_isdir(pathname
)) *slash
++ = '\0';
1548 directory(pathname
);
1549 if (*slash
== '/') slash
= pathname
;
1555 // Set the input field to the absolute path...
1556 if (slash
> pathname
) slash
[-1] = '/';
1558 fileName
->value(pathname
);
1559 fileName
->position(0, strlen(pathname
));
1560 okButton
->activate();
1562 // Then find the file in the file list and select it...
1563 fcount
= fileList
->size();
1565 fileList
->deselect(0);
1568 for (i
= 1; i
<= fcount
; i
++)
1569 #if defined(WIN32) || defined(__EMX__)
1570 if (strcasecmp(fileList
->text(i
), slash
) == 0) {
1572 if (strcmp(fileList
->text(i
), slash
) == 0) {
1573 #endif // WIN32 || __EMX__
1574 // printf("Selecting line %d...\n", i);
1575 fileList
->topline(i
);
1576 fileList
->select(i
);
1581 void Fl_File_Chooser::show()
1583 window
->hotspot(fileList
);
1586 fl_cursor(FL_CURSOR_WAIT
);
1587 rescan_keep_filename();
1588 fl_cursor(FL_CURSOR_DEFAULT
);
1589 fileName
->take_focus();
1591 showHiddenButton
->hide();
1595 void Fl_File_Chooser::showHidden(int value
)
1598 fileList
->load(directory());
1600 remove_hidden_files();
1605 void Fl_File_Chooser::remove_hidden_files()
1607 int count
= fileList
->size();
1608 for(int num
= count
; num
>= 1; num
--) {
1609 const char *p
= fileList
->text(num
);
1610 if (*p
== '.' && strcmp(p
, "../") != 0) fileList
->remove(num
);
1612 fileList
->topline(1);
1617 // 'compare_dirnames()' - Compare two directory names.
1621 compare_dirnames(const char *a
, const char *b
) {
1624 // Get length of each string...
1625 alen
= strlen(a
) - 1;
1626 blen
= strlen(b
) - 1;
1628 if (alen
< 0 || blen
< 0) return alen
- blen
;
1630 // Check for trailing slashes...
1631 if (a
[alen
] != '/') alen
++;
1632 if (b
[blen
] != '/') blen
++;
1634 // If the lengths aren't the same, then return the difference...
1635 if (alen
!= blen
) return alen
- blen
;
1637 // Do a comparison of the first N chars (alen == blen at this point)...
1639 return strncasecmp(a
, b
, alen
);
1641 return strncmp(a
, b
, alen
);
1647 // 'quote_pathname()' - Quote a pathname for a menu.
1651 quote_pathname(char *dst
, // O - Destination string
1652 const char *src
, // I - Source string
1653 int dstsize
) // I - Size of destination string
1657 while (*src
&& dstsize
> 1) {
1659 // Convert backslash to forward slash...
1664 if (*src
== '/') *dst
++ = '\\';
1675 // 'unquote_pathname()' - Unquote a pathname from a menu.
1679 unquote_pathname(char *dst
, // O - Destination string
1680 const char *src
, // I - Source string
1681 int dstsize
) // I - Size of destination string
1685 while (*src
&& dstsize
> 1) {
1686 if (*src
== '\\') src
++;
1695 // End of "$Id: Fl_File_Chooser2.cxx 8785 2011-06-06 16:11:22Z manolo $".