1 //
2 // "$Id: Fl_File_Chooser2.cxx 6092 2008-04-11 12:57:37Z matt $"
3 //
4 // More Fl_File_Chooser routines.
5 //
6 // Copyright 1999-2005 by Michael Sweet.
7 //
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.
12 //
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.
17 //
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 // http://www.fltk.org/str.php
26 //
27 // Contents:
28 //
29 // Fl_File_Chooser::count() - Return the number of selected files.
30 // Fl_File_Chooser::directory() - Set the directory in the file chooser.
31 // Fl_File_Chooser::filter() - Set the filter(s) for the chooser.
32 // Fl_File_Chooser::newdir() - Make a new directory.
33 // Fl_File_Chooser::value() - Return a selected filename.
34 // Fl_File_Chooser::rescan() - Rescan the current directory.
35 // Fl_File_Chooser::favoritesButtonCB() - Handle favorites selections.
36 // Fl_File_Chooser::fileListCB() - Handle clicks (and double-clicks)
37 // in the Fl_File_Browser.
38 // Fl_File_Chooser::fileNameCB() - Handle text entry in the FileBrowser.
39 // Fl_File_Chooser::showChoiceCB() - Handle show selections.
40 // compare_dirnames() - Compare two directory names.
41 // quote_pathname() - Quote a pathname for a menu.
42 // unquote_pathname() - Unquote a pathname from a menu.
43 //
44
45 //
46 // Include necessary headers.
47 //
48
49 #include <FL/Fl_File_Chooser.H>
50 #include <FL/filename.H>
51 #include <FL/fl_ask.H>
52 #include <FL/x.H>
53 #include <FL/Fl_Shared_Image.H>
54
55 #include <stdio.h>
56 #include <stdlib.h>
57 #include "flstring.h"
58 #include <errno.h>
59 #include <sys/types.h>
60 #include <sys/stat.h>
61
62 #if defined(WIN32) && ! defined (__CYGWIN__)
63 # include <direct.h>
64 # include <io.h>
65 // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
66 // on Windows, which is supposed to be POSIX compliant...
67 # define access _access
68 # define mkdir _mkdir
69 // Apparently Borland C++ defines DIRECTORY in <direct.h>, which
70 // interfers with the Fl_File_Icon enumeration of the same name.
71 # ifdef DIRECTORY
72 # undef DIRECTORY
73 # endif // DIRECTORY
74 #else
75 # include <unistd.h>
76 # include <pwd.h>
77 #endif /* WIN32 */
78
79
80 //
81 // File chooser label strings and sort function...
82 //
83
84 Fl_Preferences Fl_File_Chooser::prefs_(Fl_Preferences::USER, "fltk.org", "filechooser");
85
86 const char *Fl_File_Chooser::add_favorites_label = "Add to Favorites";
87 const char *Fl_File_Chooser::all_files_label = "All Files (*)";
88 const char *Fl_File_Chooser::custom_filter_label = "Custom Filter";
89 const char *Fl_File_Chooser::existing_file_label = "Please choose an existing file!";
90 const char *Fl_File_Chooser::favorites_label = "Favorites";
91 const char *Fl_File_Chooser::filename_label = "Filename:";
92 #ifdef WIN32
93 const char *Fl_File_Chooser::filesystems_label = "My Computer";
94 #else
95 const char *Fl_File_Chooser::filesystems_label = "File Systems";
96 #endif // WIN32
97 const char *Fl_File_Chooser::manage_favorites_label = "Manage Favorites";
98 const char *Fl_File_Chooser::new_directory_label = "New Directory?";
99 const char *Fl_File_Chooser::new_directory_tooltip = "Create a new directory.";
100 const char *Fl_File_Chooser::preview_label = "Preview";
101 const char *Fl_File_Chooser::save_label = "Save";
102 const char *Fl_File_Chooser::show_label = "Show:";
103 Fl_File_Sort_F *Fl_File_Chooser::sort = fl_numericsort;
104
105
106 //
107 // Local functions...
108 //
109
110 static int compare_dirnames(const char *a, const char *b);
111 static void quote_pathname(char *, const char *, int);
112 static void unquote_pathname(char *, const char *, int);
113
114
115 //
116 // 'Fl_File_Chooser::count()' - Return the number of selected files.
117 //
118
119 int // O - Number of selected files
count()120 Fl_File_Chooser::count() {
121 int i; // Looping var
122 int fcount; // Number of selected files
123 const char *filename; // Filename in input field or list
124
125
126 filename = fileName->value();
127
128 if (!(type_ & MULTI)) {
129 // Check to see if the file name input field is blank...
130 if (!filename || !filename[0]) return 0;
131 else return 1;
132 }
133
134 for (i = 1, fcount = 0; i <= fileList->size(); i ++)
135 if (fileList->selected(i)) {
136 // See if this file is a directory...
137 // matt: why would we do that? It is perfectly legal to select multiple
138 // directories in a DIR chooser. They are visually selected and value(i)
139 // returns all of them as expected
140 //filename = (char *)fileList->text(i);
141
142 //if (filename[strlen(filename) - 1] != '/')
143 fcount ++;
144 }
145
146 if (fcount) return fcount;
147 else if (!filename || !filename[0]) return 0;
148 else return 1;
149 }
150
151
152 //
153 // 'Fl_File_Chooser::directory()' - Set the directory in the file chooser.
154 //
155
156 void
directory(const char * d)157 Fl_File_Chooser::directory(const char *d)// I - Directory to change to
158 {
159 char *dirptr; // Pointer into directory
160
161
162 // printf("Fl_File_Chooser::directory(\"%s\")\n", d == NULL ? "(null)" : d);
163
164 // NULL == current directory
165 if (d == NULL)
166 d = ".";
167
168 #ifdef WIN32
169 // See if the filename contains backslashes...
170 char *slash; // Pointer to slashes
171 char fixpath[1024]; // Path with slashes converted
172 if (strchr(d, '\\')) {
173 // Convert backslashes to slashes...
174 strlcpy(fixpath, d, sizeof(fixpath));
175
176 for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
177 *slash = '/';
178
179 d = fixpath;
180 }
181 #endif // WIN32
182
183 if (d[0] != '\0')
184 {
185 // Make the directory absolute...
186 #if (defined(WIN32) && ! defined(__CYGWIN__))|| defined(__EMX__)
187 if (d[0] != '/' && d[0] != '\\' && d[1] != ':')
188 #else
189 if (d[0] != '/' && d[0] != '\\')
190 #endif /* WIN32 || __EMX__ */
191 fl_filename_absolute(directory_, d);
192 else
193 strlcpy(directory_, d, sizeof(directory_));
194
195 // Strip any trailing slash...
196 dirptr = directory_ + strlen(directory_) - 1;
197 if ((*dirptr == '/' || *dirptr == '\\') && dirptr > directory_)
198 *dirptr = '\0';
199
200 // See if we have a trailing .. or . in the filename...
201 dirptr = directory_ + strlen(directory_) - 3;
202 if (dirptr >= directory_ && strcmp(dirptr, "/..") == 0) {
203 // Yes, we have "..", so strip the trailing path...
204 *dirptr = '\0';
205 while (dirptr > directory_) {
206 if (*dirptr == '/') break;
207 dirptr --;
208 }
209
210 if (dirptr >= directory_ && *dirptr == '/')
211 *dirptr = '\0';
212 } else if ((dirptr + 1) >= directory_ && strcmp(dirptr + 1, "/.") == 0) {
213 // Strip trailing "."...
214 dirptr[1] = '\0';
215 }
216 }
217 else
218 directory_[0] = '\0';
219
220 if (shown()) {
221 // Rescan the directory...
222 rescan();
223 }
224 }
225
226
227 //
228 // 'Fl_File_Chooser::favoritesButtonCB()' - Handle favorites selections.
229 //
230
231 void
favoritesButtonCB()232 Fl_File_Chooser::favoritesButtonCB()
233 {
234 int v; // Current selection
235 char pathname[1024], // Pathname
236 menuname[2048]; // Menu name
237
238
239 v = favoritesButton->value();
240
241 if (!v) {
242 // Add current directory to favorites...
243 if (getenv("HOME")) v = favoritesButton->size() - 5;
244 else v = favoritesButton->size() - 4;
245
246 sprintf(menuname, "favorite%02d", v);
247
248 prefs_.set(menuname, directory_);
249 prefs_.flush();
250
251 quote_pathname(menuname, directory_, sizeof(menuname));
252 favoritesButton->add(menuname);
253
254 if (favoritesButton->size() > 104) {
255 ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
256 }
257 } else if (v == 1) {
258 // Manage favorites...
259 favoritesCB(0);
260 } else if (v == 2) {
261 // Filesystems/My Computer
262 directory("");
263 } else {
264 unquote_pathname(pathname, favoritesButton->text(v), sizeof(pathname));
265 directory(pathname);
266 }
267 }
268
269
270 //
271 // 'Fl_File_Chooser::favoritesCB()' - Handle favorites dialog.
272 //
273
274 void
favoritesCB(Fl_Widget * w)275 Fl_File_Chooser::favoritesCB(Fl_Widget *w)
276 // I - Widget
277 {
278 int i; // Looping var
279 char name[32], // Preference name
280 pathname[1024]; // Directory in list
281
282
283 if (!w) {
284 // Load the favorites list...
285 favList->clear();
286 favList->deselect();
287
288 for (i = 0; i < 100; i ++) {
289 // Get favorite directory 0 to 99...
290 sprintf(name, "favorite%02d", i);
291
292 prefs_.get(name, pathname, "", sizeof(pathname));
293
294 // Stop on the first empty favorite...
295 if (!pathname[0]) break;
296
297 // Add the favorite to the list...
298 favList->add(pathname,
299 Fl_File_Icon::find(pathname, Fl_File_Icon::DIRECTORY));
300 }
301
302 favUpButton->deactivate();
303 favDeleteButton->deactivate();
304 favDownButton->deactivate();
305 favOkButton->deactivate();
306
307 favWindow->hotspot(favList);
308 favWindow->show();
309 } else if (w == favList) {
310 i = favList->value();
311 if (i) {
312 if (i > 1) favUpButton->activate();
313 else favUpButton->deactivate();
314
315 favDeleteButton->activate();
316
317 if (i < favList->size()) favDownButton->activate();
318 else favDownButton->deactivate();
319 } else {
320 favUpButton->deactivate();
321 favDeleteButton->deactivate();
322 favDownButton->deactivate();
323 }
324 } else if (w == favUpButton) {
325 i = favList->value();
326
327 favList->insert(i - 1, favList->text(i), favList->data(i));
328 favList->remove(i + 1);
329 favList->select(i - 1);
330
331 if (i == 2) favUpButton->deactivate();
332
333 favDownButton->activate();
334
335 favOkButton->activate();
336 } else if (w == favDeleteButton) {
337 i = favList->value();
338
339 favList->remove(i);
340
341 if (i > favList->size()) i --;
342 favList->select(i);
343
344 if (i < favList->size()) favDownButton->activate();
345 else favDownButton->deactivate();
346
347 if (i > 1) favUpButton->activate();
348 else favUpButton->deactivate();
349
350 if (!i) favDeleteButton->deactivate();
351
352 favOkButton->activate();
353 } else if (w == favDownButton) {
354 i = favList->value();
355
356 favList->insert(i + 2, favList->text(i), favList->data(i));
357 favList->remove(i);
358 favList->select(i + 1);
359
360 if ((i + 1) == favList->size()) favDownButton->deactivate();
361
362 favUpButton->activate();
363
364 favOkButton->activate();
365 } else if (w == favOkButton) {
366 // Copy the new list over...
367 for (i = 0; i < favList->size(); i ++) {
368 // Set favorite directory 0 to 99...
369 sprintf(name, "favorite%02d", i);
370
371 prefs_.set(name, favList->text(i + 1));
372 }
373
374 // Clear old entries as necessary...
375 for (; i < 100; i ++) {
376 // Clear favorite directory 0 to 99...
377 sprintf(name, "favorite%02d", i);
378
379 prefs_.get(name, pathname, "", sizeof(pathname));
380
381 if (pathname[0]) prefs_.set(name, "");
382 else break;
383 }
384
385 update_favorites();
386 prefs_.flush();
387
388 favWindow->hide();
389 }
390 }
391
392
393 //
394 // 'Fl_File_Chooser::fileListCB()' - Handle clicks (and double-clicks) in the
395 // Fl_File_Browser.
396 //
397
398 void
fileListCB()399 Fl_File_Chooser::fileListCB()
400 {
401 char *filename, // New filename
402 pathname[1024]; // Full pathname to file
403
404
405 filename = (char *)fileList->text(fileList->value());
406 if (!filename)
407 return;
408
409 if (!directory_[0]) {
410 strlcpy(pathname, filename, sizeof(pathname));
411 } else if (strcmp(directory_, "/") == 0) {
412 snprintf(pathname, sizeof(pathname), "/%s", filename);
413 } else {
414 snprintf(pathname, sizeof(pathname), "%s/%s", directory_, filename);
415 }
416
417 if (Fl::event_clicks()) {
418 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
419 if ((strlen(pathname) == 2 && pathname[1] == ':') ||
420 _fl_filename_isdir_quick(pathname))
421 #else
422 if (_fl_filename_isdir_quick(pathname))
423 #endif /* WIN32 || __EMX__ */
424 {
425 // Change directories...
426 directory(pathname);
427
428 // Reset the click count so that a click in the same spot won't
429 // be treated as a triple-click. We use a value of -1 because
430 // the next click will increment click count to 0, which is what
431 // we really want...
432 Fl::event_clicks(-1);
433 }
434 else
435 {
436 // Hide the window - picked the file...
437 window->hide();
438 }
439 }
440 else
441 {
442 // Check if the user clicks on a directory when picking files;
443 // if so, make sure only that item is selected...
444 filename = pathname + strlen(pathname) - 1;
445
446 if ((type_ & MULTI) && !(type_ & DIRECTORY)) {
447 if (*filename == '/') {
448 // Clicked on a directory, deselect everything else...
449 int i = fileList->value();
450 fileList->deselect();
451 fileList->select(i);
452 } else {
453 // Clicked on a file - see if there are other directories selected...
454 int i;
455 const char *temp;
456 for (i = 1; i <= fileList->size(); i ++) {
457 if (i != fileList->value() && fileList->selected(i)) {
458 temp = fileList->text(i);
459 temp += strlen(temp) - 1;
460 if (*temp == '/') break; // Yes, selected directory
461 }
462 }
463
464 if (i <= fileList->size()) {
465 i = fileList->value();
466 fileList->deselect();
467 fileList->select(i);
468 }
469 }
470 }
471 // Strip any trailing slash from the directory name...
472 if (*filename == '/') *filename = '\0';
473
474 // puts("Setting fileName from fileListCB...");
475 fileName->value(pathname);
476
477 // Update the preview box...
478 Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this);
479 Fl::add_timeout(1.0, (Fl_Timeout_Handler)previewCB, this);
480
481 // Do any callback that is registered...
482 if (callback_) (*callback_)(this, data_);
483
484 // Activate the OK button as needed...
485 if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY))
486 okButton->activate();
487 else
488 okButton->deactivate();
489 }
490 }
491
492
493 //
494 // 'Fl_File_Chooser::fileNameCB()' - Handle text entry in the FileBrowser.
495 //
496
497 void
fileNameCB()498 Fl_File_Chooser::fileNameCB()
499 {
500 char *filename, // New filename
501 *slash, // Pointer to trailing slash
502 pathname[1024], // Full pathname to file
503 matchname[256]; // Matching filename
504 int i, // Looping var
505 min_match, // Minimum number of matching chars
506 max_match, // Maximum number of matching chars
507 num_files, // Number of files in directory
508 first_line; // First matching line
509 const char *file; // File from directory
510
511 // puts("fileNameCB()");
512 // printf("Event: %s\n", fl_eventnames[Fl::event()]);
513
514 // Get the filename from the text field...
515 filename = (char *)fileName->value();
516
517 if (!filename || !filename[0]) {
518 okButton->deactivate();
519 return;
520 }
521
522 // Expand ~ and $ variables as needed...
523 if (strchr(filename, '~') || strchr(filename, '$')) {
524 fl_filename_expand(pathname, sizeof(pathname), filename);
525 filename = pathname;
526 value(pathname);
527 }
528
529 // Make sure we have an absolute path...
530 #if (defined(WIN32) && !defined(__CYGWIN__)) || defined(__EMX__)
531 if (directory_[0] != '\0' && filename[0] != '/' &&
532 filename[0] != '\\' &&
533 !(isalpha(filename[0] & 255) && (!filename[1] || filename[1] == ':'))) {
534 #else
535 if (directory_[0] != '\0' && filename[0] != '/') {
536 #endif /* WIN32 || __EMX__ */
537 fl_filename_absolute(pathname, sizeof(pathname), filename);
538 value(pathname);
539 fileName->mark(fileName->position()); // no selection after expansion
540 } else if (filename != pathname) {
541 // Finally, make sure that we have a writable copy...
542 strlcpy(pathname, filename, sizeof(pathname));
543 }
544
545 filename = pathname;
546
547 // Now process things according to the key pressed...
548 if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) {
549 // Enter pressed - select or change directory...
550 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
551 if ((isalpha(pathname[0] & 255) && pathname[1] == ':' && !pathname[2]) ||
552 _fl_filename_isdir_quick(pathname) &&
553 compare_dirnames(pathname, directory_)) {
554 #else
555 if (_fl_filename_isdir_quick(pathname) &&
556 compare_dirnames(pathname, directory_)) {
557 #endif /* WIN32 || __EMX__ */
558 directory(pathname);
559 } else if ((type_ & CREATE) || access(pathname, 0) == 0) {
560 if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY)) {
561 // Update the preview box...
562 update_preview();
563
564 // Do any callback that is registered...
565 if (callback_) (*callback_)(this, data_);
566
567 // Hide the window to signal things are done...
568 window->hide();
569 }
570 } else {
571 // File doesn't exist, so beep at and alert the user...
572 fl_alert(existing_file_label);
573 }
574 }
575 else if (Fl::event_key() != FL_Delete &&
576 Fl::event_key() != FL_BackSpace) {
577 // Check to see if the user has entered a directory...
578 if ((slash = strrchr(pathname, '/')) == NULL)
579 slash = strrchr(pathname, '\\');
580
581 if (!slash) return;
582
583 // Yes, change directories if necessary...
584 *slash++ = '\0';
585 filename = slash;
586
587 #if defined(WIN32) || defined(__EMX__)
588 if (strcasecmp(pathname, directory_) &&
589 (pathname[0] || strcasecmp("/", directory_))) {
590 #else
591 if (strcmp(pathname, directory_) &&
592 (pathname[0] || strcasecmp("/", directory_))) {
593 #endif // WIN32 || __EMX__
594 int p = fileName->position();
595 int m = fileName->mark();
596
597 directory(pathname);
598
599 if (filename[0]) {
600 char tempname[1024];
601
602 snprintf(tempname, sizeof(tempname), "%s/%s", directory_, filename);
603 fileName->value(tempname);
604 strlcpy(pathname, tempname, sizeof(pathname));
605 }
606
607 fileName->position(p, m);
608 }
609
610 // Other key pressed - do filename completion as possible...
611 num_files = fileList->size();
612 min_match = strlen(filename);
613 max_match = min_match + 1;
614 first_line = 0;
615
616 for (i = 1; i <= num_files && max_match > min_match; i ++) {
617 file = fileList->text(i);
618
619 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
620 if (strncasecmp(filename, file, min_match) == 0) {
621 #else
622 if (strncmp(filename, file, min_match) == 0) {
623 #endif // WIN32 || __EMX__
624 // OK, this one matches; check against the previous match
625 if (!first_line) {
626 // First match; copy stuff over...
627 strlcpy(matchname, file, sizeof(matchname));
628 max_match = strlen(matchname);
629
630 // Strip trailing /, if any...
631 if (matchname[max_match - 1] == '/') {
632 max_match --;
633 matchname[max_match] = '\0';
634 }
635
636 // And then make sure that the item is visible
637 fileList->topline(i);
638 first_line = i;
639 } else {
640 // Succeeding match; compare to find maximum string match...
641 while (max_match > min_match)
642 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
643 if (strncasecmp(file, matchname, max_match) == 0)
644 #else
645 if (strncmp(file, matchname, max_match) == 0)
646 #endif // WIN32 || __EMX__
647 break;
648 else
649 max_match --;
650
651 // Truncate the string as needed...
652 matchname[max_match] = '\0';
653 }
654 }
655 }
656
657 // If we have any matches, add them to the input field...
658 if (first_line > 0 && min_match == max_match &&
659 max_match == (int)strlen(fileList->text(first_line))) {
660 // This is the only possible match...
661 fileList->deselect(0);
662 fileList->select(first_line);
663 fileList->redraw();
664 } else if (max_match > min_match && first_line) {
665 // Add the matching portion...
666 fileName->replace(filename - pathname, filename - pathname + min_match,
667 matchname);
668
669 // Highlight it with the cursor at the end of the selection so
670 // s/he can press the right arrow to accept the selection
671 // (Tab and End also do this for both cases.)
672 fileName->position(filename - pathname + max_match,
673 filename - pathname + min_match);
674 } else if (max_match == 0) {
675 fileList->deselect(0);
676 fileList->redraw();
677 }
678
679 // See if we need to enable the OK button...
680 if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
681 (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
682 okButton->activate();
683 } else {
684 okButton->deactivate();
685 }
686 } else {
687 // FL_Delete or FL_BackSpace
688 fileList->deselect(0);
689 fileList->redraw();
690 if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
691 (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
692 okButton->activate();
693 } else {
694 okButton->deactivate();
695 }
696 }
697 }
698
699
700 //
701 // 'Fl_File_Chooser::filter()' - Set the filter(s) for the chooser.
702 //
703
704 void
705 Fl_File_Chooser::filter(const char *p) // I - Pattern(s)
706 {
707 char *copyp, // Copy of pattern
708 *start, // Start of pattern
709 *end; // End of pattern
710 int allfiles; // Do we have a "*" pattern?
711 char temp[1024]; // Temporary pattern string
712
713
714 // Make sure we have a pattern...
715 if (!p || !*p) p = "*";
716
717 // Copy the pattern string...
718 copyp = strdup(p);
719
720 // Separate the pattern string as necessary...
721 showChoice->clear();
722
723 for (start = copyp, allfiles = 0; start && *start; start = end) {
724 end = strchr(start, '\t');
725 if (end) *end++ = '\0';
726
727 if (strcmp(start, "*") == 0) {
728 showChoice->add(all_files_label);
729 allfiles = 1;
730 } else {
731 quote_pathname(temp, start, sizeof(temp));
732 showChoice->add(temp);
733 if (strstr(start, "(*)") != NULL) allfiles = 1;
734 }
735 }
736
737 free(copyp);
738
739 if (!allfiles) showChoice->add(all_files_label);
740
741 showChoice->add(custom_filter_label);
742
743 showChoice->value(0);
744 showChoiceCB();
745 }
746
747
748 //
749 // 'Fl_File_Chooser::newdir()' - Make a new directory.
750 //
751
752 void
753 Fl_File_Chooser::newdir()
754 {
755 const char *dir; // New directory name
756 char pathname[1024]; // Full path of directory
757
758
759 // Get a directory name from the user
760 if ((dir = fl_input(new_directory_label, NULL)) == NULL)
761 return;
762
763 // Make it relative to the current directory as needed...
764 #if (defined(WIN32) && ! defined (__CYGWIN__)) || defined(__EMX__)
765 if (dir[0] != '/' && dir[0] != '\\' && dir[1] != ':')
766 #else
767 if (dir[0] != '/' && dir[0] != '\\')
768 #endif /* WIN32 || __EMX__ */
769 snprintf(pathname, sizeof(pathname), "%s/%s", directory_, dir);
770 else
771 strlcpy(pathname, dir, sizeof(pathname));
772
773 // Create the directory; ignore EEXIST errors...
774 #if defined(WIN32) && ! defined (__CYGWIN__)
775 if (mkdir(pathname))
776 #else
777 if (mkdir(pathname, 0777))
778 #endif /* WIN32 */
779 if (errno != EEXIST)
780 {
781 fl_alert("%s", strerror(errno));
782 return;
783 }
784
785 // Show the new directory...
786 directory(pathname);
787 }
788
789
790 //
791 // 'Fl_File_Chooser::preview()' - Enable or disable the preview tile.
792 //
793
794 void
795 Fl_File_Chooser::preview(int e)// I - 1 = enable preview, 0 = disable preview
796 {
797 previewButton->value(e);
798 prefs_.set("preview", e);
799 prefs_.flush();
800
801 Fl_Group *p = previewBox->parent();
802 if (e) {
803 int w = p->w() * 2 / 3;
804 fileList->resize(fileList->x(), fileList->y(),
805 w, fileList->h());
806 previewBox->resize(fileList->x()+w, previewBox->y(),
807 p->w()-w, previewBox->h());
808 previewBox->show();
809 update_preview();
810 } else {
811 fileList->resize(fileList->x(), fileList->y(),
812 p->w(), fileList->h());
813 previewBox->resize(p->x()+p->w(), previewBox->y(),
814 0, previewBox->h());
815 previewBox->hide();
816 }
817 p->init_sizes();
818
819 fileList->parent()->redraw();
820 }
821
822
823 //
824 // 'Fl_File_Chooser::previewCB()' - Timeout handler for the preview box.
825 //
826
827 void
828 Fl_File_Chooser::previewCB(Fl_File_Chooser *fc) { // I - File chooser
829 fc->update_preview();
830 }
831
832
833 //
834 // 'Fl_File_Chooser::rescan()' - Rescan the current directory.
835 //
836
837 void
838 Fl_File_Chooser::rescan()
839 {
840 char pathname[1024]; // New pathname for filename field
841
842
843 // Clear the current filename
844 strlcpy(pathname, directory_, sizeof(pathname));
845 if (pathname[0] && pathname[strlen(pathname) - 1] != '/') {
846 strlcat(pathname, "/", sizeof(pathname));
847 }
848 // puts("Setting fileName in rescan()");
849 fileName->value(pathname);
850
851 if (type_ & DIRECTORY)
852 okButton->activate();
853 else
854 okButton->deactivate();
855
856 // Build the file list...
857 fileList->load(directory_, sort);
858
859 // Update the preview box...
860 update_preview();
861 }
862
863 //
864 // 'Fl_File_Chooser::rescan_keep_filename()' - Rescan the current directory
865 // without clearing the filename, then select the file if it is in the list
866 //
867
868 void
869 Fl_File_Chooser::rescan_keep_filename()
870 {
871 // if no filename was set, this is likely a diretory browser
872 const char *fn = fileName->value();
873 if (!fn || !*fn || fn[strlen(fn) - 1]=='/') {
874 rescan();
875 return;
876 }
877
878 int i;
879 char pathname[1024]; // New pathname for filename field
880 strlcpy(pathname, fn, sizeof(pathname));
881
882 // Build the file list...
883 fileList->load(directory_, sort);
884
885 // Update the preview box...
886 update_preview();
887
888 // and select the chosen file
889 char found = 0;
890 char *slash = strrchr(pathname, '/');
891 if (slash)
892 slash++;
893 else
894 slash = pathname;
895 for (i = 1; i <= fileList->size(); i ++)
896 #if defined(WIN32) || defined(__EMX__)
897 if (strcasecmp(fileList->text(i), slash) == 0) {
898 #else
899 if (strcmp(fileList->text(i), slash) == 0) {
900 #endif // WIN32 || __EMX__
901 fileList->topline(i);
902 fileList->select(i);
903 found = 1;
904 break;
905 }
906
907 // update OK button activity
908 if (found || type_ & CREATE)
909 okButton->activate();
910 else
911 okButton->deactivate();
912 }
913
914
915 //
916 // 'Fl_File_Chooser::showChoiceCB()' - Handle show selections.
917 //
918
919 void
920 Fl_File_Chooser::showChoiceCB()
921 {
922 const char *item, // Selected item
923 *patstart; // Start of pattern
924 char *patend; // End of pattern
925 char temp[1024]; // Temporary string for pattern
926
927
928 item = showChoice->text(showChoice->value());
929
930 if (strcmp(item, custom_filter_label) == 0) {
931 if ((item = fl_input(custom_filter_label, pattern_)) != NULL) {
932 strlcpy(pattern_, item, sizeof(pattern_));
933
934 quote_pathname(temp, item, sizeof(temp));
935 showChoice->add(temp);
936 showChoice->value(showChoice->size() - 2);
937 }
938 } else if ((patstart = strchr(item, '(')) == NULL) {
939 strlcpy(pattern_, item, sizeof(pattern_));
940 } else {
941 strlcpy(pattern_, patstart + 1, sizeof(pattern_));
942 if ((patend = strrchr(pattern_, ')')) != NULL) *patend = '\0';
943 }
944
945 fileList->filter(pattern_);
946
947 if (shown()) {
948 // Rescan the directory...
949 rescan_keep_filename();
950 }
951 }
952
953
954 //
955 // 'Fl_File_Chooser::update_favorites()' - Update the favorites menu.
956 //
957
958 void
959 Fl_File_Chooser::update_favorites()
960 {
961 int i; // Looping var
962 char pathname[1024], // Pathname
963 menuname[2048]; // Menu name
964 const char *home; // Home directory
965
966
967 favoritesButton->clear();
968 favoritesButton->add("bla");
969 favoritesButton->clear();
970 favoritesButton->add(add_favorites_label, FL_ALT + 'a', 0);
971 favoritesButton->add(manage_favorites_label, FL_ALT + 'm', 0, 0, FL_MENU_DIVIDER);
972 favoritesButton->add(filesystems_label, FL_ALT + 'f', 0);
973
974 if ((home = getenv("HOME")) != NULL) {
975 quote_pathname(menuname, home, sizeof(menuname));
976 favoritesButton->add(menuname, FL_ALT + 'h', 0);
977 }
978
979 for (i = 0; i < 100; i ++) {
980 sprintf(menuname, "favorite%02d", i);
981 prefs_.get(menuname, pathname, "", sizeof(pathname));
982 if (!pathname[0]) break;
983
984 quote_pathname(menuname, pathname, sizeof(menuname));
985
986 if (i < 10) favoritesButton->add(menuname, FL_ALT + '0' + i, 0);
987 else favoritesButton->add(menuname);
988 }
989
990 if (i == 100) ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
991 }
992
993
994 //
995 // 'Fl_File_Chooser::update_preview()' - Update the preview box...
996 //
997
998 void
999 Fl_File_Chooser::update_preview()
1000 {
1001 const char *filename; // Current filename
1002 Fl_Shared_Image *image, // New image
1003 *oldimage; // Old image
1004 int pbw, pbh; // Width and height of preview box
1005 int w, h; // Width and height of preview image
1006
1007
1008 if (!previewButton->value()) return;
1009
1010 if ((filename = value()) == NULL || fl_filename_isdir(filename)) image = NULL;
1011 else {
1012 window->cursor(FL_CURSOR_WAIT);
1013 Fl::check();
1014
1015 image = Fl_Shared_Image::get(filename);
1016
1017 if (image) {
1018 window->cursor(FL_CURSOR_DEFAULT);
1019 Fl::check();
1020 }
1021 }
1022
1023 oldimage = (Fl_Shared_Image *)previewBox->image();
1024
1025 if (oldimage) oldimage->release();
1026
1027 previewBox->image(0);
1028
1029 if (!image) {
1030 FILE *fp;
1031 int bytes;
1032 char *ptr;
1033
1034 if (filename) fp = fopen(filename, "rb");
1035 else fp = NULL;
1036
1037 if (fp != NULL) {
1038 // Try reading the first 1k of data for a label...
1039 bytes = fread(preview_text_, 1, sizeof(preview_text_) - 1, fp);
1040 preview_text_[bytes] = '\0';
1041 fclose(fp);
1042 } else {
1043 // Assume we can't read any data...
1044 preview_text_[0] = '\0';
1045 }
1046
1047 window->cursor(FL_CURSOR_DEFAULT);
1048 Fl::check();
1049
1050 // Scan the buffer for printable chars...
1051 for (ptr = preview_text_;
1052 *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
1053 ptr ++);
1054
1055 if (*ptr || ptr == preview_text_) {
1056 // Non-printable file, just show a big ?...
1057 previewBox->label(filename ? "?" : 0);
1058 previewBox->align(FL_ALIGN_CLIP);
1059 previewBox->labelsize(100);
1060 previewBox->labelfont(FL_HELVETICA);
1061 } else {
1062 // Show the first 1k of text...
1063 int size = previewBox->h() / 20;
1064 if (size < 6) size = 6;
1065 else if (size > 14) size = 14;
1066
1067 previewBox->label(preview_text_);
1068 previewBox->align((Fl_Align)(FL_ALIGN_CLIP | FL_ALIGN_INSIDE |
1069 FL_ALIGN_LEFT | FL_ALIGN_TOP));
1070 previewBox->labelsize((uchar)size);
1071 previewBox->labelfont(FL_COURIER);
1072 }
1073 } else {
1074 pbw = previewBox->w() - 20;
1075 pbh = previewBox->h() - 20;
1076
1077 if (image->w() > pbw || image->h() > pbh) {
1078 w = pbw;
1079 h = w * image->h() / image->w();
1080
1081 if (h > pbh) {
1082 h = pbh;
1083 w = h * image->w() / image->h();
1084 }
1085
1086 oldimage = (Fl_Shared_Image *)image->copy(w, h);
1087 previewBox->image((Fl_Image *)oldimage);
1088
1089 image->release();
1090 } else {
1091 previewBox->image((Fl_Image *)image);
1092 }
1093
1094 previewBox->align(FL_ALIGN_CLIP);
1095 previewBox->label(0);
1096 }
1097
1098 previewBox->redraw();
1099 }
1100
1101
1102 //
1103 // 'Fl_File_Chooser::value()' - Return a selected filename.
1104 //
1105
1106 const char * // O - Filename or NULL
1107 Fl_File_Chooser::value(int f) // I - File number
1108 {
1109 int i; // Looping var
1110 int fcount; // Number of selected files
1111 const char *name; // Current filename
1112 static char pathname[1024]; // Filename + directory
1113
1114
1115 name = fileName->value();
1116
1117 if (!(type_ & MULTI)) {
1118 // Return the filename in the filename field...
1119 if (!name || !name[0]) return NULL;
1120 else return name;
1121 }
1122
1123 // Return a filename from the list...
1124 for (i = 1, fcount = 0; i <= fileList->size(); i ++)
1125 if (fileList->selected(i)) {
1126 // See if this file is a selected file/directory...
1127 name = fileList->text(i);
1128
1129 fcount ++;
1130
1131 if (fcount == f) {
1132 if (directory_[0]) {
1133 snprintf(pathname, sizeof(pathname), "%s/%s", directory_, name);
1134 } else {
1135 strlcpy(pathname, name, sizeof(pathname));
1136 }
1137
1138 return pathname;
1139 }
1140 }
1141
1142 // If nothing is selected, use the filename field...
1143 if (!name || !name[0]) return NULL;
1144 else return name;
1145 }
1146
1147
1148 //
1149 // 'Fl_File_Chooser::value()' - Set the current filename.
1150 //
1151
1152 void
1153 Fl_File_Chooser::value(const char *filename)
1154 // I - Filename + directory
1155 {
1156 int i, // Looping var
1157 fcount; // Number of items in list
1158 char *slash; // Directory separator
1159 char pathname[1024]; // Local copy of filename
1160
1161
1162 // printf("Fl_File_Chooser::value(\"%s\")\n", filename == NULL ? "(null)" : filename);
1163
1164 // See if the filename is the "My System" directory...
1165 if (filename == NULL || !filename[0]) {
1166 // Yes, just change the current directory...
1167 directory(filename);
1168 fileName->value("");
1169 okButton->deactivate();
1170 return;
1171 }
1172
1173 #ifdef WIN32
1174 // See if the filename contains backslashes...
1175 char fixpath[1024]; // Path with slashes converted
1176 if (strchr(filename, '\\')) {
1177 // Convert backslashes to slashes...
1178 strlcpy(fixpath, filename, sizeof(fixpath));
1179
1180 for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
1181 *slash = '/';
1182
1183 filename = fixpath;
1184 }
1185 #endif // WIN32
1186
1187 // See if there is a directory in there...
1188 fl_filename_absolute(pathname, sizeof(pathname), filename);
1189
1190 if ((slash = strrchr(pathname, '/')) != NULL) {
1191 // Yes, change the display to the directory...
1192 if (!fl_filename_isdir(pathname)) *slash++ = '\0';
1193
1194 directory(pathname);
1195 if (*slash == '/') slash = pathname;
1196 } else {
1197 directory(".");
1198 slash = pathname;
1199 }
1200
1201 // Set the input field to the absolute path...
1202 if (slash > pathname) slash[-1] = '/';
1203
1204 fileName->value(pathname);
1205 fileName->position(0, strlen(pathname));
1206 okButton->activate();
1207
1208 // Then find the file in the file list and select it...
1209 fcount = fileList->size();
1210
1211 fileList->deselect(0);
1212 fileList->redraw();
1213
1214 for (i = 1; i <= fcount; i ++)
1215 #if defined(WIN32) || defined(__EMX__)
1216 if (strcasecmp(fileList->text(i), slash) == 0) {
1217 #else
1218 if (strcmp(fileList->text(i), slash) == 0) {
1219 #endif // WIN32 || __EMX__
1220 // printf("Selecting line %d...\n", i);
1221 fileList->topline(i);
1222 fileList->select(i);
1223 break;
1224 }
1225 }
1226
1227
1228 //
1229 // 'compare_dirnames()' - Compare two directory names.
1230 //
1231
1232 static int
1233 compare_dirnames(const char *a, const char *b) {
1234 int alen, blen;
1235
1236 // Get length of each string...
1237 alen = strlen(a) - 1;
1238 blen = strlen(b) - 1;
1239
1240 if (alen < 0 || blen < 0) return alen - blen;
1241
1242 // Check for trailing slashes...
1243 if (a[alen] != '/') alen ++;
1244 if (b[blen] != '/') blen ++;
1245
1246 // If the lengths aren't the same, then return the difference...
1247 if (alen != blen) return alen - blen;
1248
1249 // Do a comparison of the first N chars (alen == blen at this point)...
1250 #ifdef WIN32
1251 return strncasecmp(a, b, alen);
1252 #else
1253 return strncmp(a, b, alen);
1254 #endif // WIN32
1255 }
1256
1257
1258 //
1259 // 'quote_pathname()' - Quote a pathname for a menu.
1260 //
1261
1262 static void
1263 quote_pathname(char *dst, // O - Destination string
1264 const char *src, // I - Source string
1265 int dstsize) // I - Size of destination string
1266 {
1267 dstsize --;
1268
1269 while (*src && dstsize > 1) {
1270 if (*src == '\\') {
1271 // Convert backslash to forward slash...
1272 *dst++ = '\\';
1273 *dst++ = '/';
1274 src ++;
1275 } else {
1276 if (*src == '/') *dst++ = '\\';
1277
1278 *dst++ = *src++;
1279 }
1280 }
1281
1282 *dst = '\0';
1283 }
1284
1285
1286 //
1287 // 'unquote_pathname()' - Unquote a pathname from a menu.
1288 //
1289
1290 static void
1291 unquote_pathname(char *dst, // O - Destination string
1292 const char *src, // I - Source string
1293 int dstsize) // I - Size of destination string
1294 {
1295 dstsize --;
1296
1297 while (*src && dstsize > 1) {
1298 if (*src == '\\') src ++;
1299 *dst++ = *src++;
1300 }
1301
1302 *dst = '\0';
1303 }
1304
1305
1306 //
1307 // End of "$Id: Fl_File_Chooser2.cxx 6092 2008-04-11 12:57:37Z matt $".
1308 //
1309