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

Fl_File_Chooser2.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_File_Chooser2.cxx 8063 2010-12-19 21:20:10Z matt $"
00003 //
00004 // More Fl_File_Chooser routines.
00005 //
00006 // Copyright 1999-2010 by Michael Sweet.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 // fabien: ATTENTION: Only Out Of Source Gen. because cxx/H files are autogenerated by fluid.
00310 // *** END OF OUT OF SOURCE DOC ***
00311 
00312 // Contents:
00313 //
00314 //   Fl_File_Chooser::count()             - Return the number of selected files.
00315 //   Fl_File_Chooser::directory()         - Set the directory in the file chooser.
00316 //   Fl_File_Chooser::filter()            - Set the filter(s) for the chooser.
00317 //   Fl_File_Chooser::newdir()            - Make a new directory.
00318 //   Fl_File_Chooser::value()             - Return a selected filename.
00319 //   Fl_File_Chooser::rescan()            - Rescan the current directory.
00320 //   Fl_File_Chooser::favoritesButtonCB() - Handle favorites selections.
00321 //   Fl_File_Chooser::fileListCB()        - Handle clicks (and double-clicks)
00322 //                                          in the Fl_File_Browser.
00323 //   Fl_File_Chooser::fileNameCB()        - Handle text entry in the FileBrowser.
00324 //   Fl_File_Chooser::showChoiceCB()      - Handle show selections.
00325 //   compare_dirnames()                   - Compare two directory names.
00326 //   quote_pathname()                     - Quote a pathname for a menu.
00327 //   unquote_pathname()                   - Unquote a pathname from a menu.
00328 //
00329 //   Fl_File_Chooser::add_extra()         - add extra widget at the bottom, return pointer 
00330 //                                          to previous extra widget or NULL if none,
00331 //                                          If argument is NULL extra widget removed.
00332 //                                          NOTE! file chooser does't delete extra widget in 
00333 //                                          destructor! To prevent memory leakage don't forget
00334 //                                          delete unused extra widgets by yourself.
00335 //
00336 
00337 //
00338 // Include necessary headers.
00339 //
00340 
00341 #include <FL/Fl_File_Chooser.H>
00342 #include <FL/filename.H>
00343 #include <FL/fl_ask.H>
00344 #include <FL/x.H>
00345 #include <FL/Fl_Shared_Image.H>
00346 
00347 #include <stdio.h>
00348 #include <stdlib.h>
00349 #include "flstring.h"
00350 #include <errno.h>
00351 #include <sys/types.h>
00352 #include <sys/stat.h>
00353 
00354 #if defined(WIN32) && ! defined (__CYGWIN__)
00355 #  include <direct.h>
00356 #  include <io.h>
00357 // Visual C++ 2005 incorrectly displays a warning about the use of POSIX APIs
00358 // on Windows, which is supposed to be POSIX compliant...
00359 #  define access _access
00360 #  define mkdir _mkdir
00361 // Apparently Borland C++ defines DIRECTORY in <direct.h>, which
00362 // interfers with the Fl_File_Icon enumeration of the same name.
00363 #  ifdef DIRECTORY
00364 #    undef DIRECTORY
00365 #  endif // DIRECTORY
00366 #else
00367 #  include <unistd.h>
00368 #  include <pwd.h>
00369 #endif /* WIN32 */
00370 
00371 
00372 //
00373 // File chooser label strings and sort function...
00374 //
00375 
00376 Fl_Preferences  Fl_File_Chooser::prefs_(Fl_Preferences::USER, "fltk.org", "filechooser");
00377 
00378 const char      *Fl_File_Chooser::add_favorites_label = "Add to Favorites";
00379 const char      *Fl_File_Chooser::all_files_label = "All Files (*)";
00380 const char      *Fl_File_Chooser::custom_filter_label = "Custom Filter";
00381 const char      *Fl_File_Chooser::existing_file_label = "Please choose an existing file!";
00382 const char      *Fl_File_Chooser::favorites_label = "Favorites";
00383 const char      *Fl_File_Chooser::filename_label = "Filename:";
00384 #ifdef WIN32
00385 const char      *Fl_File_Chooser::filesystems_label = "My Computer";
00386 #else
00387 const char      *Fl_File_Chooser::filesystems_label = "File Systems";
00388 #endif // WIN32
00389 const char      *Fl_File_Chooser::manage_favorites_label = "Manage Favorites";
00390 const char      *Fl_File_Chooser::new_directory_label = "New Directory?";
00391 const char      *Fl_File_Chooser::new_directory_tooltip = "Create a new directory.";
00392 const char      *Fl_File_Chooser::preview_label = "Preview";
00393 const char      *Fl_File_Chooser::save_label = "Save";
00394 const char      *Fl_File_Chooser::show_label = "Show:";
00395 Fl_File_Sort_F  *Fl_File_Chooser::sort = fl_numericsort;
00396 
00397 
00398 //
00399 // Local functions...
00400 //
00401 
00402 static int      compare_dirnames(const char *a, const char *b);
00403 static void     quote_pathname(char *, const char *, int);
00404 static void     unquote_pathname(char *, const char *, int);
00405 
00406 
00407 //
00408 // 'Fl_File_Chooser::count()' - Return the number of selected files.
00409 //
00410 
00411 int                             // O - Number of selected files
00412 Fl_File_Chooser::count() {
00413   int           i;              // Looping var
00414   int           fcount;         // Number of selected files
00415   const char    *filename;      // Filename in input field or list
00416 
00417 
00418   filename = fileName->value();
00419 
00420   if (!(type_ & MULTI)) {
00421     // Check to see if the file name input field is blank...
00422     if (!filename || !filename[0]) return 0;
00423     else return 1;
00424   }
00425 
00426   for (i = 1, fcount = 0; i <= fileList->size(); i ++)
00427     if (fileList->selected(i)) {
00428       // See if this file is a directory...
00429       // matt: why would we do that? It is perfectly legal to select multiple
00430       // directories in a DIR chooser. They are visually selected and value(i)
00431       // returns all of them as expected
00432       //filename = (char *)fileList->text(i);
00433 
00434       //if (filename[strlen(filename) - 1] != '/')
00435         fcount ++;
00436     }
00437 
00438   if (fcount) return fcount;
00439   else if (!filename || !filename[0]) return 0;
00440   else return 1;
00441 }
00442 
00443 
00444 //
00445 // 'Fl_File_Chooser::directory()' - Set the directory in the file chooser.
00446 //
00447 
00448 void
00449 Fl_File_Chooser::directory(const char *d)// I - Directory to change to
00450 {
00451   char  *dirptr;                        // Pointer into directory
00452 
00453 
00454 //  printf("Fl_File_Chooser::directory(\"%s\")\n", d == NULL ? "(null)" : d);
00455 
00456   // NULL == current directory
00457   if (d == NULL)
00458     d = ".";
00459 
00460 #ifdef WIN32
00461   // See if the filename contains backslashes...
00462   char  *slash;                         // Pointer to slashes
00463   char  fixpath[FL_PATH_MAX];                   // Path with slashes converted
00464   if (strchr(d, '\\')) {
00465     // Convert backslashes to slashes...
00466     strlcpy(fixpath, d, sizeof(fixpath));
00467 
00468     for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
00469       *slash = '/';
00470 
00471     d = fixpath;
00472   }
00473 #endif // WIN32
00474 
00475   if (d[0] != '\0')
00476   {
00477     // Make the directory absolute...
00478 #if (defined(WIN32) && ! defined(__CYGWIN__))|| defined(__EMX__)
00479     if (d[0] != '/' && d[0] != '\\' && d[1] != ':')
00480 #else
00481     if (d[0] != '/' && d[0] != '\\')
00482 #endif /* WIN32 || __EMX__ */
00483       fl_filename_absolute(directory_, d);
00484     else
00485       strlcpy(directory_, d, sizeof(directory_));
00486 
00487     // Strip any trailing slash...
00488     dirptr = directory_ + strlen(directory_) - 1;
00489     if ((*dirptr == '/' || *dirptr == '\\') && dirptr > directory_)
00490       *dirptr = '\0';
00491 
00492     // See if we have a trailing .. or . in the filename...
00493     dirptr = directory_ + strlen(directory_) - 3;
00494     if (dirptr >= directory_ && strcmp(dirptr, "/..") == 0) {
00495       // Yes, we have "..", so strip the trailing path...
00496       *dirptr = '\0';
00497       while (dirptr > directory_) {
00498         if (*dirptr == '/') break;
00499         dirptr --;
00500       }
00501 
00502       if (dirptr >= directory_ && *dirptr == '/')
00503         *dirptr = '\0';
00504     } else if ((dirptr + 1) >= directory_ && strcmp(dirptr + 1, "/.") == 0) {
00505       // Strip trailing "."...
00506       dirptr[1] = '\0';
00507     }
00508   }
00509   else
00510     directory_[0] = '\0';
00511 
00512   if (shown()) {
00513     // Rescan the directory...
00514     rescan();
00515   }
00516 }
00517 
00518 
00519 //
00520 // 'Fl_File_Chooser::favoritesButtonCB()' - Handle favorites selections.
00521 //
00522 
00523 void
00524 Fl_File_Chooser::favoritesButtonCB()
00525 {
00526   int           v;                      // Current selection
00527   char          pathname[FL_PATH_MAX],          // Pathname
00528                 menuname[FL_PATH_MAX];          // Menu name
00529 
00530 
00531   v = favoritesButton->value();
00532 
00533   if (!v) {
00534     // Add current directory to favorites...
00535     if (getenv("HOME")) v = favoritesButton->size() - 5;
00536     else v = favoritesButton->size() - 4;
00537 
00538     sprintf(menuname, "favorite%02d", v);
00539 
00540     prefs_.set(menuname, directory_);
00541     prefs_.flush();
00542 
00543     quote_pathname(menuname, directory_, sizeof(menuname));
00544     favoritesButton->add(menuname);
00545 
00546     if (favoritesButton->size() > 104) {
00547       ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
00548     }
00549   } else if (v == 1) {
00550     // Manage favorites...
00551     favoritesCB(0);
00552   } else if (v == 2) {
00553     // Filesystems/My Computer
00554     directory("");
00555   } else {
00556     unquote_pathname(pathname, favoritesButton->text(v), sizeof(pathname));
00557     directory(pathname);
00558   }
00559 }
00560 
00561 
00562 //
00563 // 'Fl_File_Chooser::favoritesCB()' - Handle favorites dialog.
00564 //
00565 
00566 void
00567 Fl_File_Chooser::favoritesCB(Fl_Widget *w)
00568                                         // I - Widget
00569 {
00570   int           i;                      // Looping var
00571   char          name[32],               // Preference name
00572                 pathname[1024];         // Directory in list
00573 
00574 
00575   if (!w) {
00576     // Load the favorites list...
00577     favList->clear();
00578     favList->deselect();
00579 
00580     for (i = 0; i < 100; i ++) {
00581       // Get favorite directory 0 to 99...
00582       sprintf(name, "favorite%02d", i);
00583 
00584       prefs_.get(name, pathname, "", sizeof(pathname));
00585 
00586       // Stop on the first empty favorite...
00587       if (!pathname[0]) break;
00588 
00589       // Add the favorite to the list...
00590       favList->add(pathname,
00591                    Fl_File_Icon::find(pathname, Fl_File_Icon::DIRECTORY));
00592     }
00593 
00594     favUpButton->deactivate();
00595     favDeleteButton->deactivate();
00596     favDownButton->deactivate();
00597     favOkButton->deactivate();
00598 
00599     favWindow->hotspot(favList);
00600     favWindow->show();
00601   } else if (w == favList) {
00602     i = favList->value();
00603     if (i) {
00604       if (i > 1) favUpButton->activate();
00605       else favUpButton->deactivate();
00606 
00607       favDeleteButton->activate();
00608 
00609       if (i < favList->size()) favDownButton->activate();
00610       else favDownButton->deactivate();
00611     } else {
00612       favUpButton->deactivate();
00613       favDeleteButton->deactivate();
00614       favDownButton->deactivate();
00615     }
00616   } else if (w == favUpButton) {
00617     i = favList->value();
00618 
00619     favList->insert(i - 1, favList->text(i), favList->data(i));
00620     favList->remove(i + 1);
00621     favList->select(i - 1);
00622 
00623     if (i == 2) favUpButton->deactivate();
00624 
00625     favDownButton->activate();
00626 
00627     favOkButton->activate();
00628   } else if (w == favDeleteButton) {
00629     i = favList->value();
00630 
00631     favList->remove(i);
00632 
00633     if (i > favList->size()) i --;
00634     favList->select(i);
00635 
00636     if (i < favList->size()) favDownButton->activate();
00637     else favDownButton->deactivate();
00638 
00639     if (i > 1) favUpButton->activate();
00640     else favUpButton->deactivate();
00641 
00642     if (!i) favDeleteButton->deactivate();
00643 
00644     favOkButton->activate();
00645   } else if (w == favDownButton) {
00646     i = favList->value();
00647 
00648     favList->insert(i + 2, favList->text(i), favList->data(i));
00649     favList->remove(i);
00650     favList->select(i + 1);
00651 
00652     if ((i + 1) == favList->size()) favDownButton->deactivate();
00653 
00654     favUpButton->activate();
00655 
00656     favOkButton->activate();
00657   } else if (w == favOkButton) {
00658     // Copy the new list over...
00659     for (i = 0; i < favList->size(); i ++) {
00660       // Set favorite directory 0 to 99...
00661       sprintf(name, "favorite%02d", i);
00662 
00663       prefs_.set(name, favList->text(i + 1));
00664     }
00665 
00666     // Clear old entries as necessary...
00667     for (; i < 100; i ++) {
00668       // Clear favorite directory 0 to 99...
00669       sprintf(name, "favorite%02d", i);
00670 
00671       prefs_.get(name, pathname, "", sizeof(pathname));
00672 
00673       if (pathname[0]) prefs_.set(name, "");
00674       else break;
00675     }
00676 
00677     update_favorites();
00678     prefs_.flush();
00679 
00680     favWindow->hide();
00681   }
00682 }
00683 
00684 
00685 //
00686 // 'Fl_File_Chooser::fileListCB()' - Handle clicks (and double-clicks) in the
00687 //                                   Fl_File_Browser.
00688 //
00689 
00690 void
00691 Fl_File_Chooser::fileListCB()
00692 {
00693   char  *filename,                      // New filename
00694         pathname[FL_PATH_MAX];                  // Full pathname to file
00695 
00696 
00697   filename = (char *)fileList->text(fileList->value());
00698   if (!filename)
00699     return;
00700 
00701   if (!directory_[0]) {
00702     strlcpy(pathname, filename, sizeof(pathname));
00703   } else if (strcmp(directory_, "/") == 0) {
00704     snprintf(pathname, sizeof(pathname), "/%s", filename);
00705   } else {
00706     snprintf(pathname, sizeof(pathname), "%s/%s", directory_, filename);
00707   }
00708 
00709   if (Fl::event_clicks()) {
00710 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
00711     if ((strlen(pathname) == 2 && pathname[1] == ':') ||
00712         _fl_filename_isdir_quick(pathname))
00713 #else
00714     if (_fl_filename_isdir_quick(pathname))
00715 #endif /* WIN32 || __EMX__ */
00716     {
00717       // Change directories...
00718       directory(pathname);
00719 
00720       // Reset the click count so that a click in the same spot won't
00721       // be treated as a triple-click.  We use a value of -1 because
00722       // the next click will increment click count to 0, which is what
00723       // we really want...
00724       Fl::event_clicks(-1);
00725     }
00726     else
00727     {
00728       // Hide the window - picked the file...
00729       window->hide();
00730       if (callback_) (*callback_)(this, data_);
00731     }
00732   }
00733   else
00734   {
00735     // Check if the user clicks on a directory when picking files;
00736     // if so, make sure only that item is selected...
00737     filename = pathname + strlen(pathname) - 1;
00738 
00739     if ((type_ & MULTI) && !(type_ & DIRECTORY)) {
00740       if (*filename == '/') {
00741         // Clicked on a directory, deselect everything else...
00742         int i = fileList->value();
00743         fileList->deselect();
00744         fileList->select(i);
00745       } else {
00746         // Clicked on a file - see if there are other directories selected...
00747         int i;
00748         const char *temp;
00749         for (i = 1; i <= fileList->size(); i ++) {
00750           if (i != fileList->value() && fileList->selected(i)) {
00751             temp = fileList->text(i);
00752             temp += strlen(temp) - 1;
00753             if (*temp == '/') break;    // Yes, selected directory
00754           }
00755         }
00756 
00757         if (i <= fileList->size()) {
00758           i = fileList->value();
00759           fileList->deselect();
00760           fileList->select(i);
00761         }
00762       }
00763     }
00764     // Strip any trailing slash from the directory name...
00765     if (*filename == '/') *filename = '\0';
00766 
00767 //    puts("Setting fileName from fileListCB...");
00768     fileName->value(pathname);
00769 
00770     // Update the preview box...
00771     Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this);
00772     Fl::add_timeout(1.0, (Fl_Timeout_Handler)previewCB, this);
00773 
00774     // Do any callback that is registered...
00775     if (callback_) (*callback_)(this, data_);
00776 
00777     // Activate the OK button as needed...
00778     if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY))
00779       okButton->activate();
00780     else
00781       okButton->deactivate();
00782   }
00783 }
00784 
00785 
00786 //
00787 // 'Fl_File_Chooser::fileNameCB()' - Handle text entry in the FileBrowser.
00788 //
00789 
00790 void
00791 Fl_File_Chooser::fileNameCB()
00792 {
00793   char          *filename,      // New filename
00794                 *slash,         // Pointer to trailing slash
00795                 pathname[FL_PATH_MAX],  // Full pathname to file
00796                 matchname[FL_PATH_MAX]; // Matching filename
00797   int           i,              // Looping var
00798                 min_match,      // Minimum number of matching chars
00799                 max_match,      // Maximum number of matching chars
00800                 num_files,      // Number of files in directory
00801                 first_line;     // First matching line
00802   const char    *file;          // File from directory
00803 
00804 //  puts("fileNameCB()");
00805 //  printf("Event: %s\n", fl_eventnames[Fl::event()]);
00806 
00807   // Get the filename from the text field...
00808   filename = (char *)fileName->value();
00809 
00810   if (!filename || !filename[0]) {
00811     okButton->deactivate();
00812     return;
00813   }
00814 
00815   // Expand ~ and $ variables as needed...
00816   if (strchr(filename, '~') || strchr(filename, '$')) {
00817     fl_filename_expand(pathname, sizeof(pathname), filename);
00818     filename = pathname;
00819     value(pathname);
00820   }
00821 
00822   // Make sure we have an absolute path...
00823 #if (defined(WIN32) && !defined(__CYGWIN__)) || defined(__EMX__)
00824   if (directory_[0] != '\0' && filename[0] != '/' &&
00825       filename[0] != '\\' &&
00826       !(isalpha(filename[0] & 255) && (!filename[1] || filename[1] == ':'))) {
00827 #else
00828   if (directory_[0] != '\0' && filename[0] != '/') {
00829 #endif /* WIN32 || __EMX__ */
00830     fl_filename_absolute(pathname, sizeof(pathname), filename);
00831     value(pathname);
00832     fileName->mark(fileName->position()); // no selection after expansion
00833   } else if (filename != pathname) {
00834     // Finally, make sure that we have a writable copy...
00835     strlcpy(pathname, filename, sizeof(pathname));
00836   }
00837 
00838   filename = pathname;
00839 
00840   // Now process things according to the key pressed...
00841   if (Fl::event_key() == FL_Enter || Fl::event_key() == FL_KP_Enter) {
00842     // Enter pressed - select or change directory...
00843 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
00844     if ((isalpha(pathname[0] & 255) && pathname[1] == ':' && !pathname[2]) ||
00845         (_fl_filename_isdir_quick(pathname) &&
00846          compare_dirnames(pathname, directory_))) {
00847 #else
00848     if (_fl_filename_isdir_quick(pathname) &&
00849         compare_dirnames(pathname, directory_)) {
00850 #endif /* WIN32 || __EMX__ */
00851       directory(pathname);
00852     } else if ((type_ & CREATE) || access(pathname, 0) == 0) {
00853       if (!_fl_filename_isdir_quick(pathname) || (type_ & DIRECTORY)) {
00854         // Update the preview box...
00855         update_preview();
00856 
00857         // Do any callback that is registered...
00858         if (callback_) (*callback_)(this, data_);
00859 
00860         // Hide the window to signal things are done...
00861         window->hide();
00862       }
00863     } else {
00864       // File doesn't exist, so beep at and alert the user...
00865       fl_alert("%s",existing_file_label);
00866     }
00867   }
00868   else if (Fl::event_key() != FL_Delete &&
00869            Fl::event_key() != FL_BackSpace) {
00870     // Check to see if the user has entered a directory...
00871     if ((slash = strrchr(pathname, '/')) == NULL)
00872       slash = strrchr(pathname, '\\');
00873 
00874     if (!slash) return;
00875 
00876     // Yes, change directories if necessary...
00877     *slash++ = '\0';
00878     filename = slash;
00879 
00880 #if defined(WIN32) || defined(__EMX__)
00881     if (strcasecmp(pathname, directory_) &&
00882         (pathname[0] || strcasecmp("/", directory_))) {
00883 #else
00884     if (strcmp(pathname, directory_) &&
00885         (pathname[0] || strcasecmp("/", directory_))) {
00886 #endif // WIN32 || __EMX__
00887       int p = fileName->position();
00888       int m = fileName->mark();
00889 
00890       directory(pathname);
00891 
00892       if (filename[0]) {
00893         char tempname[FL_PATH_MAX];
00894 
00895         snprintf(tempname, sizeof(tempname), "%s/%s", directory_, filename);
00896         fileName->value(tempname);
00897         strlcpy(pathname, tempname, sizeof(pathname));
00898       }
00899 
00900       fileName->position(p, m);
00901     }
00902 
00903     // Other key pressed - do filename completion as possible...
00904     num_files  = fileList->size();
00905     min_match  = strlen(filename);
00906     max_match  = min_match + 1;
00907     first_line = 0;
00908 
00909     for (i = 1; i <= num_files && max_match > min_match; i ++) {
00910       file = fileList->text(i);
00911 
00912 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
00913       if (strncasecmp(filename, file, min_match) == 0) {
00914 #else
00915       if (strncmp(filename, file, min_match) == 0) {
00916 #endif // WIN32 || __EMX__
00917         // OK, this one matches; check against the previous match
00918         if (!first_line) {
00919           // First match; copy stuff over...
00920           strlcpy(matchname, file, sizeof(matchname));
00921           max_match = strlen(matchname);
00922 
00923           // Strip trailing /, if any...
00924           if (matchname[max_match - 1] == '/') {
00925             max_match --;
00926             matchname[max_match] = '\0';
00927           }
00928 
00929           // And then make sure that the item is visible
00930           fileList->topline(i);
00931           first_line = i;
00932         } else {
00933           // Succeeding match; compare to find maximum string match...
00934           while (max_match > min_match)
00935 #if (defined(WIN32) && ! defined(__CYGWIN__)) || defined(__EMX__)
00936             if (strncasecmp(file, matchname, max_match) == 0)
00937 #else
00938             if (strncmp(file, matchname, max_match) == 0)
00939 #endif // WIN32 || __EMX__
00940               break;
00941             else
00942               max_match --;
00943 
00944           // Truncate the string as needed...
00945           matchname[max_match] = '\0';
00946         }
00947       }
00948     }
00949 
00950     // If we have any matches, add them to the input field...
00951     if (first_line > 0 && min_match == max_match &&
00952         max_match == (int)strlen(fileList->text(first_line))) {
00953       // This is the only possible match...
00954       fileList->deselect(0);
00955       fileList->select(first_line);
00956       fileList->redraw();
00957     } else if (max_match > min_match && first_line) {
00958       // Add the matching portion...
00959       fileName->replace(filename - pathname, filename - pathname + min_match,
00960                         matchname);
00961 
00962       // Highlight it with the cursor at the end of the selection so
00963       // s/he can press the right arrow to accept the selection
00964       // (Tab and End also do this for both cases.)
00965       fileName->position(filename - pathname + max_match,
00966                          filename - pathname + min_match);
00967     } else if (max_match == 0) {
00968       fileList->deselect(0);
00969       fileList->redraw();
00970     }
00971 
00972     // See if we need to enable the OK button...
00973     if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
00974         (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
00975       okButton->activate();
00976     } else {
00977       okButton->deactivate();
00978     }
00979   } else {
00980     // FL_Delete or FL_BackSpace
00981     fileList->deselect(0);
00982     fileList->redraw();
00983     if (((type_ & CREATE) || !access(fileName->value(), 0)) &&
00984         (!fl_filename_isdir(fileName->value()) || (type_ & DIRECTORY))) {
00985       okButton->activate();
00986     } else {
00987       okButton->deactivate();
00988     }
00989   }
00990 }
00991 
00992 
00993 //
00994 // 'Fl_File_Chooser::filter()' - Set the filter(s) for the chooser.
00995 //
00996 
00997 void
00998 Fl_File_Chooser::filter(const char *p)          // I - Pattern(s)
00999 {
01000   char          *copyp,                         // Copy of pattern
01001                 *start,                         // Start of pattern
01002                 *end;                           // End of pattern
01003   int           allfiles;                       // Do we have a "*" pattern?
01004   char          temp[FL_PATH_MAX];                      // Temporary pattern string
01005 
01006 
01007   // Make sure we have a pattern...
01008   if (!p || !*p) p = "*";
01009 
01010   // Copy the pattern string...
01011   copyp = strdup(p);
01012 
01013   // Separate the pattern string as necessary...
01014   showChoice->clear();
01015 
01016   for (start = copyp, allfiles = 0; start && *start; start = end) {
01017     end = strchr(start, '\t');
01018     if (end) *end++ = '\0';
01019 
01020     if (strcmp(start, "*") == 0) {
01021       showChoice->add(all_files_label);
01022       allfiles = 1;
01023     } else {
01024       quote_pathname(temp, start, sizeof(temp));
01025       showChoice->add(temp);
01026       if (strstr(start, "(*)") != NULL) allfiles = 1;
01027     }
01028   }
01029 
01030   free(copyp);
01031 
01032   if (!allfiles) showChoice->add(all_files_label);
01033 
01034   showChoice->add(custom_filter_label);
01035   
01036   // TODO: add a menu item to switch hidden files on and off
01037 
01038   showChoice->value(0);
01039   showChoiceCB();
01040 }
01041 
01042 
01043 //
01044 // 'Fl_File_Chooser::newdir()' - Make a new directory.
01045 //
01046 
01047 void
01048 Fl_File_Chooser::newdir()
01049 {
01050   const char    *dir;           // New directory name
01051   char          pathname[FL_PATH_MAX];  // Full path of directory
01052 
01053 
01054   // Get a directory name from the user
01055   if ((dir = fl_input("%s", NULL, new_directory_label)) == NULL)
01056     return;
01057 
01058   // Make it relative to the current directory as needed...
01059 #if (defined(WIN32) && ! defined (__CYGWIN__)) || defined(__EMX__)
01060   if (dir[0] != '/' && dir[0] != '\\' && dir[1] != ':')
01061 #else
01062   if (dir[0] != '/' && dir[0] != '\\')
01063 #endif /* WIN32 || __EMX__ */
01064     snprintf(pathname, sizeof(pathname), "%s/%s", directory_, dir);
01065   else
01066     strlcpy(pathname, dir, sizeof(pathname));
01067 
01068   // Create the directory; ignore EEXIST errors...
01069 #if defined(WIN32) && ! defined (__CYGWIN__)
01070   if (mkdir(pathname))
01071 #else
01072   if (mkdir(pathname, 0777))
01073 #endif /* WIN32 */
01074     if (errno != EEXIST)
01075     {
01076       fl_alert("%s", strerror(errno));
01077       return;
01078     }
01079 
01080   // Show the new directory...
01081   directory(pathname);
01082 }
01083 
01084 
01085 
01087 void Fl_File_Chooser::preview(int e)
01088 {
01089   previewButton->value(e);
01090   prefs_.set("preview", e);
01091   prefs_.flush();
01092 
01093   Fl_Group *p = previewBox->parent();
01094   if (e) {
01095     int w = p->w() * 2 / 3;
01096     fileList->resize(fileList->x(), fileList->y(),
01097                      w, fileList->h());
01098     previewBox->resize(fileList->x()+w, previewBox->y(),
01099                        p->w()-w, previewBox->h());
01100     previewBox->show();
01101     update_preview();
01102   } else {
01103     fileList->resize(fileList->x(), fileList->y(),
01104                      p->w(), fileList->h());
01105     previewBox->resize(p->x()+p->w(), previewBox->y(),
01106                        0, previewBox->h());
01107     previewBox->hide();
01108   }
01109   p->init_sizes();
01110 
01111   fileList->parent()->redraw();
01112 }
01113 
01114 
01115 //
01116 // 'Fl_File_Chooser::previewCB()' - Timeout handler for the preview box.
01117 //
01118 
01119 void
01120 Fl_File_Chooser::previewCB(Fl_File_Chooser *fc) {       // I - File chooser
01121   fc->update_preview();
01122 }
01123 
01124 
01125 //
01126 // 'Fl_File_Chooser::rescan()' - Rescan the current directory.
01127 //
01128 
01129 void
01130 Fl_File_Chooser::rescan()
01131 {
01132   char  pathname[FL_PATH_MAX];          // New pathname for filename field
01133 
01134 
01135   // Clear the current filename
01136   strlcpy(pathname, directory_, sizeof(pathname));
01137   if (pathname[0] && pathname[strlen(pathname) - 1] != '/') {
01138     strlcat(pathname, "/", sizeof(pathname));
01139   }
01140 //  puts("Setting fileName in rescan()");
01141   fileName->value(pathname);
01142 
01143   if (type_ & DIRECTORY)
01144     okButton->activate();
01145   else
01146     okButton->deactivate();
01147 
01148   // Build the file list...
01149   fileList->load(directory_, sort);
01150 
01151   // Update the preview box...
01152   update_preview();
01153 }
01154 
01155 //
01160 void Fl_File_Chooser::rescan_keep_filename()
01161 {
01162   // if no filename was set, this is likely a diretory browser
01163   const char *fn = fileName->value();
01164   if (!fn || !*fn || fn[strlen(fn) - 1]=='/') {
01165     rescan();
01166     return;
01167   }
01168 
01169   int   i;
01170   char  pathname[FL_PATH_MAX];          // New pathname for filename field
01171   strlcpy(pathname, fn, sizeof(pathname));
01172 
01173   // Build the file list...
01174   fileList->load(directory_, sort);
01175 
01176   // Update the preview box...
01177   update_preview();
01178 
01179   // and select the chosen file
01180   char found = 0;
01181   char *slash = strrchr(pathname, '/');
01182   if (slash) 
01183     slash++;
01184   else
01185     slash = pathname;
01186   for (i = 1; i <= fileList->size(); i ++)
01187 #if defined(WIN32) || defined(__EMX__)
01188     if (strcasecmp(fileList->text(i), slash) == 0) {
01189 #else
01190     if (strcmp(fileList->text(i), slash) == 0) {
01191 #endif // WIN32 || __EMX__
01192       fileList->topline(i);
01193       fileList->select(i);
01194       found = 1;
01195       break;
01196     }
01197 
01198   // update OK button activity
01199   if (found || type_ & CREATE)
01200     okButton->activate();
01201   else
01202     okButton->deactivate();
01203 }
01204 
01205 
01206 //
01207 // 'Fl_File_Chooser::showChoiceCB()' - Handle show selections.
01208 //
01209 
01210 void
01211 Fl_File_Chooser::showChoiceCB()
01212 {
01213   const char    *item,                  // Selected item
01214                 *patstart;              // Start of pattern
01215   char          *patend;                // End of pattern
01216   char          temp[FL_PATH_MAX];              // Temporary string for pattern
01217 
01218 
01219   item = showChoice->text(showChoice->value());
01220 
01221   if (strcmp(item, custom_filter_label) == 0) {
01222     if ((item = fl_input("%s", pattern_, custom_filter_label)) != NULL) {
01223       strlcpy(pattern_, item, sizeof(pattern_));
01224 
01225       quote_pathname(temp, item, sizeof(temp));
01226       showChoice->add(temp);
01227       showChoice->value(showChoice->size() - 2);
01228     }
01229   } else if ((patstart = strchr(item, '(')) == NULL) {
01230     strlcpy(pattern_, item, sizeof(pattern_));
01231   } else {
01232     strlcpy(pattern_, patstart + 1, sizeof(pattern_));
01233     if ((patend = strrchr(pattern_, ')')) != NULL) *patend = '\0';
01234   }
01235 
01236   fileList->filter(pattern_);
01237 
01238   if (shown()) {
01239     // Rescan the directory...
01240     rescan_keep_filename();
01241   }
01242 }
01243 
01244 
01245 //
01246 // 'Fl_File_Chooser::update_favorites()' - Update the favorites menu.
01247 //
01248 
01249 void
01250 Fl_File_Chooser::update_favorites()
01251 {
01252   int           i;                      // Looping var
01253   char          pathname[FL_PATH_MAX],          // Pathname
01254                 menuname[2048];         // Menu name
01255   const char    *home;                  // Home directory
01256 
01257 
01258   favoritesButton->clear();
01259   favoritesButton->add("bla");
01260   favoritesButton->clear();
01261   favoritesButton->add(add_favorites_label, FL_ALT + 'a', 0);
01262   favoritesButton->add(manage_favorites_label, FL_ALT + 'm', 0, 0, FL_MENU_DIVIDER);
01263   favoritesButton->add(filesystems_label, FL_ALT + 'f', 0);
01264     
01265   if ((home = getenv("HOME")) != NULL) {
01266     quote_pathname(menuname, home, sizeof(menuname));
01267     favoritesButton->add(menuname, FL_ALT + 'h', 0);
01268   }
01269 
01270   for (i = 0; i < 100; i ++) {
01271     sprintf(menuname, "favorite%02d", i);
01272     prefs_.get(menuname, pathname, "", sizeof(pathname));
01273     if (!pathname[0]) break;
01274 
01275     quote_pathname(menuname, pathname, sizeof(menuname));
01276 
01277     if (i < 10) favoritesButton->add(menuname, FL_ALT + '0' + i, 0);
01278     else favoritesButton->add(menuname);
01279   }
01280 
01281   if (i == 100) ((Fl_Menu_Item *)favoritesButton->menu())[0].deactivate();
01282 }
01283 
01284 
01285 //
01286 // 'Fl_File_Chooser::update_preview()' - Update the preview box...
01287 //
01288 
01289 void
01290 Fl_File_Chooser::update_preview()
01291 {
01292   const char            *filename;      // Current filename
01293   const char            *newlabel = 0;  // New label text
01294   Fl_Shared_Image       *image = 0,     // New image
01295                         *oldimage;      // Old image
01296   int                   pbw, pbh;       // Width and height of preview box
01297   int                   w, h;           // Width and height of preview image
01298   int                   set = 0;        // Set this flag as soon as a decent preview is found
01299 
01300 
01301   if (!previewButton->value()) return;
01302 
01303   filename = value();
01304   if (filename == NULL) {
01305     // no file name at all, so we have an empty preview
01306     set = 1;
01307   } else if (fl_filename_isdir(filename)) {
01308     // filename is a directory, show a folder icon
01309     newlabel = "@fileopen";
01310     set = 1;
01311   } else {
01312     struct stat s;
01313     if (fl_stat(filename, &s)==0) {
01314       if ((s.st_mode&S_IFMT)!=S_IFREG) {
01315         // this is no regular file, probably some kind of device
01316         newlabel = "@-3refresh"; // a cross
01317         set = 1;
01318       } else if (s.st_size==0) {
01319         // this file is emty
01320         newlabel = "<empty file>";
01321         set = 1;
01322       } else {
01323         // if this file is an image, try to load it
01324         window->cursor(FL_CURSOR_WAIT);
01325         Fl::check();
01326         
01327         image = Fl_Shared_Image::get(filename);
01328         
01329         if (image) {
01330           window->cursor(FL_CURSOR_DEFAULT);
01331           Fl::check();
01332           set = 1;
01333         }
01334       }
01335     }
01336   }
01337 
01338   oldimage = (Fl_Shared_Image *)previewBox->image();
01339 
01340   if (oldimage) oldimage->release();
01341 
01342   previewBox->image(0);
01343 
01344   if (!set) {
01345     FILE        *fp;
01346     int         bytes;
01347     char        *ptr;
01348 
01349     if (filename) fp = fl_fopen(filename, "rb");
01350     else fp = NULL;
01351 
01352     if (fp != NULL) {
01353       // Try reading the first 1k of data for a label...
01354       bytes = fread(preview_text_, 1, sizeof(preview_text_) - 1, fp);
01355       preview_text_[bytes] = '\0';
01356       fclose(fp);
01357     } else {
01358       // Assume we can't read any data...
01359       preview_text_[0] = '\0';
01360     }
01361 
01362     window->cursor(FL_CURSOR_DEFAULT);
01363     Fl::check();
01364 
01365     // Scan the buffer for printable UTF8 chars...
01366     for (ptr = preview_text_; *ptr; ptr++) {
01367       uchar c = uchar(*ptr);
01368       if ( (c&0x80)==0 ) {
01369         if (!isprint(c&255) && !isspace(c&255)) break;
01370       } else if ( (c&0xe0)==0xc0 ) {
01371         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01372         ptr++;
01373       } else if ( (c&0xf0)==0xe0 ) {
01374         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01375         ptr++;
01376         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01377         ptr++;
01378       } else if ( (c&0xf8)==0xf0 ) {
01379         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01380         ptr++;
01381         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01382         ptr++;
01383         if (ptr[1] && (ptr[1]&0xc0)!=0x80) break;
01384         ptr++;
01385       }
01386     } 
01387 //         *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
01388 //       ptr ++);
01389 
01390     // Scan the buffer for printable characters in 8 bit
01391     if (*ptr || ptr == preview_text_) {
01392       for (ptr = preview_text_;
01393          *ptr && (isprint(*ptr & 255) || isspace(*ptr & 255));
01394          ptr ++);
01395     }
01396 
01397     if (*ptr || ptr == preview_text_) {
01398       // Non-printable file, just show a big ?...
01399       previewBox->label(filename ? "?" : 0);
01400       previewBox->align(FL_ALIGN_CLIP);
01401       previewBox->labelsize(75);
01402       previewBox->labelfont(FL_HELVETICA);
01403     } else {
01404       // Show the first 1k of text...
01405       int size = previewBox->h() / 20;
01406       if (size < 6) size = 6;
01407       else if (size > 14) size = 14;
01408 
01409       previewBox->label(preview_text_);
01410       previewBox->align((Fl_Align)(FL_ALIGN_CLIP | FL_ALIGN_INSIDE |
01411                                    FL_ALIGN_LEFT | FL_ALIGN_TOP));
01412       previewBox->labelsize(size);
01413       previewBox->labelfont(FL_COURIER);
01414     }
01415   } else if (image) {
01416     pbw = previewBox->w() - 20;
01417     pbh = previewBox->h() - 20;
01418 
01419     if (image->w() > pbw || image->h() > pbh) {
01420       w   = pbw;
01421       h   = w * image->h() / image->w();
01422 
01423       if (h > pbh) {
01424         h = pbh;
01425         w = h * image->w() / image->h();
01426       }
01427 
01428       oldimage = (Fl_Shared_Image *)image->copy(w, h);
01429       previewBox->image((Fl_Image *)oldimage);
01430 
01431       image->release();
01432     } else {
01433       previewBox->image((Fl_Image *)image);
01434     }
01435 
01436     previewBox->align(FL_ALIGN_CLIP);
01437     previewBox->label(0);
01438   } else if (newlabel) {
01439     previewBox->label(newlabel);
01440     previewBox->align(FL_ALIGN_CLIP);
01441     previewBox->labelsize(newlabel[0]=='@'?75:12);
01442     previewBox->labelfont(FL_HELVETICA);
01443   }
01444 
01445   previewBox->redraw();
01446 }
01447 
01448 
01449 //
01450 // 'Fl_File_Chooser::value()' - Return a selected filename.
01451 //
01452 
01453 const char *                    // O - Filename or NULL
01454 Fl_File_Chooser::value(int f)   // I - File number
01455 {
01456   int           i;              // Looping var
01457   int           fcount;         // Number of selected files
01458   const char    *name;          // Current filename
01459   static char   pathname[FL_PATH_MAX];  // Filename + directory
01460 
01461 
01462   name = fileName->value();
01463 
01464   if (!(type_ & MULTI)) {
01465     // Return the filename in the filename field...
01466     if (!name || !name[0]) return NULL;
01467     else return name;
01468   }
01469 
01470   // Return a filename from the list...
01471   for (i = 1, fcount = 0; i <= fileList->size(); i ++)
01472     if (fileList->selected(i)) {
01473       // See if this file is a selected file/directory...
01474       name = fileList->text(i);
01475 
01476       fcount ++;
01477 
01478       if (fcount == f) {
01479         if (directory_[0]) {
01480           snprintf(pathname, sizeof(pathname), "%s/%s", directory_, name);
01481         } else {
01482           strlcpy(pathname, name, sizeof(pathname));
01483         }
01484 
01485         return pathname;
01486       }
01487     }
01488 
01489   // If nothing is selected, use the filename field...
01490   if (!name || !name[0]) return NULL;
01491   else return name;
01492 }
01493 
01494 
01495 //
01496 // 'Fl_File_Chooser::value()' - Set the current filename.
01497 //
01498 
01499 void
01500 Fl_File_Chooser::value(const char *filename)
01501                                         // I - Filename + directory
01502 {
01503   int   i,                              // Looping var
01504         fcount;                         // Number of items in list
01505   char  *slash;                         // Directory separator
01506   char  pathname[FL_PATH_MAX];          // Local copy of filename
01507 
01508 
01509 //  printf("Fl_File_Chooser::value(\"%s\")\n", filename == NULL ? "(null)" : filename);
01510 
01511   // See if the filename is the "My System" directory...
01512   if (filename == NULL || !filename[0]) {
01513     // Yes, just change the current directory...
01514     directory(filename);
01515     fileName->value("");
01516     okButton->deactivate();
01517     return;
01518   }
01519 
01520 #ifdef WIN32
01521   // See if the filename contains backslashes...
01522   char  fixpath[FL_PATH_MAX];                   // Path with slashes converted
01523   if (strchr(filename, '\\')) {
01524     // Convert backslashes to slashes...
01525     strlcpy(fixpath, filename, sizeof(fixpath));
01526 
01527     for (slash = strchr(fixpath, '\\'); slash; slash = strchr(slash + 1, '\\'))
01528       *slash = '/';
01529 
01530     filename = fixpath;
01531   }
01532 #endif // WIN32
01533 
01534   // See if there is a directory in there...
01535   fl_filename_absolute(pathname, sizeof(pathname), filename);
01536 
01537   if ((slash = strrchr(pathname, '/')) != NULL) {
01538     // Yes, change the display to the directory... 
01539     if (!fl_filename_isdir(pathname)) *slash++ = '\0';
01540 
01541     directory(pathname);
01542     if (*slash == '/') slash = pathname;
01543   } else {
01544     directory(".");
01545     slash = pathname;
01546   }
01547 
01548   // Set the input field to the absolute path...
01549   if (slash > pathname) slash[-1] = '/';
01550 
01551   fileName->value(pathname);
01552   fileName->position(0, strlen(pathname));
01553   okButton->activate();
01554 
01555   // Then find the file in the file list and select it...
01556   fcount = fileList->size();
01557 
01558   fileList->deselect(0);
01559   fileList->redraw();
01560 
01561   for (i = 1; i <= fcount; i ++)
01562 #if defined(WIN32) || defined(__EMX__)
01563     if (strcasecmp(fileList->text(i), slash) == 0) {
01564 #else
01565     if (strcmp(fileList->text(i), slash) == 0) {
01566 #endif // WIN32 || __EMX__
01567 //      printf("Selecting line %d...\n", i);
01568       fileList->topline(i);
01569       fileList->select(i);
01570       break;
01571     }
01572 }
01573 
01574 
01575 //
01576 // 'compare_dirnames()' - Compare two directory names.
01577 //
01578 
01579 static int
01580 compare_dirnames(const char *a, const char *b) {
01581   int alen, blen;
01582 
01583   // Get length of each string...
01584   alen = strlen(a) - 1;
01585   blen = strlen(b) - 1;
01586 
01587   if (alen < 0 || blen < 0) return alen - blen;
01588 
01589   // Check for trailing slashes...
01590   if (a[alen] != '/') alen ++;
01591   if (b[blen] != '/') blen ++;
01592 
01593   // If the lengths aren't the same, then return the difference...
01594   if (alen != blen) return alen - blen;
01595 
01596   // Do a comparison of the first N chars (alen == blen at this point)...
01597 #ifdef WIN32
01598   return strncasecmp(a, b, alen);
01599 #else
01600   return strncmp(a, b, alen);
01601 #endif // WIN32
01602 }
01603 
01604 
01605 //
01606 // 'quote_pathname()' - Quote a pathname for a menu.
01607 //
01608 
01609 static void
01610 quote_pathname(char       *dst,         // O - Destination string
01611                const char *src,         // I - Source string
01612                int        dstsize)      // I - Size of destination string
01613 {
01614   dstsize --;
01615 
01616   while (*src && dstsize > 1) {
01617     if (*src == '\\') {
01618       // Convert backslash to forward slash...
01619       *dst++ = '\\';
01620       *dst++ = '/';
01621       src ++;
01622     } else {
01623       if (*src == '/') *dst++ = '\\';
01624 
01625       *dst++ = *src++;
01626     }
01627   }
01628 
01629   *dst = '\0';
01630 }
01631 
01632 
01633 //
01634 // 'unquote_pathname()' - Unquote a pathname from a menu.
01635 //
01636 
01637 static void
01638 unquote_pathname(char       *dst,       // O - Destination string
01639                  const char *src,       // I - Source string
01640                  int        dstsize)    // I - Size of destination string
01641 {
01642   dstsize --;
01643 
01644   while (*src && dstsize > 1) {
01645     if (*src == '\\') src ++;
01646     *dst++ = *src++;
01647   }
01648 
01649   *dst = '\0';
01650 }
01651 
01652 
01653 //
01654 // End of "$Id: Fl_File_Chooser2.cxx 8063 2010-12-19 21:20:10Z matt $".
01655 //