Flu_File_Chooser.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:98k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: Flu_File_Chooser.cpp,v $
  4.  * PRODUCTION Revision 1000.1  2004/06/01 21:05:46  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.3
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*
  10.  * These files were imported into NCBI's CVS directly from FLU version 2.9.1.
  11.  * Modifications to the source are listed below.
  12.  *
  13.  * ==========================================================================
  14.  * $Log: Flu_File_Chooser.cpp,v $
  15.  * Revision 1000.1  2004/06/01 21:05:46  gouriano
  16.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.3
  17.  *
  18.  * Revision 1.3  2004/05/21 22:27:51  gorelenk
  19.  * Added PCH ncbi_pch.hpp
  20.  *
  21.  * Revision 1.2  2004/03/11 14:10:10  dicuccio
  22.  * Fixes for 64-bit compilation.  Cast away const from strchr() where needed
  23.  *
  24.  * Revision 1.1  2004/03/11 13:51:39  dicuccio
  25.  * Imported FLU version 2.9.1.  Altered export specifiers to match NCBI layout.
  26.  * Altered include paths to match NCBI toolkit layout.
  27.  *
  28.  * ==========================================================================
  29.  */
  30. // $Id: Flu_File_Chooser.cpp,v 1000.1 2004/06/01 21:05:46 gouriano Exp $
  31. /***************************************************************
  32.  *                FLU - FLTK Utility Widgets 
  33.  *  Copyright (C) 2002 Ohio Supercomputer Center, Ohio State University
  34.  *
  35.  * This file and its content is protected by a software license.
  36.  * You should have received a copy of this license with this file.
  37.  * If not, please contact the Ohio Supercomputer Center immediately:
  38.  * Attn: Jason Bryan Re: FLU 1224 Kinnear Rd, Columbus, Ohio 43212
  39.  * 
  40.  ***************************************************************/
  41. #include <ncbi_pch.hpp>
  42. #include <stdio.h>
  43. #include <string.h>
  44. #include <stdlib.h>
  45. #include <ctype.h>
  46. #include <time.h>
  47. #include <sys/types.h>
  48. #include <sys/stat.h>
  49. #ifdef WIN32
  50. #include <windows.h>
  51. #include <shellapi.h>
  52. #include <lmcons.h>
  53. #endif
  54. #if defined WIN32 && !defined CYGWIN
  55. #include <direct.h>
  56. #else
  57. #include <unistd.h>
  58. #endif
  59. #include <FL/Fl.H>
  60. #include <FL/fl_draw.H>
  61. #include <FL/fl_ask.H>
  62. #include <FL/math.h>
  63. #include <FL/filename.H>
  64. #include <FL/Fl_Pixmap.H>
  65. #include <FL/Fl_Scroll.H>
  66. #include <FL/Fl_Shared_Image.H>
  67. #include <gui/widgets/FLU/flu_pixmaps.h>
  68. #include <gui/widgets/FLU/Flu_Label.h>
  69. #include <gui/widgets/FLU/Flu_Tree_Browser.h>
  70. #include <gui/widgets/FLU/Flu_Separator.h>
  71. #include <gui/widgets/FLU/Flu_Enumerations.h>
  72. #include <gui/widgets/FLU/Flu_File_Chooser.h>
  73. #include <gui/widgets/FLU/flu_file_chooser_pixmaps.h>
  74. #include <gui/widgets/FLU/flu_pixmaps.h>
  75. // just a string that no file could probably ever be called
  76. #define FAVORITES_UNIQUE_STRING   "t!@#$%^&*(Favorites)-=+"
  77. #define DEFAULT_ENTRY_WIDTH 235
  78. Fl_Pixmap up_folder_img( (char*const*)big_folder_up_xpm ),
  79.   trash( (char*const*)trash_xpm ),
  80.   new_folder( (char*const*)big_folder_new_xpm ),
  81.   reload( (char*const*)reload_xpm ),
  82.   preview_img( (char*const*)monalisa_xpm ),
  83.   file_list_img( (char*const*)filelist_xpm ),
  84.   file_listwide_img( (char*const*)filelistwide_xpm ),
  85.   fileDetails( (char*const*)filedetails_xpm ),
  86.   add_to_favorite_folder( (char*const*)folder_favorite_xpm ),
  87.   home( (char*const*)bighome_xpm ),
  88.   favorites( (char*const*)bigfavorites_xpm ),
  89.   desktop( (char*const*)desktop_xpm ),
  90.   folder_closed( (char*const*)folder_closed_xpm ),
  91.   default_file( (char*const*)textdoc_xpm ),
  92.   my_computer( (char*const*)my_computer_xpm ),
  93.   computer( (char*const*)computer_xpm ),
  94.   disk_drive( (char*const*)disk_drive_xpm ),
  95.   cd_drive( (char*const*)cd_drive_xpm ),
  96.   floppy_drive( (char*const*)floppy_drive_xpm ),
  97.   removable_drive( (char*const*)removable_drive_xpm ),
  98.   ram_drive( (char*const*)ram_drive_xpm ),
  99.   network_drive( (char*const*)network_drive_xpm ),
  100.   documents( (char*const*)filled_folder_xpm ),
  101.   littlehome( (char*const*)home_xpm ),
  102.   little_favorites( (char*const*)mini_folder_favorites_xpm ),
  103.   little_desktop( (char*const*)mini_desktop_xpm ),
  104.   bigdocuments( (char*const*)bigdocuments_xpm ),
  105.   bigtemporary( (char*const*)bigtemporary_xpm );
  106. #define streq(a,b) (strcmp(a,b)==0)
  107. Flu_File_Chooser::FileTypeInfo* Flu_File_Chooser::types = NULL;
  108. int Flu_File_Chooser::numTypes = 0;
  109. int Flu_File_Chooser::typeArraySize = 0;
  110. Flu_File_Chooser::ContextHandlerVector Flu_File_Chooser::contextHandlers;
  111. Flu_File_Chooser::PreviewHandlerVector Flu_File_Chooser::previewHandlers;
  112. Flu_File_Chooser::ImgTxtPreview* Flu_File_Chooser::imgTxtPreview = 0;
  113. int (*Flu_File_Chooser::customSort)(const char*,const char*) = 0;
  114. // taken explicitly from fltk/src/filename_match.cxx
  115. // and changed to support case-sensitive matching
  116. static int flu_filename_match(const char *s, const char *p)
  117. {
  118.   int matched;
  119.   for (;;) {
  120.     switch(*p++) {
  121.     case '?' : // match any single character
  122.       if (!*s++) return 0;
  123.       break;
  124.     case '*' : // match 0-n of any characters
  125.       if (!*p) return 1; // do trailing * quickly
  126.       while (!flu_filename_match(s, p)) if (!*s++) return 0;
  127.       return 1;
  128.     case '[': { // match one character in set of form [abc-d] or [^a-b]
  129.       if (!*s) return 0;
  130.       int reverse = (*p=='^' || *p=='!'); if (reverse) p++;
  131.       matched = 0;
  132.       char last = 0;
  133.       while (*p) {
  134. if (*p=='-' && last) {
  135.   if (*s <= *++p && *s >= last ) matched = 1;
  136.   last = 0;
  137. } else {
  138.   if (*s == *p) matched = 1;
  139. }
  140. last = *p++;
  141. if (*p==']') break;
  142.       }
  143.       if (matched == reverse) return 0;
  144.       s++; p++;}
  145.     break;
  146.     case '{' : // {pattern1|pattern2|pattern3}
  147.     NEXTCASE:
  148.     if (flu_filename_match(s,p)) return 1;
  149.     for (matched = 0;;) {
  150.       switch (*p++) {
  151.       case '\': if (*p) p++; break;
  152.       case '{': matched++; break;
  153.       case '}': if (!matched--) return 0; break;
  154.       case '|': case ',': if (matched==0) goto NEXTCASE;
  155.       case 0: return 0;
  156.       }
  157.     }
  158.     case '|': // skip rest of |pattern|pattern} when called recursively
  159.     case ',':
  160.       for (matched = 0; *p && matched >= 0;) {
  161. switch (*p++) {
  162. case '\': if (*p) p++; break;
  163. case '{': matched++; break;
  164. case '}': matched--; break;
  165. }
  166.       }
  167.       break;
  168.     case '}':
  169.       break;
  170.     case 0: // end of pattern
  171.       return !*s;
  172.     case '\': // quote next character
  173.       if (*p) p++;
  174.     default:
  175.       //if (tolower(*s) != tolower(*(p-1))) return 0;
  176.       if( *s != *(p-1) ) return 0;
  177.       s++;
  178.       break;
  179.     }
  180.   }
  181. }
  182. void Flu_File_Chooser :: add_context_handler( int type, const char *ext, const char *name,
  183.       void (*cb)(const char*,int,void*), void *cbd )
  184. {
  185.   if( cb == NULL )
  186.     return;
  187.   ContextHandler h;
  188.   h.ext = ext ? ext : "";
  189.   h.ext.downcase();
  190.   h.type = type;
  191.   h.name = name;
  192.   h.callback = cb;
  193.   h.callbackData = cbd;
  194.   Flu_File_Chooser::contextHandlers.add( h );
  195. }
  196. void Flu_File_Chooser :: add_preview_handler( PreviewWidgetBase *w )
  197. {
  198.   if( w == NULL )
  199.     return;
  200.   Flu_File_Chooser::previewHandlers.add( w );
  201. }
  202. // extensions == NULL implies directories
  203. void Flu_File_Chooser :: add_type( const char *extensions, const char *short_description, Fl_Image *icon )
  204. {
  205.   FluSimpleString ext;
  206.   if( extensions )
  207.     ext = extensions;
  208.   else
  209.     ext = "t"; // indicates a directory
  210.   ext.upcase();
  211.   // are we overwriting an existing type?
  212.   for( int i = 0; i < numTypes; i++ )
  213.     {
  214.       if( types[i].extensions == ext )
  215. {
  216.   types[i].icon = icon;
  217.   types[i].type = short_description;
  218.   return;
  219. }
  220.     }
  221.   if( numTypes == typeArraySize )
  222.     {
  223.       int newSize = ( typeArraySize == 0 ) ? 1 : typeArraySize*2; // double the size of the old list (same behavior as STL vector)
  224.       // allocate the new list
  225.       FileTypeInfo* newTypes = new FileTypeInfo[ newSize ];
  226.       // copy the old list to the new list
  227.       for( int i = 0; i < numTypes; i++ )
  228. {
  229.   newTypes[i].icon = types[i].icon;
  230.   newTypes[i].extensions = types[i].extensions;
  231.   newTypes[i].type = types[i].type;
  232. }
  233.       // delete the old list and replace it with the new list
  234.       delete[] types;
  235.       types = newTypes;
  236.       typeArraySize = newSize;
  237.     }
  238.   types[numTypes].icon = icon;
  239.   types[numTypes].extensions = ext;
  240.   types[numTypes].type = short_description;
  241.   numTypes++;
  242. }
  243. Flu_File_Chooser::FileTypeInfo* Flu_File_Chooser :: find_type( const char *extension )
  244. {
  245.   FluSimpleString ext;
  246.   if( extension )
  247.     ext = extension;
  248.   else
  249.     ext = "t"; // indicates a directory
  250.   ext.upcase();
  251.   // lookup the type based on the extension
  252.   for( int i = 0; i < numTypes; i++ )
  253.     {
  254.       // check extension against every token
  255.       FluSimpleString e = types[i].extensions;
  256.       char *tok = strtok( (char*)e.c_str(), " ," );
  257.       while( tok )
  258. {
  259.   if( ext == tok )
  260.     return &(types[i]);
  261.   tok = strtok( NULL, " ," );
  262. }
  263.     }
  264.   return NULL;
  265. }
  266. Flu_File_Chooser :: Flu_File_Chooser( const char *pathname, const char *pat, int type, const char *title )
  267.   : Fl_Double_Window( 600, 400, title ),
  268.     filename( 70, h()-60, w()-70-85-10, 25, "Filename:", this ),
  269.     ok( w()-90, h()-60, 85, 25, "Ok" ),
  270.     cancel( w()-90, h()-30, 85, 25, "Cancel" ),
  271.     entryPopup( 0, 0, 0, 0 )
  272. {
  273.   Fl_Double_Window::size_range( 600, 400 );
  274.   Fl_Group *g;
  275.   add_type( NULL, "Directory", &folder_closed );
  276.   history = currentHist = NULL;
  277.   walkingHistory = false;
  278.   fileEditing = true;
  279. #ifdef WIN32
  280.   refreshDrives = true;
  281.   caseSort = false;
  282. #else
  283.   caseSort = true;
  284. #endif
  285.   int oldNormalSize = FL_NORMAL_SIZE;
  286.   FL_NORMAL_SIZE = 12;
  287.   selectionType = type;
  288.   filenameEnterCallback = filenameTabCallback = false;
  289.   sortMethod = SORT_NAME;
  290.   lastSelected = NULL;
  291.   filename.labelsize( 12 );
  292.   filename.when( FL_WHEN_ENTER_KEY_ALWAYS );
  293.   filename.callback( _filenameCB, this );
  294.   filename.value( "" );
  295.   Fl_Group *quickIcons = new Fl_Group( 5, 5, 100, h()-10-60 );
  296.   quickIcons->box( FL_DOWN_BOX );
  297.   quickIcons->color( FL_DARK3 );
  298.   Flu_Button *desktopBtn = new Flu_Button( 30, 18, 50, 48 );
  299.   desktopBtn->box( FL_FLAT_BOX );
  300.   desktopBtn->image( desktop );
  301.   desktopBtn->enter_box( FL_THIN_UP_BOX );
  302.   desktopBtn->color( FL_DARK3 );
  303.   desktopBtn->callback( _desktopCB, this );
  304.   { 
  305.     Flu_Label *l = new Flu_Label( 5, 62, 100, 20, "Desktop" );
  306.     l->labelcolor( FL_WHITE );
  307.     l->align( FL_ALIGN_CENTER );
  308.   }
  309.   Flu_Button *homeBtn = new Flu_Button( 30, 98, 50, 48 );
  310.   homeBtn->box( FL_FLAT_BOX );
  311.   homeBtn->enter_box( FL_THIN_UP_BOX );
  312.   homeBtn->color( FL_DARK3 );
  313.   homeBtn->callback( _homeCB, this );
  314.   {
  315. #ifdef WIN32
  316.     Flu_Label *l = new Flu_Label( 5, 142, 100, 20, "My Computer" );
  317.     homeBtn->image( my_computer );
  318. #else
  319.     Flu_Label *l = new Flu_Label( 5, 142, 100, 20, "Home" );
  320.     homeBtn->image( home );
  321. #endif
  322.     l->labelcolor( FL_WHITE );
  323.     l->align( FL_ALIGN_CENTER );
  324.   }
  325.   Flu_Button *documentsBtn = new Flu_Button( 30, 178, 50, 48 );
  326.   documentsBtn->box( FL_FLAT_BOX );
  327.   documentsBtn->enter_box( FL_THIN_UP_BOX );
  328.   documentsBtn->labelcolor( FL_WHITE );
  329.   documentsBtn->color( FL_DARK3 );
  330.   documentsBtn->callback( _documentsCB, this );
  331.   { 
  332. #ifdef WIN32
  333.     Flu_Label *l = new Flu_Label( 5, 222, 100, 20, "My Documents" );
  334.     documentsBtn->image( &bigdocuments );
  335. #else
  336.     Flu_Label *l = new Flu_Label( 5, 222, 100, 20, "Temporary" );
  337.     documentsBtn->image( &bigtemporary );
  338. #endif
  339.     l->labelcolor( FL_WHITE );
  340.     l->align( FL_ALIGN_CENTER );
  341.   }
  342.   Flu_Button *favoritesBtn = new Flu_Button( 30, 258, 50, 48 );
  343.   favoritesBtn->box( FL_FLAT_BOX );
  344.   favoritesBtn->image( favorites );
  345.   favoritesBtn->enter_box( FL_THIN_UP_BOX );
  346.   favoritesBtn->color( FL_DARK3 );
  347.   favoritesBtn->callback( _favoritesCB, this );
  348.   { 
  349.     Flu_Label *l = new Flu_Label( 5, 302, 100, 20, "Favorites" );
  350.     l->labelcolor( FL_WHITE );
  351.     l->align( FL_ALIGN_CENTER );
  352.   }
  353.   favoritesList = new Fl_Browser( 0, 0, 0, 0 );
  354.   favoritesList->hide();
  355.   {
  356.     Fl_Group* dummy = new Fl_Group( 5, h()-10-61, 100, 1 );
  357.     quickIcons->resizable( dummy );
  358.   }
  359.   quickIcons->end();
  360.   Fl_Group *dummy = new Fl_Group( 110, 0, w()-110, 60 );
  361.   filesystems = new Flu_Combo_Tree( 166, 5, w()-171, 22, "Location:" );
  362.   filesystems->editable( false );
  363.   filesystems->pop_height( 200 );
  364.   filesystems->tree.all_branches_always_open( true );
  365. #ifdef WIN32
  366.   filesystems->tree.show_root( false );
  367. #endif
  368.   filesystems->tree.show_connectors( false );
  369.   filesystems->tree.horizontal_gap( -10 );
  370.   filesystems->tree.show_leaves( false );
  371.   filesystems->callback( _filesystemsCB, this );
  372.   ////////////////////////////////////////////////////////////////
  373.   g = new Fl_Group( 110, 30, w()-110, 30 ); // group enclosing all the buttons at top
  374.   backBtn = new Flu_Button( 285, 33, 25, 25, "@<-" );
  375.   backBtn->labelcolor( fl_rgb_color( 80, 180, 200 ) );
  376.   backBtn->labelsize( 16 );
  377.   backBtn->box( FL_FLAT_BOX );
  378.   backBtn->enter_box( FL_THIN_UP_BOX );
  379.   backBtn->callback( _backCB, this );
  380.   backBtn->tooltip( "Go back one directory in the history" );
  381.   forwardBtn = new Flu_Button( 310, 33, 25, 25, "@->" );
  382.   forwardBtn->labelcolor( fl_rgb_color( 80, 180, 200 ) );
  383.   forwardBtn->labelsize( 16 );
  384.   forwardBtn->box( FL_FLAT_BOX );
  385.   forwardBtn->enter_box( FL_THIN_UP_BOX );
  386.   forwardBtn->callback( _forwardCB, this );
  387.   forwardBtn->tooltip( "Go forward one directory in the history" );
  388.   upDirBtn = new Flu_Button( 335, 33, 25, 25 );
  389.   upDirBtn->image( up_folder_img );
  390.   upDirBtn->box( FL_FLAT_BOX );
  391.   upDirBtn->enter_box( FL_THIN_UP_BOX );
  392.   upDirBtn->callback( upDirCB, this );
  393.   upDirBtn->tooltip( "Go to the parent directory" );
  394.   reloadBtn = new Flu_Button( 360, 33, 25, 25 );
  395.   reloadBtn->image( reload );
  396.   reloadBtn->box( FL_FLAT_BOX );
  397.   reloadBtn->enter_box( FL_THIN_UP_BOX );
  398.   reloadBtn->callback( reloadCB, this );
  399.   reloadBtn->tooltip( "Refresh this directory" );
  400.   {
  401.     Flu_Separator *sep = new Flu_Separator( 385, 32, 10, 28 );
  402.     sep->type( Flu_Separator::VERTICAL );
  403.     sep->box( FL_ENGRAVED_BOX );
  404.   }
  405.   trashBtn = new Flu_Button( 395, 33, 25, 25 );
  406.   trashBtn->image( trash );
  407.   trashBtn->box( FL_FLAT_BOX );
  408.   trashBtn->enter_box( FL_THIN_UP_BOX );
  409.   trashBtn->callback( _trashCB, this );
  410.   trashBtn->tooltip( "Delete file(s)" );
  411.   newDirBtn = new Flu_Button( 420, 33, 25, 25 );
  412.   newDirBtn->image( new_folder );
  413.   newDirBtn->box( FL_FLAT_BOX );
  414.   newDirBtn->enter_box( FL_THIN_UP_BOX );
  415.   newDirBtn->callback( _newFolderCB, this );
  416.   newDirBtn->tooltip( "Create new directory" );
  417.   addFavoriteBtn = new Flu_Button( 445, 33, 25, 25 );
  418.   addFavoriteBtn->image( add_to_favorite_folder );
  419.   addFavoriteBtn->box( FL_FLAT_BOX );
  420.   addFavoriteBtn->enter_box( FL_THIN_UP_BOX );
  421.   addFavoriteBtn->callback( _addToFavoritesCB, this );
  422.   addFavoriteBtn->tooltip( "Add this directory to my favorites" );
  423.   {
  424.     Flu_Separator *sep = new Flu_Separator( 470, 32, 10, 28 );
  425.     sep->type( Flu_Separator::VERTICAL );
  426.     sep->box( FL_ENGRAVED_BOX );
  427.   }
  428.   previewBtn = new Flu_Button( 482, 33, 23, 25 );
  429.   previewBtn->type( FL_TOGGLE_BUTTON );
  430.   previewBtn->image( preview_img );
  431.   previewBtn->callback( _previewCB, this );
  432.   previewBtn->tooltip( "Preview files" );
  433.   {
  434.     Fl_Group *g = new Fl_Group( 511, 33, 81, 25 );
  435.     fileListBtn = new Flu_Button( 511, 33, 25, 25 );
  436.     fileListBtn->type( FL_RADIO_BUTTON );
  437.     fileListBtn->value(1);
  438.     fileListBtn->callback( reloadCB, this );
  439.     fileListBtn->image( file_list_img );
  440.     fileListBtn->tooltip( "List mode" );
  441.     fileListWideBtn = new Flu_Button( 540, 33, 25, 25 );
  442.     fileListWideBtn->type( FL_RADIO_BUTTON );
  443.     fileListWideBtn->callback( reloadCB, this );
  444.     fileListWideBtn->image( file_listwide_img );
  445.     fileListWideBtn->tooltip( "Wide List mode" );
  446.     fileDetailsBtn = new Flu_Button( 569, 33, 25, 25 );
  447.     fileDetailsBtn->type( FL_RADIO_BUTTON );
  448.     fileDetailsBtn->image( fileDetails );
  449.     fileDetailsBtn->callback( reloadCB, this );
  450.     fileDetailsBtn->tooltip( "Detail mode" );
  451.     g->end();
  452.   }
  453.   hiddenFiles = new Fl_Check_Button( 110, 33, 130, 25, "Show Hidden Files" );
  454.   hiddenFiles->callback( reloadCB, this );
  455. #ifdef WIN32
  456.   hiddenFiles->hide();
  457. #endif
  458.   g->resizable( hiddenFiles );
  459.   g->end();
  460.   dummy->resizable( filesystems );
  461.   dummy->end();
  462.   ////////////////////////////////////////////////////////////////
  463.   previewTile = new PreviewTile( 110, 60, w()-110-5, h()-80-40-5, this );
  464.   fileGroup = new Fl_Group( 110, 60, w()-120-5, h()-80-40-5 );
  465.   {
  466.     fileGroup->box( FL_DOWN_FRAME );
  467.     filelist = new FileList( fileGroup->x()+2, fileGroup->y()+2, fileGroup->w()-4, fileGroup->h()-4, this );
  468.     filelist->box( FL_FLAT_BOX );
  469.     filelist->color( FL_WHITE );
  470.     filelist->type( FL_HORIZONTAL );
  471.     filelist->spacing( 4, 1 );
  472.     filelist->scrollbar.linesize( DEFAULT_ENTRY_WIDTH+4 );
  473.     filelist->end();
  474.     fileDetailsGroup = new Fl_Group( fileGroup->x()+2, fileGroup->y()+2, fileGroup->w()-4, fileGroup->h()-4 );
  475.     filecolumns = new FileColumns( fileGroup->x()+2, fileGroup->y()+2, fileGroup->w()-4, 20, this );
  476.     //filecolumns->hide();
  477.     filescroll = new Fl_Scroll( fileGroup->x()+2, fileGroup->y()+22, fileGroup->w()-4, fileGroup->h()-20-4 );
  478.     filescroll->color( FL_WHITE );
  479.     filescroll->scrollbar.linesize( 20 );
  480.     filescroll->box( FL_FLAT_BOX );
  481.     filescroll->type( Fl_Scroll::VERTICAL );
  482.     {
  483.       filedetails = new FileDetails( fileGroup->x()+2, fileGroup->y()+22, fileGroup->w()-4, fileGroup->h()-20-4, this );
  484.       filedetails->end();
  485.     }
  486.     filescroll->end();
  487.     //filescroll->hide();
  488.     fileDetailsGroup->end();
  489.     fileDetailsGroup->resizable( filescroll );
  490.     fileGroup->resizable( filelist );
  491.   }
  492.   fileGroup->end();
  493.   previewGroup = new PreviewGroup( fileGroup->x()+fileGroup->w(), fileGroup->y(), previewTile->w()-fileGroup->w(), fileGroup->h(), this );
  494.   previewGroup->end();
  495.   {
  496.     Fl_Box *b = new Fl_Box( previewTile->x()+250, previewTile->y(), previewTile->w()-350, previewTile->h() );
  497.     previewTile->add_resizable( *b );
  498.   }
  499.   previewTile->end();
  500.   previewTile->position( previewGroup->x(), previewGroup->y(), previewTile->x()+previewTile->w(), previewGroup->y() );
  501.   previewTile->last = previewTile->x()+previewTile->w()-200;
  502.   resizable( previewTile );
  503.   filePattern = new Flu_Combo_List( 70, h()-30, w()-70-85-10, 25, "File types:" );
  504.   filePattern->editable( false );
  505.   filePattern->callback( reloadCB, this );
  506.   filePattern->pop_height( 200 );
  507.   ok.callback( _okCB, this );
  508.   cancel.callback( _cancelCB, this );
  509.   {
  510.     g = new Fl_Group( 0, h()-60, w(), 30 );
  511.     g->end();
  512.     g->add( filename );
  513.     g->add( ok );
  514.     g->resizable( filename );
  515.     g = new Fl_Group( 0, h()-30, w(), 30 );
  516.     g->end();
  517.     g->add( filePattern );
  518.     g->add( cancel );
  519.     g->resizable( filePattern );
  520.   }
  521.   end();
  522.   FL_NORMAL_SIZE = oldNormalSize;
  523.   char buf[1024];
  524.   // determine the user's home area for loading/storing favorites and transient config info
  525. #ifdef WIN32
  526.   {
  527.     LPTSTR lpszSystemInfo;          // pointer to system information string 
  528.     DWORD cchBuff = 256;            // size of user name
  529.     TCHAR tchBuffer[UNLEN + 1];     // buffer for expanded string
  530.     lpszSystemInfo = tchBuffer;
  531.     // Get the user name.
  532.     GetUserName(lpszSystemInfo, &cchBuff);
  533.     userHome = "C:/Documents and Settings/";
  534.     userHome += lpszSystemInfo;
  535.     userHome += "/";
  536.   }
  537. #else
  538.   fl_filename_expand( buf, 1024, "~/" );
  539.   userHome = buf;
  540. #endif
  541.   configFilename = userHome + ".Flu_File_Chooser.favorites";
  542.   // try to load the favorites
  543.   {
  544.     FILE *f = fopen( configFilename.c_str(), "r" );
  545.     //printf( "load: %s %Xn", buf, f );
  546.     if( f )
  547.       {
  548. buf[0] = '';
  549. while( !feof(f) )
  550.   {
  551.     fgets( buf, 1024, f );
  552.     char *newline = strrchr( buf, 'n' );
  553.     if( newline )
  554.       *newline = '';
  555.     if( strlen( buf ) > 0 )
  556.       {
  557. // eliminate duplicates
  558. bool duplicate = false;
  559. for( int i = 1; i <= favoritesList->size(); i++ )
  560.   {
  561.     if( streq( buf, favoritesList->text(i) ) )
  562.       {
  563. duplicate = true;
  564. break;
  565.       }
  566.   }
  567. if( !duplicate )
  568.   favoritesList->add( buf );
  569.       }
  570.   }
  571. fclose( f );
  572.       }
  573.   }
  574.   if( !imgTxtPreview )
  575.     {
  576.       imgTxtPreview = new ImgTxtPreview();
  577.       add_preview_handler( imgTxtPreview );
  578.     }
  579.   pattern( pat );
  580.   default_file_icon( &default_file );
  581.   cd( NULL ); // prime with the current directory
  582.   clear_history();
  583.   cd( pathname );
  584. }
  585. Flu_File_Chooser :: ~Flu_File_Chooser()
  586. {
  587.   Fl::remove_timeout( Entry::_editCB );
  588.   filelist->clear();
  589.   filedetails->clear();
  590.   clear_history();
  591. }
  592. void Flu_File_Chooser :: cancelCB()
  593.   filename.value("");
  594.   filename.position( filename.size(), filename.size() );
  595.   unselect_all();
  596.   hide();
  597. }
  598. void Flu_File_Chooser :: pattern( const char *p )
  599. {
  600.   // just like in Fl_File_Chooser, we accept tab, |, and ; delimited strings like this:
  601.   // "Description (patterns)" or just "patterns" where patterns is 
  602.   // of the form *.xxx or *.{xxx,yyy,zzz}}
  603.   rawPattern = p;
  604.   // clear out the old
  605.   filePattern->list.clear();
  606.   filePattern->input.value( "" );
  607.   patterns.clear();
  608.   if( p == 0 )
  609.     p = "*";
  610.   else if( p[0] == '' )
  611.     p = "*";
  612.   FluSimpleString pat = p, pattern;
  613.   bool addedAll = false;
  614.   const char *next = strtok( (char*)pat.c_str(), "t|;" );
  615.   const char *start;
  616.   while( next )
  617.     {
  618.       //printf( "next pattern = %sn", next );
  619.       if( next[0] == '' )
  620. break;
  621.       // eat whitespace
  622.       while( isspace( *next ) )
  623. next++;
  624.       // degenerate check
  625.       if( strcmp( next, "*" ) == 0 )
  626. {
  627.   addedAll = true;
  628.   filePattern->list.add( "All Files (*)" );
  629.   patterns.add( "*" );
  630.   next = strtok( NULL, "t|;" );
  631.   continue;
  632. }
  633.       // extract the patterns from the substring
  634.       if( next[0] != '*' ) // starts with description
  635. {
  636.   // the pattern starts after the first '('
  637.   start = strchr( next, '(' );
  638.   if( !start ) // error: couldn't find the '('
  639.     {
  640.       next = strtok( NULL, "t|;" );
  641.       continue;
  642.     }
  643.   start++; // skip the '('
  644. }
  645.       else
  646. start = next;
  647.       if( start[0] != '*' )
  648. {
  649.   next = strtok( NULL, "t|;" );
  650.   continue;
  651. }
  652.       start++; // skip the '*'
  653.       if( start[0] != '.' )
  654. {
  655.   next = strtok( NULL, "t|;" );
  656.   continue;
  657. }
  658.       start++; // skip the '.'
  659.       if( start[0] == '{' )
  660. {
  661.   // the pattern is between '{' and '}'
  662.   pattern = start+1;
  663. }
  664.       else
  665. pattern = start;
  666.       // remove the last '}'
  667.       int brace = pattern.find( '}' );
  668.       if( brace != -1 )
  669. pattern[brace] = '';
  670.       // remove the last ')'
  671.       int paren = pattern.find( ')' );
  672.       if( paren != -1 )
  673. pattern[paren] = '';
  674.       //printf( "patterns: %sn", pattern.c_str() );
  675.       if( pattern.size() )
  676. {
  677.   // add the whole string to the list
  678.   filePattern->list.add( next );
  679.   patterns.add( pattern );
  680. }
  681.       // advance to the pattern token
  682.       next = strtok( NULL, "t|;" );
  683.    }
  684.   // add all files
  685.   if( !addedAll )
  686.     {
  687.       filePattern->list.add( "All Files (*)" );
  688.       patterns.add( "*" );
  689.     }
  690.   // choose the first added item
  691.   filePattern->value( filePattern->list.text(1) );
  692. }
  693. int Flu_File_Chooser :: handle( int event )
  694. {
  695.   if( Fl_Double_Window::handle( event ) )
  696.     return 1;
  697.   else if( event == FL_KEYDOWN && Fl::event_key(FL_Escape) )
  698.     {
  699.       cancel.do_callback();
  700.       return 1;
  701.     }
  702.   else if( event == FL_KEYDOWN && Fl::event_key('a') && Fl::event_state(FL_CTRL) )
  703.     {
  704.       select_all();
  705.       return 1;
  706.     }
  707.   else
  708.     return 0;
  709. }
  710. void Flu_File_Chooser :: newFolderCB()
  711. {
  712.   // start with the name "New Folder". while the name exists, keep appending a number (1..2..etc)
  713.   FluSimpleString newName = "New Folder", path = currentDir + newName;
  714.   int count = 1;
  715.   int i;
  716.   for(;;)
  717.     {
  718.       bool found = false;
  719.       // see if any entry already has that name
  720.       Fl_Group *g = getEntryGroup();
  721.       for( i = 0; i < g->children(); i++ )
  722. {
  723.   if( ((Entry*)g->child(i))->filename == newName )
  724.     {
  725.       found = true;
  726.       break;
  727.     }
  728. }
  729.       // since an entry already exists, change the name and try again
  730.       if( found )
  731. {
  732.   char buf[16];
  733.   sprintf( buf, "%d", count++ );
  734.   newName = "New Folder" + FluSimpleString(buf);
  735.   path = currentDir + newName;
  736. }
  737.       else
  738. break;
  739.     }
  740.   // try to create the folder
  741. #if ( defined WIN32 || defined MINGW ) && !defined CYGWIN
  742.   if( mkdir( path.c_str() ) != 0 )
  743. #else
  744.   if( mkdir( path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH ) != 0 )
  745. #endif
  746.     {
  747.       fl_alert( "Could not create directory '%s'. You may not have permission to perform this operation.",
  748. newName.c_str() );
  749.       return;
  750.     }  
  751.   // create a new entry with the name of the new folder. add to either the list or the details
  752.   Entry *entry = new Entry( newName.c_str(), ENTRY_DIR, fileDetailsBtn->value(), this );
  753.   if( !fileDetailsBtn->value() )
  754.     filelist->add( *entry );
  755.   else
  756.     filedetails->add( *entry );
  757.   // switch that entry to input mode and scroll the browser to it
  758.   entry->editCB();
  759.   /*
  760.   entry->editMode = 2;
  761.   entry->value( entry->filename.c_str() );
  762.   entry->take_focus();
  763.   entry->position( 0, entry->filename.size() );  
  764.   entry->redraw();
  765.   */
  766.   if( !fileDetailsBtn->value() )
  767.     filelist->scroll_to( entry );
  768.   else
  769.     filedetails->scroll_to( entry );
  770. }
  771. void Flu_File_Chooser :: recursiveScan( const char *dir, StringVector *files )
  772. {
  773.   dirent **e;
  774.   char *name;
  775.   FluSimpleString fullpath;
  776.   int num = fl_filename_list( dir, &e );
  777.   for( int i = 0; i < num; i++ )
  778.     {
  779.       name = e[i]->d_name;
  780.       // if 'name' ends in '/' or '', remove it
  781.       if( name[strlen(name)-1] == '/' || name[strlen(name)-1] == '\' )
  782. name[strlen(name)-1] = '';
  783.       // ignore the "." and ".." names
  784.       if( strcmp( name, "." ) == 0 || strcmp( name, ".." ) == 0 )
  785. continue;
  786.       // file or directory?
  787.       fullpath = dir;
  788.       fullpath += "/";
  789.       fullpath += name;
  790.       if( fl_filename_isdir( fullpath.c_str() ) != 0 )
  791. recursiveScan( fullpath.c_str(), files );
  792.       files->add( fullpath );
  793.     }
  794.   files->add( dir );
  795. }
  796. void Flu_File_Chooser :: trashCB( bool recycle )
  797. {
  798.   // linux doesn't have a recycle bin
  799. #ifndef WIN32
  800.   recycle = false;
  801. #endif
  802.   bool inFavorites = ( currentDir == FAVORITES_UNIQUE_STRING );
  803.   if( inFavorites )
  804.     recycle = false;
  805.   // see how many files are selected
  806.   FluSimpleString name;
  807.   int selected = 0;
  808.   int i;
  809.   const char *first = "";
  810.   Fl_Group *g = getEntryGroup();
  811.   for( i = 0; i < g->children(); i++ )
  812.     {
  813.       if( ((Entry*)g->child(i))->selected )
  814. {
  815.   if( selected == 0 )
  816.     first = ((Entry*)g->child(i))->filename.c_str();
  817.   selected++;
  818. }
  819.     }
  820.    if( selected )
  821.      {
  822.        if( selected == 1 )
  823.  {
  824.    if( recycle )
  825.      {
  826.        if( !fl_ask( "Really send '%s' to the Recycle Bin?", first ) )
  827.  return;
  828.      }
  829.    else
  830.      {
  831.        if( !fl_ask( "Really delete '%s'?", first ) )
  832.  return;
  833.      }
  834.  }
  835.        else
  836.  {
  837.    if( recycle )
  838.      {
  839.        if( !fl_ask( "Really send these %d files to the Recycle Bin?", selected ) )
  840.  return;
  841.      }
  842.    else
  843.      {
  844.        if( !fl_ask( "Really delete these %d files?", selected ) )
  845.  return;
  846.      }
  847.  }
  848.        if( inFavorites )
  849.  {
  850.    for( i = 0; i < g->children(); )
  851.      {
  852.        Entry *e = ((Entry*)g->child(i));
  853.        if( e->selected )
  854.  {
  855.    favoritesList->remove(i+1);
  856.    g->remove( *e );
  857.    delete e;
  858.  }
  859.        else 
  860.  i++;
  861.      }
  862.    // save the favorites
  863.    FILE *f = fopen( configFilename.c_str(), "w" );
  864.    if( f )
  865.      {
  866.        for( i = 1; i <= favoritesList->size(); i++ )
  867.  fprintf( f, "%sn", favoritesList->text(i) );
  868.        fclose( f );
  869.      }
  870.    cd( FAVORITES_UNIQUE_STRING );
  871.    return;
  872.  }
  873. #ifdef WIN32
  874.        SHFILEOPSTRUCT fileop;
  875.        memset( &fileop, 0, sizeof(SHFILEOPSTRUCT) );
  876.        fileop.fFlags = FOF_SILENT | FOF_NOERRORUI | FOF_NOCONFIRMATION;
  877.        if( recycle )
  878.  fileop.fFlags |= FOF_ALLOWUNDO;
  879.        fileop.wFunc = FO_DELETE;
  880.        fileop.pTo = NULL;
  881. #endif
  882.        for( i = 0; i < g->children(); i++ )
  883.  {
  884.    if( ((Entry*)g->child(i))->selected )
  885.      {
  886.        int result = 0;
  887.        name = currentDir + ((Entry*)g->child(i))->filename;
  888.        // if directory, recursively remove
  889.        if( ((Entry*)g->child(i))->type == ENTRY_DIR )
  890.  {
  891.    // if we are recycling in windows, then the recursive part happens automatically
  892. #ifdef WIN32
  893.    if( !recycle )
  894. #endif
  895.      {
  896.        Fl_Group::current(0);
  897.        Fl_Window *win = new Fl_Window( 200, 100, "Notice" );
  898.        Flu_Label *label = new Flu_Label( 30, 30, 150, 30, "Preparing to delete..." );
  899.        win->end();
  900.        win->show();
  901.        Fl::check();
  902.        // recursively build a list of all files that will be deleted
  903.        StringVector files;
  904.        recursiveScan( name.c_str(), &files );
  905.        // delete all the files
  906.        label->label( "Deleting files..." );
  907.        for( unsigned int i = 0; i < files.size(); i++ )
  908.  {
  909.    //printf( "%sn", files[i].c_str() );
  910.    if( ::remove( files[i].c_str() ) != 0 )
  911.      {
  912.        win->hide();
  913.        delete win;
  914.        cd( "./" );
  915.        return;
  916.      }
  917.  }
  918.        win->hide();
  919.        delete win;
  920.        Fl::check();
  921.        continue;
  922.      }
  923.  }
  924. #ifdef WIN32
  925.        // this moves files to the recycle bin, depending on the value of 'recycle'
  926.        {
  927.  int len = name.size();
  928.  char *buf = (char*)malloc( len+2 );
  929.  strcpy( buf, name.c_str() );
  930.  buf[len+1] = ''; // have to have 2 '' at the end
  931.  fileop.pFrom = buf;
  932.  result = SHFileOperation( &fileop );
  933.  free( buf );
  934.        }
  935. #else
  936.        result = ::remove( name.c_str() );
  937. #endif
  938.        // if remove fails, report an error
  939.        if( result != 0 )
  940.  {
  941.    fl_alert( "An error ocurred while trying to delete '%s'.", name.c_str() );
  942.    cd( "./" );
  943.    return;
  944.  }
  945.      }
  946.  }
  947.        // refresh this directory
  948.        cd( "./" );
  949.     }
  950. }
  951. void Flu_File_Chooser :: filesystemsCB( const char *path )
  952. {
  953.   //printf( "filesystems: %sn", path );
  954. #ifdef WIN32
  955.   FluSimpleString p = path;
  956.   if( p == "/Favorites/" )
  957.     favoritesCB();
  958.   else if( p == "/Desktop/My Computer/" )
  959.     myComputerCB();
  960.   else if( p == "/Desktop/My Documents/" )
  961.     documentsCB();
  962.   else if( p == "/Desktop/" )
  963.     desktopCB();
  964.   // if the path leads off with "/Desktop/My Computer", then strip that part off and cd
  965.   // to the remaining
  966.   else if( strstr( path, "/Desktop/My Computer/" ) == path )
  967.     {
  968.       // seach for '(' and if present, extract the drive name and cd to it
  969.       char *paren = strrchr( path, '(' );
  970.       if( paren )
  971. {
  972.   char drive[] = "A:/";
  973.   drive[0] = paren[1];
  974.   cd( drive );
  975. }
  976.       else
  977. {
  978.   cd( path+21 );
  979. }
  980.     }
  981. #else
  982.   cd( path );
  983. #endif
  984. }
  985. void Flu_File_Chooser :: favoritesCB()
  986.   cd( FAVORITES_UNIQUE_STRING );
  987. }
  988. void Flu_File_Chooser :: myComputerCB()
  989.   cd( "/" );
  990. }
  991. void Flu_File_Chooser :: documentsCB()
  992. {
  993. #ifdef WIN32
  994.   FluSimpleString s = userHome + "My Documents";
  995.   cd( s.c_str() );
  996. #else
  997.   cd( "/tmp/" );
  998. #endif
  999. }
  1000. Flu_File_Chooser :: FileInput :: FileInput( int x, int y, int w, int h, const char *l, Flu_File_Chooser *c )
  1001.   : Fl_Input( x, y, w, h, l )
  1002. {
  1003.   chooser = c;
  1004. }
  1005. Flu_File_Chooser :: FileInput :: ~FileInput()
  1006. {
  1007. }
  1008. int Flu_File_Chooser :: FileInput :: handle( int event )
  1009. {
  1010.   if( event == FL_KEYDOWN )
  1011.     {
  1012.       if( Fl::event_key(FL_Tab) )
  1013. {
  1014.   chooser->filenameTabCallback = true;
  1015.   FluSimpleString v(value());
  1016. #ifdef WIN32
  1017.   // turn "c:" into "c:"
  1018.   if( v.size() >= 2 )
  1019.     if( v[1] == ':' && v[2] == '' )
  1020.       {
  1021. v += "/";
  1022. value( v.c_str() );
  1023. position( size(), size() );
  1024.       }
  1025. #endif
  1026.   chooser->delayedCd = v + "*";
  1027.   Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, chooser );
  1028.   return 1;
  1029. }
  1030.       else if( Fl::event_key(FL_Left) )
  1031. {
  1032.   if( Fl_Input::position() == 0 )
  1033.     return 1;
  1034.   else
  1035.     return Fl_Input::handle( event );
  1036. }
  1037.       else if( Fl::event_key(FL_Right) )
  1038. {
  1039.   if( Fl_Input::position() == (int)strlen(Fl_Input::value()) )
  1040.     return 1;
  1041.   else
  1042.     return Fl_Input::handle( event );
  1043. }
  1044.       else if( Fl::event_key(FL_Up) || Fl::event_key(FL_Down) )
  1045. {
  1046.   chooser->getEntryContainer()->take_focus();
  1047.   if( !chooser->lastSelected )
  1048.     {
  1049.       if( chooser->getEntryGroup()->children() )
  1050. {
  1051.   Flu_File_Chooser::Entry *e = (Flu_File_Chooser::Entry*)chooser->getEntryGroup()->child(0);
  1052.   e->selected = true;
  1053.   chooser->lastSelected = e;
  1054.   e->redraw();
  1055. }
  1056.     }
  1057.   return chooser->getEntryContainer()->handle( event );
  1058. }
  1059.     }
  1060.   return Fl_Input::handle( event );
  1061. }
  1062. Flu_File_Chooser :: PreviewTile :: PreviewTile( int x, int y, int w, int h, Flu_File_Chooser *c )
  1063.    : Fl_Tile( x, y, w, h )
  1064. {
  1065.   chooser = c;
  1066. }
  1067. int Flu_File_Chooser :: PreviewTile :: handle( int event )
  1068. {
  1069.   // if we're not in preview mode, then the user isn't allowed to resize the tile
  1070.   if( !chooser->previewBtn->value() )
  1071.     return Fl_Group::handle( event );
  1072.   if( event == FL_DRAG )
  1073.     {
  1074.       // the user is probably dragging to resize the columns
  1075.       // update the sizes for each entry
  1076.       chooser->updateEntrySizes();
  1077.       chooser->redraw();
  1078.     }
  1079.   return Fl_Tile::handle(event);
  1080. }
  1081. Flu_File_Chooser :: PreviewWidgetBase :: PreviewWidgetBase()
  1082.   : Fl_Group( 0, 0, 0, 0 )
  1083. {
  1084. }
  1085. Flu_File_Chooser :: PreviewWidgetBase :: ~PreviewWidgetBase()
  1086. {
  1087. }
  1088. Flu_File_Chooser :: PreviewGroup :: PreviewGroup( int x, int y, int w, int h, Flu_File_Chooser *c )
  1089.   : Fl_Group( x, y, w, h )
  1090. {
  1091.   box( FL_DOWN_BOX );
  1092.   align( FL_ALIGN_CENTER | FL_ALIGN_CLIP );
  1093.   labelsize( 60 );
  1094.   labelfont( FL_HELVETICA );
  1095.   chooser = c;
  1096.   handled = 0;
  1097. }
  1098. void Flu_File_Chooser :: PreviewGroup :: draw()
  1099. {
  1100.   if( !chooser->previewBtn->value() )
  1101.     return;
  1102.   if( file.size() == 0 )
  1103.     return;
  1104.   FILE *f = fopen( file.c_str(), "rb" );
  1105.   if( !f )
  1106.     {
  1107.       label( "" );
  1108.       Fl_Group::draw();
  1109.       return;
  1110.     }
  1111.   fclose( f );
  1112.   if( lastFile != file )
  1113.     {
  1114.       lastFile = file;
  1115.       handled = 0;
  1116.       PreviewWidgetBase *next;
  1117.       for( int i = chooser->previewHandlers.size()-1; i >= 0; i-- )
  1118. {
  1119.   next = chooser->previewHandlers[i];
  1120.   next->hide();
  1121.   if( !handled )
  1122.     {
  1123.       Fl_Group *p = next->parent();
  1124.       Fl_Group::add( next );
  1125.       if( next->preview( file.c_str() ) != 0 )
  1126. {
  1127.   next->show();
  1128.   handled = next;
  1129. }
  1130.       Fl_Group::remove( *next );
  1131.       if( p )
  1132. p->add( next );
  1133.     }
  1134. }
  1135.     }
  1136.   if( handled == 0 )
  1137.     {
  1138.       label( "?" );
  1139.       Fl_Group::draw();
  1140.     }
  1141.   else
  1142.     {
  1143.       label( "" );
  1144.       Fl_Group *p = handled->parent();
  1145.       Fl_Group::add( handled );
  1146.       handled->resize( x()+Fl::box_dx(box()), y()+Fl::box_dy(box()),
  1147.        w()-Fl::box_dw(box()), h()-Fl::box_dh(box()) );
  1148.       Fl_Group::draw();
  1149.       Fl_Group::remove( *handled );
  1150.       if( p )
  1151. p->add( handled );
  1152.     }
  1153. }
  1154. // adapted from Fl_File_Chooser2.cxx : update_preview()
  1155. int Flu_File_Chooser :: ImgTxtPreview ::  preview( const char *filename )
  1156. {
  1157.   Fl_Shared_Image *img, // New image
  1158. *oldimg; // Old image
  1159.   int pbw, pbh; // Width and height of preview box
  1160.   int w, h; // Width and height of preview image
  1161.   window()->cursor( FL_CURSOR_WAIT );
  1162.   Fl::check();
  1163.   img = Fl_Shared_Image::get( filename );
  1164.   if( img )
  1165.     {
  1166.       window()->cursor( FL_CURSOR_DEFAULT );
  1167.       Fl::check();
  1168.     }
  1169.   oldimg = (Fl_Shared_Image*)image();
  1170.   if( oldimg )
  1171.     oldimg->release();
  1172.   image(0);
  1173.   if( !img )
  1174.     {
  1175.       // Try reading the first 1k of data for a label...
  1176.       FILE *f = fopen( filename, "rb" );
  1177.       if( f )
  1178. {
  1179.   int bytes = fread( previewTxt, 1, sizeof(previewTxt) - 1, f );
  1180.   previewTxt[bytes] = '';
  1181.   fclose( f );
  1182.       else
  1183. return 0;
  1184.       window()->cursor( FL_CURSOR_DEFAULT );
  1185.       Fl::check();
  1186.       // Scan the buffer for printable chars...
  1187.       unsigned char *ptr;
  1188.       for( ptr = previewTxt; *ptr && (isprint(*ptr) || isspace(*ptr)); ptr++ ) {}
  1189.       if( *ptr || ptr == previewTxt )
  1190. {
  1191.   // Non-printable file - can't handle
  1192.   return 0;
  1193.       else
  1194. {
  1195.   // Show the first 1k of text...
  1196.   label( (const char*)previewTxt );
  1197.   align((Fl_Align)(FL_ALIGN_CLIP | FL_ALIGN_INSIDE | FL_ALIGN_LEFT | FL_ALIGN_TOP));
  1198.   labelsize( 12 );
  1199.   labelfont( FL_COURIER );
  1200. }
  1201.     }
  1202.   else if( img->w() > 0 && img->h() > 0 )
  1203.     {
  1204.       pbw = this->w() - 20;
  1205.       pbh = this->h() - 20;
  1206.       pbw = (pbw < 10) ? 10 : pbw;
  1207.       pbh = (pbh < 10) ? 10 : pbh;
  1208.       if( img->w() > pbw || img->h() > pbh )
  1209. {
  1210.   w = pbw;
  1211.   h = int(float(w*img->h()) / float(img->w()));
  1212.   if( h > pbh )
  1213.     {
  1214.       h = pbh;
  1215.       w = int(float(h*img->w()) / float(img->h()));
  1216.     }
  1217.   oldimg = (Fl_Shared_Image *)img->copy(w, h);
  1218.   image((Fl_Image *)oldimg);
  1219.   img->release();
  1220. }
  1221.       else
  1222. image((Fl_Image *)img);
  1223.       align( FL_ALIGN_CLIP );
  1224.       label(0);
  1225.     }
  1226.   redraw();
  1227.   return 1;
  1228. }
  1229. void Flu_File_Chooser :: previewCB()
  1230. {
  1231.   if( previewBtn->value() )
  1232.     {
  1233.       fileGroup->resize( fileGroup->x(), fileGroup->y(), previewTile->last-fileGroup->x(), fileGroup->h() );
  1234.       previewGroup->resize( previewTile->last, previewGroup->y(), previewTile->w()-fileGroup->w(), previewGroup->h() );
  1235.       previewGroup->show();
  1236.     }
  1237.   else
  1238.     {
  1239.       previewTile->last = previewGroup->x();
  1240.       fileGroup->resize( fileGroup->x(), fileGroup->y(), previewTile->w(), fileGroup->h() );
  1241.       previewGroup->resize( previewTile->x()+previewTile->w(), previewGroup->y(), 0, previewGroup->h() );
  1242.       previewGroup->hide();
  1243.     }
  1244.   previewGroup->redraw();
  1245.   previewTile->init_sizes();
  1246.   //filescroll->parent()->init_sizes();
  1247.   fileDetailsGroup->parent()->init_sizes();
  1248.   updateEntrySizes();
  1249.   redraw();
  1250. }
  1251. void Flu_File_Chooser :: sortCB( Fl_Widget *w )
  1252. {
  1253.   // if the sort method is already selected, toggle the REVERSE bit
  1254.   if( w == detailNameBtn )
  1255.     {
  1256.       if( sortMethod & SORT_NAME )
  1257. sortMethod ^= SORT_REVERSE;
  1258.       else
  1259. sortMethod = SORT_NAME;
  1260.     }
  1261.   else if( w == detailSizeBtn )
  1262.     {
  1263.       if( sortMethod & SORT_SIZE )
  1264. sortMethod ^= SORT_REVERSE;
  1265.       else
  1266. sortMethod = SORT_SIZE;
  1267.     }
  1268.   else if( w == detailDateBtn )
  1269.     {
  1270.       if( sortMethod & SORT_DATE )
  1271. sortMethod ^= SORT_REVERSE;
  1272.       else
  1273. sortMethod = SORT_DATE;
  1274.     }
  1275.   else if( w == detailTypeBtn )
  1276.     {
  1277.       if( sortMethod & SORT_TYPE )
  1278. sortMethod ^= SORT_REVERSE;
  1279.       else
  1280. sortMethod = SORT_TYPE;
  1281.     }
  1282.   bool reverse = ( sortMethod & SORT_REVERSE );
  1283.   detailNameBtn->label( "Name" );
  1284.   detailSizeBtn->label( "Size" );
  1285.   detailDateBtn->label( "Date" );
  1286.   detailTypeBtn->label( "Type" );
  1287.   switch( sortMethod & ~SORT_REVERSE )
  1288.     {
  1289.     case SORT_NAME: detailNameBtn->label( reverse?"@-12DnArrow Name":"@-18UpArrow Name" ); break;
  1290.     case SORT_SIZE: detailSizeBtn->label( reverse?"@-12DnArrow Size":"@-18UpArrow Size" ); break;
  1291.     case SORT_DATE: detailDateBtn->label( reverse?"@-12DnArrow Date":"@-18UpArrow Date" ); break;
  1292.     case SORT_TYPE: detailTypeBtn->label( reverse?"@-12DnArrow Type":"@-18UpArrow Type" ); break;
  1293.     }
  1294.   filelist->sort();
  1295.   filedetails->sort();
  1296. }
  1297. Flu_File_Chooser :: CBTile :: CBTile( int x, int y, int w, int h, Flu_File_Chooser *c )
  1298.    : Fl_Tile( x, y, w, h )
  1299. {
  1300.   chooser = c;
  1301. }
  1302. int Flu_File_Chooser :: CBTile :: handle( int event )
  1303. {
  1304.   if( event == FL_DRAG )
  1305.     {
  1306.       // the user is probably dragging to resize the columns
  1307.       // update the sizes for each entry
  1308.       chooser->updateEntrySizes();
  1309.       chooser->redraw();
  1310.     }
  1311.   return Fl_Tile::handle(event);
  1312. }
  1313. Flu_File_Chooser :: FileColumns :: FileColumns( int x, int y, int w, int h, Flu_File_Chooser *c )
  1314.   : Fl_Tile( x, y, w, h )
  1315. {
  1316.   chooser = c;
  1317.   W1 = int(float(w)*0.35f);
  1318.   W2 = int(float(w)*0.20f);
  1319.   W3 = int(float(w)*0.15f);
  1320.   W4 = w-W1-W2-W3;
  1321.   Fl_Box *box = new Fl_Box( x+50, y, w-200, h );
  1322.   add_resizable( *box );
  1323.   c->detailNameBtn = new Flu_Button( x, y, W1, h, "Name" );
  1324.   c->detailNameBtn->align( FL_ALIGN_CLIP );
  1325.   c->detailNameBtn->callback( Flu_File_Chooser::_sortCB, c );
  1326.   {
  1327.     CBTile *tile = new CBTile( x+W1, y, W2+W3+W4, h, c );
  1328.     Fl_Box *box = new Fl_Box( tile->x()+50, tile->y(), tile->w()-150, tile->h() );
  1329.     tile->add_resizable( *box );
  1330.     c->detailTypeBtn = new Flu_Button( x+W1, y, W2, h, "Type" );
  1331.     c->detailTypeBtn->align( FL_ALIGN_CLIP );
  1332.     c->detailTypeBtn->callback( Flu_File_Chooser::_sortCB, c );
  1333.     {
  1334.       CBTile *tile = new CBTile( x+W1+W2, y, W3+W4, h, c );
  1335.       Fl_Box *box = new Fl_Box( tile->x()+50, tile->y(), tile->w()-100, tile->h() );
  1336.       tile->add_resizable( *box );
  1337.       c->detailSizeBtn = new Flu_Button( x+W1+W2, y, W3, h, "Size" );
  1338.       c->detailSizeBtn->align( FL_ALIGN_CLIP );
  1339.       c->detailSizeBtn->callback( Flu_File_Chooser::_sortCB, c );
  1340.       c->detailDateBtn = new Flu_Button( x+W1+W2+W3, y, W4, h, "Date" );
  1341.       c->detailDateBtn->align( FL_ALIGN_CLIP );
  1342.       c->detailDateBtn->callback( Flu_File_Chooser::_sortCB, c );
  1343.       tile->end();
  1344.     }
  1345.     tile->end();
  1346.   }
  1347.   end();
  1348. }
  1349. Flu_File_Chooser :: FileColumns :: ~FileColumns()
  1350. {
  1351. }
  1352. void Flu_File_Chooser :: FileColumns :: resize( int x, int y, int w, int h )
  1353. {
  1354.   // TODO resize the buttons/tiles according to their stored relative sizes
  1355.   Fl_Tile::resize( x, y, w, h );
  1356. }
  1357. int Flu_File_Chooser :: FileColumns :: handle( int event )
  1358. {
  1359.   if( event == FL_DRAG )
  1360.     {
  1361.       // the user is probably dragging to resize the columns
  1362.       // update the sizes for each entry
  1363.       chooser->updateEntrySizes();
  1364.       chooser->redraw();
  1365.     }
  1366.   return Fl_Tile::handle(event);
  1367. }
  1368. void Flu_File_Chooser :: filenameCB()
  1369. {
  1370.   filenameEnterCallback = true;
  1371.   cd( filename.value() );
  1372. }
  1373. inline bool _isProbablyAPattern( const char *s )
  1374. {
  1375.   return( strpbrk( s, "*;|[]?" ) != NULL );
  1376. }
  1377. void Flu_File_Chooser :: okCB()
  1378. {
  1379.   // only hide if the filename is not blank or the user is choosing directories,
  1380.   // in which case use the current directory
  1381.   //printf( "%sn", filename.value() );
  1382.   if( selectionType & DIRECTORY )
  1383.     {
  1384. #ifdef WIN32
  1385.       if( strcmp( filename.value(), "My Computer" ) == 0 )
  1386. return;
  1387. #endif
  1388.       if( !(selectionType & MULTI ))
  1389. {
  1390.   if( strlen( filename.value() ) != 0 )
  1391.     cd( filename.value() );
  1392.   filename.value( currentDir.c_str() );
  1393.   filename.position( filename.size(), filename.size() );
  1394. }
  1395.       hide();
  1396.     }
  1397.   else
  1398.     {
  1399.       if( strlen( filename.value() ) != 0 )
  1400. {
  1401.   if( _isProbablyAPattern( filename.value() ) )
  1402.     {
  1403.       cd( filename.value() );
  1404.       return;
  1405.     }
  1406. #ifdef WIN32
  1407.   if( filename.value()[1] == ':' )
  1408. #else
  1409.   if( filename.value()[0] == '/' )
  1410. #endif
  1411.     if( fl_filename_isdir( filename.value() ) )
  1412.       {
  1413. filename.value( "" );
  1414. return;
  1415.       }
  1416.   // prepend the path
  1417.   FluSimpleString path = currentDir + filename.value();
  1418.   filename.value( path.c_str() );
  1419.   filename.position( filename.size(), filename.size() );
  1420.   hide();
  1421. }
  1422.     }
  1423. }
  1424. void Flu_File_Chooser :: homeCB()
  1425. {
  1426. #ifdef WIN32
  1427.   cd( "/" );
  1428. #else
  1429.   cd( userHome.c_str() );
  1430. #endif
  1431. }
  1432. void Flu_File_Chooser :: desktopCB()
  1433. {
  1434. #ifdef WIN32
  1435.   FluSimpleString s = "/Desktop";
  1436. #else
  1437.   FluSimpleString s = userHome + "Desktop";
  1438. #endif
  1439.   cd( s.c_str() );
  1440. }
  1441. #define QSCANL( field ) 
  1442.       while( ((Flu_File_Chooser::Entry*)array[left])->field < 
  1443.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) left++
  1444. #define QSCANR( field ) 
  1445.       while( ((Flu_File_Chooser::Entry*)array[right])->field > 
  1446.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) right--
  1447. #define RQSCANL( field ) 
  1448.       while( ((Flu_File_Chooser::Entry*)array[left])->field > 
  1449.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) left++
  1450. #define RQSCANR( field ) 
  1451.       while( ((Flu_File_Chooser::Entry*)array[right])->field < 
  1452.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) right--
  1453. #define CASE_QSCANL( field ) 
  1454.       while( casecompare( ((Flu_File_Chooser::Entry*)array[left])->field, 
  1455.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) < 0 ) left++
  1456. #define CASE_QSCANR( field ) 
  1457.       while( casecompare( ((Flu_File_Chooser::Entry*)array[right])->field, 
  1458.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) > 0 ) right--
  1459. #define CASE_RQSCANL( field ) 
  1460.       while( casecompare( ((Flu_File_Chooser::Entry*)array[left])->field, 
  1461.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) > 0 ) left++
  1462. #define CASE_RQSCANR( field ) 
  1463.       while( casecompare( ((Flu_File_Chooser::Entry*)array[right])->field, 
  1464.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) < 0 ) right--
  1465. #define CUSTOM_QSCANL( field ) 
  1466.       while( customSort( ((Flu_File_Chooser::Entry*)array[left])->field, 
  1467.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) < 0 ) left++
  1468. #define CUSTOM_QSCANR( field ) 
  1469.       while( customSort( ((Flu_File_Chooser::Entry*)array[right])->field, 
  1470.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) > 0 ) right--
  1471. #define CUSTOM_RQSCANL( field ) 
  1472.       while( customSort( ((Flu_File_Chooser::Entry*)array[left])->field, 
  1473.              ((Flu_File_Chooser::Entry*)array[pivot])->field ) > 0 ) left++
  1474. #define CUSTOM_RQSCANR( field ) 
  1475.       while( customSort( ((Flu_File_Chooser::Entry*)array[right])->field, 
  1476.      ((Flu_File_Chooser::Entry*)array[pivot])->field ) < 0 ) right--
  1477. void Flu_File_Chooser :: _qSort( int how, bool caseSort, Fl_Widget **array, int low, int high )
  1478. {
  1479.   int left, right, pivot;
  1480.   Fl_Widget *temp;
  1481.   bool reverse = ( how & SORT_REVERSE );
  1482.   if( high > low )
  1483.     {
  1484.       left = low;
  1485.       right = high;
  1486.       pivot = low;
  1487.       while( right >= left )
  1488. {
  1489.   switch( how & ~SORT_REVERSE )
  1490.     {
  1491.     case SORT_NAME:
  1492.       if( reverse )
  1493. {
  1494.   if( customSort )
  1495.     {
  1496.       CUSTOM_RQSCANL( filename.c_str() );
  1497.       CUSTOM_RQSCANR( filename.c_str() );
  1498.     }
  1499.   else if( !caseSort )
  1500.     {
  1501.       CASE_RQSCANL( filename );
  1502.       CASE_RQSCANR( filename );
  1503.     }
  1504.   else
  1505.     {
  1506.       RQSCANL( filename );
  1507.       RQSCANR( filename );
  1508.     }
  1509. }
  1510.       else
  1511. {
  1512.   if( customSort )
  1513.     {
  1514.       CUSTOM_QSCANL( filename.c_str() );
  1515.       CUSTOM_QSCANR( filename.c_str() );
  1516.     }
  1517.   else if( !caseSort )
  1518.     {
  1519.       CASE_QSCANL( filename );
  1520.       CASE_QSCANR( filename );
  1521.     }
  1522.   else
  1523.     {
  1524.       QSCANL( filename );
  1525.       QSCANR( filename );
  1526.     }
  1527. }
  1528.       break;
  1529.     case SORT_SIZE:
  1530.       if( reverse )
  1531. {
  1532.   RQSCANL( isize );
  1533.   RQSCANR( isize );
  1534. }
  1535.       else
  1536. {
  1537.   QSCANL( isize );
  1538.   QSCANR( isize );
  1539. }
  1540.       break;
  1541.     case SORT_DATE:
  1542.       if( reverse )
  1543. {
  1544.   RQSCANL( idate );
  1545.   RQSCANR( idate );
  1546. }
  1547.       else
  1548. {
  1549.   QSCANL( idate );
  1550.   QSCANR( idate );
  1551. }
  1552.       break;
  1553.     case SORT_TYPE:
  1554.       if( reverse )
  1555. {
  1556.   RQSCANL( description );
  1557.   RQSCANR( description );
  1558. }
  1559.       else
  1560. {
  1561.   QSCANL( description );
  1562.   QSCANR( description );
  1563. }
  1564.       break;
  1565.     }
  1566.   if( left > right )
  1567.     break;
  1568.   temp = array[left];
  1569.   array[left] = array[right];
  1570.   array[right] = temp;
  1571.   left++;
  1572.   right--;
  1573. }
  1574.       _qSort( how, caseSort, array, low, right );
  1575.       _qSort( how, caseSort, array, left, high );
  1576.     }
  1577. }
  1578. Flu_File_Chooser :: FileList :: FileList( int x, int y, int w, int h, Flu_File_Chooser *c )
  1579.   : Flu_Wrap_Group( x, y, w, h )
  1580. {
  1581.   chooser = c;
  1582. }
  1583. Flu_File_Chooser :: FileList :: ~FileList()
  1584. {
  1585. }
  1586. void Flu_File_Chooser :: FileList :: sort( int n )
  1587. {
  1588.   if( n != -1 )
  1589.     numDirs = n;
  1590.   if( children() == 0 )
  1591.     return;
  1592.   // the directories are already first. sort the directories then the names lexigraphically
  1593.   //if( chooser->sortMethod & Flu_File_Chooser::SORT_NAME )
  1594.   Flu_File_Chooser::_qSort( chooser->sortMethod, chooser->caseSort, (Fl_Widget**)array(), 0, numDirs-1 );
  1595.   Flu_File_Chooser::_qSort( chooser->sortMethod, chooser->caseSort, (Fl_Widget**)array(), numDirs, children()-1 );
  1596.   chooser->redraw();
  1597. }
  1598. int Flu_File_Chooser :: FileList :: handle( int event )
  1599. {
  1600.   //printf( "%dn", event );
  1601.   if( event == FL_FOCUS || event == FL_UNFOCUS )
  1602.     return 1;
  1603.   if( Flu_Wrap_Group::handle( event ) )
  1604.     return 1;
  1605.   // if push on no file, unselect all files and turn off editing mode
  1606.   if( event == FL_PUSH && !Fl::event_key( FL_SHIFT ) && !Fl::event_key( FL_CTRL ) )
  1607.     {
  1608.       chooser->unselect_all();
  1609.       chooser->filename.value( "" );
  1610.       chooser->filename.position( chooser->filename.size(), chooser->filename.size() );
  1611.       if( Fl::event_button3() )
  1612. return chooser->popupContextMenu( NULL );
  1613.       return 1;
  1614.     }
  1615.   else if( event == FL_KEYDOWN )
  1616.     {
  1617.       if( Fl::event_key( FL_Delete ) )
  1618. {
  1619.   // recycle by default, unless the shift key is held down
  1620.   chooser->trashCB( !Fl::event_state( FL_SHIFT ) );
  1621.   return 1;
  1622. }
  1623.       Flu_File_Chooser::Entry *e = chooser->lastSelected;
  1624.       if( !e )
  1625. {
  1626.   for( int i = 0; i < children(); i++ )
  1627.     if( ((Flu_File_Chooser::Entry*)child(i))->selected )
  1628.       {
  1629. e = (Flu_File_Chooser::Entry*)child(i);
  1630. break;
  1631.       }
  1632. }
  1633.       if( e )
  1634. {
  1635.   switch( Fl::event_key() )
  1636.     {
  1637.     case FL_Up: e = (Flu_File_Chooser::Entry*)previous( e );
  1638.       if( !e && children() ) e = (Flu_File_Chooser::Entry*)child(0); break;
  1639.     case FL_Down: e = (Flu_File_Chooser::Entry*)next( e );
  1640.       if( !e && children() ) e = (Flu_File_Chooser::Entry*)child(children()-1); break;
  1641.     case FL_Left: e = (Flu_File_Chooser::Entry*)left( e ); break;
  1642.     case FL_Right: e = (Flu_File_Chooser::Entry*)right( e ); break;
  1643.     case FL_Home: if( children() ) e = (Flu_File_Chooser::Entry*)child(0); break;
  1644.     case FL_End: if( children() ) e = (Flu_File_Chooser::Entry*)child(children()-1); break;
  1645.     case FL_Enter:
  1646.       chooser->filenameEnterCallback = true;
  1647.       chooser->cd( e->filename.c_str() );
  1648.       return 1;
  1649.     case ' ':
  1650.       chooser->cd( e->filename.c_str() );
  1651.       return 1;
  1652.     default: e = 0; break;
  1653.     }
  1654.   if( e )
  1655.     {
  1656.       chooser->unselect_all();
  1657.       e->selected = true;
  1658.       chooser->lastSelected = e;
  1659.       chooser->filename.value( e->filename.c_str() );
  1660.       chooser->filename.position( chooser->filename.size(), chooser->filename.size() );
  1661.       chooser->redraw();
  1662.       scroll_to( e );
  1663.       //take_focus();
  1664.       return 1;
  1665.     }
  1666. }
  1667.     }
  1668.   return 0;
  1669. }
  1670. Flu_File_Chooser :: FileDetails :: FileDetails( int x, int y, int w, int h, Flu_File_Chooser *c )
  1671.   : Fl_Pack( x, y, w, h )
  1672. {
  1673.   chooser = c;
  1674. }
  1675. Flu_File_Chooser :: FileDetails :: ~FileDetails()
  1676. {
  1677. }
  1678. void Flu_File_Chooser :: FileDetails :: scroll_to( Fl_Widget *w )
  1679. {
  1680.   // we know all the widgets are the same height
  1681.   // so just find this widget and scroll to the accumulated height
  1682.   int H = 0;
  1683.   for( int i = 0; i < children(); i++ )
  1684.     {
  1685.       if( child(i) == w )
  1686. {
  1687.   if( H > (int)chooser->filescroll->scrollbar.maximum() )
  1688.     H = (int)chooser->filescroll->scrollbar.maximum();
  1689.   chooser->filescroll->position( 0, H );
  1690.   return;
  1691. }
  1692.       H += w->h();
  1693.     }
  1694. }
  1695. void Flu_File_Chooser :: FileDetails :: sort( int n )
  1696. {
  1697.   if( n != -1 )
  1698.     numDirs = n;
  1699.   if( children() == 0 )
  1700.     return;
  1701.   // the directories are already first. sort the directories then the names lexigraphically
  1702.   //if( chooser->sortMethod & Flu_File_Chooser::SORT_NAME )
  1703.   Flu_File_Chooser::_qSort( chooser->sortMethod, chooser->caseSort, (Fl_Widget**)array(), 0, numDirs-1 );
  1704.   Flu_File_Chooser::_qSort( chooser->sortMethod, chooser->caseSort, (Fl_Widget**)array(), numDirs, children()-1 );
  1705.   chooser->redraw();
  1706. }
  1707. Fl_Widget* Flu_File_Chooser :: FileDetails :: next( Fl_Widget* w )
  1708. {
  1709.   for( int i = 0; i < children()-1; i++ )
  1710.     {
  1711.       if( w == child(i) )
  1712. return child(i+1);
  1713.     }
  1714.   return NULL;
  1715. }
  1716. Fl_Widget* Flu_File_Chooser :: FileDetails :: previous( Fl_Widget* w )
  1717. {
  1718.   for( int i = 1; i < children(); i++ )
  1719.     {
  1720.       if( w == child(i) )
  1721. return child(i-1);
  1722.     }
  1723.   return NULL;
  1724. }
  1725. int Flu_File_Chooser :: FileDetails :: handle( int event )
  1726. {
  1727.   if( event == FL_FOCUS || event == FL_UNFOCUS )
  1728.     return 1;
  1729.   if( Fl_Pack::handle( event ) )
  1730.     return 1;
  1731.   else if( event == FL_PUSH )
  1732.     return 1;
  1733.   else if( event == FL_KEYDOWN )
  1734.     {
  1735.       if( Fl::event_key( FL_Delete ) )
  1736. {
  1737.   // recycle by default, unless the shift key is held down
  1738.   chooser->trashCB( !Fl::event_state( FL_SHIFT ) );
  1739.   return 1;
  1740. }
  1741.       Flu_File_Chooser::Entry *e = chooser->lastSelected;
  1742.       if( !e )
  1743. {
  1744.   for( int i = 0; i < children(); i++ )
  1745.     if( ((Flu_File_Chooser::Entry*)child(i))->selected )
  1746.       {
  1747. e = (Flu_File_Chooser::Entry*)child(i);
  1748. break;
  1749.       }
  1750. }
  1751.       if( e )
  1752. {
  1753.   switch( Fl::event_key() )
  1754.     {
  1755.     case FL_Up: e = (Flu_File_Chooser::Entry*)previous( e );
  1756.       if( !e && children() ) e = (Flu_File_Chooser::Entry*)child(0); break;
  1757.     case FL_Down: e = (Flu_File_Chooser::Entry*)next( e );
  1758.       if( !e && children() ) e = (Flu_File_Chooser::Entry*)child(children()-1); break;
  1759.     case FL_Home: if( children() ) e = (Flu_File_Chooser::Entry*)child(0); break;
  1760.     case FL_End: if( children() ) e = (Flu_File_Chooser::Entry*)child(children()-1); break;
  1761.     case FL_Enter:
  1762.       chooser->filenameEnterCallback = true;
  1763.       chooser->cd( e->filename.c_str() );
  1764.       return 1;
  1765.     case ' ':
  1766.       chooser->cd( e->filename.c_str() );
  1767.       return 1;
  1768.     default: e = 0; break;
  1769.     }
  1770.   if( e )
  1771.     {
  1772.       chooser->unselect_all();
  1773.       e->selected = true;
  1774.       chooser->lastSelected = e;
  1775.       chooser->filename.value( e->filename.c_str() );
  1776.       chooser->filename.position( chooser->filename.size(), chooser->filename.size() );
  1777.       chooser->redraw();
  1778.       scroll_to( e );
  1779.       //take_focus();
  1780.       return 1;
  1781.     }
  1782. }
  1783.     }
  1784.   return 0;
  1785. }
  1786. Flu_File_Chooser :: Entry :: Entry( const char* name, int t, bool d, Flu_File_Chooser *c )
  1787.   : Fl_Input( 0, 0, 0, 0 )
  1788. {
  1789.   resize( 0, 0, DEFAULT_ENTRY_WIDTH, 20 );
  1790.   labelsize( 12 );
  1791.   textsize( 12 );
  1792.   box( FL_BORDER_BOX );
  1793.   when( FL_WHEN_RELEASE_ALWAYS | FL_WHEN_ENTER_KEY_ALWAYS );
  1794.   callback( _inputCB, this );
  1795.   filename = name;
  1796.   selected = false;
  1797.   chooser = c;
  1798.   details = d;
  1799.   type = t;
  1800.   icon = NULL;
  1801.   editMode = 0;
  1802.   description = "";
  1803.   updateSize();
  1804.   updateIcon();
  1805. }
  1806. void Flu_File_Chooser :: Entry :: updateIcon()
  1807. {
  1808.   Flu_File_Chooser::FileTypeInfo *tt = NULL;
  1809.   if( type==ENTRY_MYCOMPUTER )
  1810.     {
  1811.       icon = &computer;
  1812.       description = "My Computer";
  1813.     }
  1814.   else if( type==ENTRY_MYDOCUMENTS )
  1815.     {
  1816.       icon = &documents;
  1817.       description = "My Documents";
  1818.     }
  1819.   else if( type==ENTRY_DRIVE )
  1820.     {
  1821.       //icon = &disk_drive;
  1822.       //description = "";
  1823.     }
  1824.   else if( type==ENTRY_DIR || type==ENTRY_FAVORITE )
  1825.     tt = Flu_File_Chooser::find_type( NULL );
  1826.   else
  1827.     {
  1828.       const char *dot = strrchr( filename.c_str(), '.' );
  1829.       if( dot )
  1830. {
  1831.   tt = Flu_File_Chooser::find_type( dot+1 );
  1832.   if( !tt )
  1833.     description = dot+1;
  1834. }
  1835.     }
  1836.   if( tt )
  1837.     {
  1838.       icon = tt->icon;
  1839.       description = tt->type;
  1840.     }
  1841.   // if there is no icon, assign a default one
  1842.   if( !icon && type==ENTRY_FILE )
  1843.     icon = chooser->defaultFileIcon;
  1844.   if( type==ENTRY_FAVORITE )
  1845.     icon = &little_favorites;
  1846.   toolTip = "Name: " + filename;
  1847.   if( type == ENTRY_FILE )
  1848.     toolTip += "nSize: " + filesize;
  1849.   toolTip += "nType: " + description;
  1850.   tooltip( toolTip.c_str() );
  1851.   redraw();
  1852. }
  1853. void Flu_File_Chooser :: resize( int x, int y, int w, int h )
  1854. {
  1855.   Fl_Double_Window::resize( x, y, w, h );
  1856.   if( fileListWideBtn->value() )
  1857.     filelist->scrollbar.linesize( filelist->w() );
  1858.   else if( fileListBtn->value() )
  1859.     filelist->scrollbar.linesize( DEFAULT_ENTRY_WIDTH+4 );
  1860.   for( int i = 0; i < filelist->children(); i++ )
  1861.     ((Entry*)filelist->child(i))->updateSize();
  1862. }
  1863. void Flu_File_Chooser :: Entry :: updateSize()
  1864. {
  1865.   if( type==ENTRY_FAVORITE || chooser->fileListWideBtn->value() )
  1866.     {
  1867.       resize( x(), y(), chooser->filelist->w()-4, 20 );
  1868.     }
  1869.   if( details )
  1870.     {
  1871.       nameW = chooser->detailNameBtn->w();
  1872.       typeW = chooser->detailTypeBtn->w();
  1873.       sizeW = chooser->detailSizeBtn->w();
  1874.       dateW = chooser->detailDateBtn->w();
  1875.       resize( x(), y(), chooser->filedetails->w(), 20 );
  1876.     }
  1877.   else
  1878.     nameW = w();
  1879.   // how big is the icon?
  1880.   int iW = 0, iH = 0;
  1881.   if( icon )
  1882.     {
  1883.       iW = icon->w()+2;
  1884.       iH = icon->h();
  1885.     }
  1886.   fl_font( labelfont(), labelsize() );
  1887.   // measure the name and see if we need a truncated version
  1888.   int W = 0, H = 0;
  1889.   fl_measure( filename.c_str(), W, H );  
  1890.   if( W > nameW-iW )
  1891.     {
  1892.       // progressively strip characters off the end of the name until
  1893.       // it fits with "..." at the end
  1894.       if( altname[0] != '' )
  1895. shortname = altname;
  1896.       else
  1897. shortname = filename;
  1898.       int len = shortname.size();
  1899.       while( W > (nameW-iW) && len > 3 )
  1900. {
  1901.   shortname[len-3] = '.';
  1902.   shortname[len-2] = '.';
  1903.   shortname[len-1] = '.';
  1904.   shortname[len] = '';
  1905.   len--;
  1906.   W = 0;
  1907.   fl_measure( shortname.c_str(), W, H );  
  1908. }
  1909.     }
  1910.   else
  1911.     shortname = "";
  1912.   // measure the description and see if we need a truncated version
  1913.   shortDescription = "";
  1914.   if( details )
  1915.     {
  1916.       W = 0; H = 0;
  1917.       fl_measure( description.c_str(), W, H );  
  1918.       if( W > typeW-4 )
  1919. {
  1920.   // progressively strip characters off the end of the description until
  1921.   // it fits with "..." at the end
  1922.   shortDescription = description;
  1923.   int len = shortDescription.size();
  1924.   while( W > typeW-4 && len > 3 )
  1925.     {
  1926.       shortDescription[len-3] = '.';
  1927.       shortDescription[len-2] = '.';
  1928.       shortDescription[len-1] = '.';
  1929.       shortDescription[len] = '';
  1930.       len--;
  1931.       W = 0;
  1932.       fl_measure( shortDescription.c_str(), W, H );  
  1933.     }
  1934. }
  1935.     }
  1936.   redraw();
  1937. }
  1938. Flu_File_Chooser :: Entry :: ~Entry()
  1939. {
  1940. }
  1941. void Flu_File_Chooser :: Entry :: inputCB()
  1942. {
  1943.   redraw();
  1944.   // if the user tried to change the string to nothing, restore the original name and turn off edit mode
  1945.   if( strlen( value() ) == 0 )
  1946.     {
  1947.       editMode = 0;
  1948.       return;
  1949.     }
  1950.   // if input text is different from filename, try to change the filename
  1951.   if( strcmp( value(), filename.c_str() ) != 0 )
  1952.     {
  1953.       // build the total old filename and new filename
  1954.       FluSimpleString oldName = chooser->currentDir + filename,
  1955. newName = chooser->currentDir + value();
  1956.       // see if new name already exists
  1957.       struct stat s;
  1958.       int result = ::stat( newName.c_str(), &s );
  1959.       if( result == 0 )
  1960. {
  1961.   fl_alert( "File '%s' already exists!", newName.c_str() );
  1962.   return;  // leave editing on
  1963. }
  1964.       if( rename( oldName.c_str(), newName.c_str() ) == -1 )
  1965. {
  1966.   fl_alert( "Unable to rename '%s' to '%s'", oldName.c_str(), newName.c_str() );
  1967.   //return;  // leave editing on
  1968. }
  1969.       else
  1970. {
  1971.   filename = value();
  1972.   updateSize();
  1973.   updateIcon();
  1974. }
  1975.       // QUESTION: should we set the chooser filename to the modified name?
  1976.       //chooser->filename.value( filename.c_str() );
  1977.     }
  1978.   // only turn off editing if we have a successful name change
  1979.   editMode = 0;
  1980. }
  1981. Fl_Group* Flu_File_Chooser :: getEntryGroup()
  1982. {
  1983.   return (!fileDetailsBtn->value() || currentDir == FAVORITES_UNIQUE_STRING ) ? &(filelist->group) : filedetails;
  1984. }
  1985. Fl_Group* Flu_File_Chooser :: getEntryContainer()
  1986. {
  1987.   return (!fileDetailsBtn->value() || currentDir == FAVORITES_UNIQUE_STRING ) ? (Fl_Group*)filelist : filedetails;
  1988. }
  1989. int Flu_File_Chooser :: Entry :: handle( int event )
  1990. {
  1991.   if( editMode )
  1992.     {
  1993.       // if user hits 'Escape' while in edit mode, restore the original name and turn off edit mode
  1994.       if( event == FL_KEYDOWN && Fl::event_key( FL_Escape ) )
  1995. {
  1996.   editMode = 0;
  1997.   redraw();
  1998.   if( selected )
  1999.     chooser->trashBtn->activate();
  2000.   return 1;
  2001. }
  2002.       return Fl_Input::handle( event );
  2003.     }
  2004.   if( event == FL_FOCUS || event == FL_UNFOCUS )
  2005.     return 1;
  2006.   if( event == FL_ENTER || event == FL_LEAVE )
  2007.     return 1;
  2008.   Fl_Group *g = chooser->getEntryGroup();
  2009.   if( event == FL_PUSH )
  2010.     {
  2011.       if( Fl::event_clicks() > 0 )
  2012. {
  2013.   Fl::event_clicks(0);
  2014.   // double-clicking a favorite cd's to it
  2015.   if( type == ENTRY_FAVORITE )
  2016.     {
  2017.       chooser->delayedCd = filename;
  2018.       Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, chooser );
  2019.     }
  2020.   // double-clicking a directory cd's to it
  2021.   else if( type != ENTRY_FILE )
  2022.     {
  2023. #ifdef WIN32
  2024.       if( filename[1] == ':' )
  2025. chooser->delayedCd = filename;
  2026.       else
  2027. #endif
  2028. chooser->delayedCd = chooser->currentDir + filename + "/";
  2029.       Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, chooser );
  2030.     }
  2031.   // double-clicking a file chooses it if we are in file selection mode
  2032.   else if( (chooser->selectionType & DIRECTORY) == 0 )
  2033.     {
  2034.       // prepend the path
  2035.       FluSimpleString path = chooser->currentDir + filename;
  2036.       chooser->filename.value( path.c_str() );
  2037.       //chooser->filename.value( filename.c_str() );
  2038.       chooser->filename.position( chooser->filename.size(), chooser->filename.size() );
  2039.       Fl::add_timeout( 0.0f, Flu_File_Chooser::selectCB, chooser );
  2040.     }
  2041.   if( selected )
  2042.     chooser->trashBtn->activate();
  2043.   return 1;
  2044. }
  2045.       /*
  2046.       if( selected && !Fl::event_button3() && !Fl::event_state(FL_CTRL) && !Fl::event_state(FL_SHIFT) )
  2047. {
  2048.   // only allow editing of certain files and directories
  2049.   if( chooser->fileEditing && ( type == ENTRY_FILE || type == ENTRY_DIR ) )
  2050.     {
  2051.       // if already selected, switch to input mode
  2052.       Fl::add_timeout( 1.0, _editCB, this );
  2053.       return 1;
  2054.     }
  2055. }
  2056. else*/ if( chooser->selectionType & MULTI )
  2057. {
  2058.   if( Fl::event_state(FL_CTRL) )
  2059.     {
  2060.       selected = !selected;  // toggle this item
  2061.       chooser->lastSelected = this;
  2062.       if( type == ENTRY_FILE )
  2063. chooser->previewGroup->file = chooser->currentDir + filename;
  2064.       chooser->redraw();
  2065.       chooser->getEntryContainer()->take_focus();
  2066.     }
  2067.   else if( Fl::event_state(FL_SHIFT) )
  2068.     {
  2069.       // toggle all items from the last selected item to this one
  2070.       if( chooser->lastSelected == NULL )
  2071. {
  2072.   selected = true;
  2073.   chooser->lastSelected = this;
  2074.   if( type == ENTRY_FILE )
  2075.     chooser->previewGroup->file = chooser->currentDir + filename;
  2076.   chooser->redraw();
  2077.   chooser->getEntryContainer()->take_focus();
  2078. }
  2079.       else
  2080. {
  2081.   // get the index of the last selected item and this item
  2082.   int lastindex = -1, thisindex = -1;
  2083.   int i;
  2084.   for( i = 0; i < g->children(); i++ )
  2085.     {
  2086.       if( g->child(i) == chooser->lastSelected )
  2087. lastindex = i;
  2088.       if( g->child(i) == this )
  2089. thisindex = i;
  2090.       if( lastindex >= 0 && thisindex >= 0 )
  2091. break;
  2092.     }
  2093.   if( lastindex >= 0 && thisindex >= 0 )
  2094.     {
  2095.       // loop from this item to the last item, toggling each item except the last
  2096.       int inc;
  2097.       if( thisindex > lastindex )
  2098. inc = -1;
  2099.       else
  2100. inc = 1;
  2101.       Entry *e;
  2102.       for( i = thisindex; i != lastindex; i += inc )
  2103. {
  2104.   e = (Entry*)g->child(i);
  2105.   e->selected = !e->selected;
  2106.   e->redraw();
  2107. }
  2108.       chooser->lastSelected = this;
  2109.       if( type == ENTRY_FILE )
  2110. chooser->previewGroup->file = chooser->currentDir + filename;
  2111.       chooser->redraw();
  2112.       chooser->getEntryContainer()->take_focus();
  2113.     }
  2114. }
  2115.     }
  2116.   else
  2117.     {
  2118.       chooser->unselect_all();
  2119.       selected = true;
  2120.       chooser->lastSelected = this;
  2121.       if( type == ENTRY_FILE )
  2122. chooser->previewGroup->file = chooser->currentDir + filename;
  2123.       chooser->redraw();
  2124.       chooser->getEntryContainer()->take_focus();
  2125.     }
  2126. }
  2127.       else
  2128. {
  2129.   chooser->unselect_all();
  2130.   selected = true;
  2131.   chooser->lastSelected = this;
  2132.   if( type == ENTRY_FILE )
  2133.     chooser->previewGroup->file = chooser->currentDir + filename;
  2134.   chooser->redraw();
  2135.   chooser->getEntryContainer()->take_focus();
  2136. }
  2137.       //g->take_focus();
  2138.       redraw();
  2139.       if( selected )
  2140. chooser->trashBtn->activate();
  2141.       if( Fl::event_button3() )
  2142. return chooser->popupContextMenu( this );
  2143.       // don't put the filename into the box if we are directory but we are not choosing directories
  2144.       if( (chooser->selectionType & Flu_File_Chooser::DIRECTORY)  ||  type==ENTRY_FILE )
  2145. chooser->filename.value( filename.c_str() );
  2146.       else
  2147. chooser->filename.value( "" );
  2148.       chooser->filename.position( chooser->filename.size(), chooser->filename.size() );
  2149.       return 1;
  2150.     }
  2151.   else if( event == FL_DRAG )
  2152.     {
  2153.       if( chooser->selectionType & MULTI )
  2154. {
  2155.   // toggle all items from the last selected item to this one
  2156.   if( chooser->lastSelected != NULL )
  2157.     {
  2158.       selected = true;
  2159.       // get the index of the last selected item and this item
  2160.       int lastindex = -1, thisindex = -1;
  2161.       int i;
  2162.       for( i = 0; i < g->children(); i++ )
  2163. {
  2164.   if( g->child(i) == chooser->lastSelected )
  2165.     lastindex = i;
  2166.   if( g->child(i) == this )
  2167.     thisindex = i;
  2168.   if( lastindex >= 0 && thisindex >= 0 )
  2169.     break;
  2170. }
  2171.       if( lastindex >= 0 && thisindex >= 0 )
  2172. {
  2173.   // loop from this item to the last item, toggling each item except the last
  2174.   int inc;
  2175.   if( thisindex > lastindex )
  2176.     inc = -1;
  2177.   else
  2178.     inc = 1;
  2179.   Entry *e;
  2180.   for( i = thisindex; i != lastindex; i += inc )
  2181.     {
  2182.       e = (Entry*)g->child(i);
  2183.       e->selected = !e->selected;
  2184.       e->redraw();
  2185.     }
  2186.   chooser->lastSelected = this;
  2187.   if( type == ENTRY_FILE )
  2188.     chooser->previewGroup->file = chooser->currentDir + filename;
  2189.   chooser->redraw();
  2190. }
  2191.       redraw();
  2192.       chooser->getEntryContainer()->take_focus();
  2193.       if( selected )
  2194. chooser->trashBtn->activate();
  2195.       return 1;
  2196.     }
  2197. }
  2198.     }
  2199.   return Fl_Widget::handle(event);
  2200. }
  2201. void Flu_File_Chooser :: Entry :: editCB()
  2202. {
  2203.   // if already selected, switch to input mode
  2204.   editMode = 2;
  2205.   value( filename.c_str() );
  2206.   take_focus();
  2207.   // select the text up to but not including the extension
  2208.   const char *dot = strrchr( filename.c_str(), '.' );
  2209.   if( dot )
  2210.     position( 0, dot-filename.c_str() );
  2211.   else
  2212.     position( 0, filename.size() );
  2213.   chooser->trashBtn->deactivate();
  2214.   redraw();
  2215. }
  2216. int Flu_File_Chooser :: popupContextMenu( Entry *entry )
  2217. {
  2218.   int type = entry ? entry->type : ENTRY_NONE;
  2219.   const char *filename = entry ? entry->filename.c_str() : NULL;
  2220.   char *ext = NULL;
  2221.   if( filename )
  2222.     ext = (char*)strrchr( filename, '.' );
  2223.   if( ext )
  2224.     {
  2225.       ext = strdup( ext+1 ); // skip the '.'
  2226.       for( unsigned int i = 0; i < strlen(ext); i++ )
  2227. ext[i] = tolower( ext[i] );
  2228.     }
  2229.   enum { ACTION_NEW_FOLDER = -1, ACTION_RENAME = -2, ACTION_DELETE = -3 };
  2230.   entryPopup.clear();
  2231.   switch( type )
  2232.     {
  2233.     case ENTRY_NONE: // right click on nothing
  2234.       entryPopup.add( "New Folder", 0, 0, (void*)ACTION_NEW_FOLDER );
  2235.       break;
  2236.     case ENTRY_DIR:
  2237.       entryPopup.add( "Rename", 0, 0, (void*)ACTION_RENAME );
  2238.       entryPopup.add( "Delete", 0, 0, (void*)ACTION_DELETE );
  2239.       break;
  2240.     case ENTRY_FILE:
  2241.       entryPopup.add( "Rename", 0, 0, (void*)ACTION_RENAME );
  2242.       entryPopup.add( "Delete", 0, 0, (void*)ACTION_DELETE );
  2243.       break;
  2244.    case ENTRY_FAVORITE:
  2245.      entryPopup.add( "Delete", 0, 0, (void*)ACTION_DELETE );
  2246.       break;
  2247.     case ENTRY_DRIVE:
  2248.       break;
  2249.     case ENTRY_MYDOCUMENTS:
  2250.       break;
  2251.     case ENTRY_MYCOMPUTER:
  2252.       break;
  2253.     }
  2254.   // add the programmable context handlers
  2255.   for( unsigned int i = 0; i < contextHandlers.size(); i++ )
  2256.     {
  2257.       if( !(contextHandlers[i].type & type) )
  2258. continue;
  2259.       if( type == ENTRY_FILE )
  2260. if( contextHandlers[i].ext.size() && contextHandlers[i].ext != ext )
  2261.   continue;
  2262.       entryPopup.add( contextHandlers[i].name.c_str(), 0, 0, (void*)i );
  2263.     }
  2264.   if( ext )
  2265.     free( ext );
  2266.   entryPopup.position( Fl::event_x(), Fl::event_y() );
  2267.   const Fl_Menu_Item *selection = entryPopup.popup();
  2268.   if( selection )
  2269.     {
  2270.       int handler = static_cast<int>(reinterpret_cast<long>(selection->user_data()));
  2271.       switch( handler )
  2272. {
  2273. case ACTION_NEW_FOLDER:
  2274.   newFolderCB();
  2275.   break;
  2276. case ACTION_RENAME:
  2277.   entry->editCB();
  2278.   /*
  2279.   entry->editMode = 2;
  2280.   entry->value( entry->filename.c_str() );
  2281.   entry->take_focus();
  2282.   entry->position( 0, entry->filename.size() );
  2283.   trashBtn->deactivate();
  2284.   */
  2285.   break;
  2286. case ACTION_DELETE:
  2287.   // recycle by default, unless the shift key is held down
  2288.   trashCB( !Fl::event_state( FL_SHIFT ) );
  2289.   break;
  2290. default:
  2291.   contextHandlers[handler].callback( filename, type, contextHandlers[handler].callbackData );
  2292.   break;
  2293. }
  2294.     }
  2295.   else
  2296.     return handle( FL_PUSH );
  2297.   return 1;
  2298. }
  2299. void Flu_File_Chooser :: Entry :: draw()
  2300. {
  2301.   if( editMode )
  2302.     {
  2303.       if( editMode == 2 )
  2304. {
  2305.   editMode--;
  2306.   fl_draw_box( FL_FLAT_BOX, x(), y(), w(), h(), FL_WHITE );
  2307.   redraw();
  2308. }
  2309.       Fl_Input::draw();
  2310.       return;
  2311.     }
  2312.   if( selected )
  2313.     {
  2314.       fl_draw_box( FL_FLAT_BOX, x(), y(), w(), h(), FL_SELECTION_COLOR );
  2315.       fl_color( FL_WHITE );
  2316.     }
  2317.   else
  2318.     {
  2319.       fl_draw_box( FL_FLAT_BOX, x(), y(), w(), h(), FL_WHITE );
  2320.       fl_color( FL_BLACK );
  2321.     }
  2322.   int X = x()+4;
  2323.   if( icon )
  2324.     {
  2325.       icon->draw( X, y()+h()/2-icon->h()/2 );
  2326.       X += icon->w()+2;
  2327.     }
  2328.   fl_font( labelfont(), labelsize() );
  2329.   if( shortname[0] != '' )
  2330.     fl_draw( shortname.c_str(), X, y(), nameW, h(), FL_ALIGN_LEFT );
  2331.   else if( altname[0] != '' )
  2332.     fl_draw( altname.c_str(), X, y(), nameW, h(), FL_ALIGN_LEFT );
  2333.   else
  2334.     fl_draw( filename.c_str(), X, y(), nameW, h(), FL_ALIGN_LEFT );
  2335.   X = x()+4 + nameW;
  2336.   if( details )
  2337.     {
  2338.       if( shortDescription[0] != '' )
  2339. fl_draw( shortDescription.c_str(), X, y(), typeW-4, h(), Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP) );
  2340.       else
  2341. fl_draw( description.c_str(), X, y(), typeW-4, h(), Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP) );
  2342.       X += typeW;
  2343.       fl_draw( filesize.c_str(), X, y(), sizeW-4, h(), Fl_Align(FL_ALIGN_RIGHT | FL_ALIGN_CLIP) );
  2344.       X += sizeW+4;
  2345.       fl_draw( date.c_str(), X, y(), dateW-4, h(), Fl_Align(FL_ALIGN_LEFT | FL_ALIGN_CLIP) );
  2346.     }
  2347. }
  2348. void Flu_File_Chooser :: unselect_all()
  2349. {
  2350.   Fl_Group *g = getEntryGroup();
  2351.   Entry *e;
  2352.   for( int i = 0; i < g->children(); i++ )
  2353.     {
  2354.       e = ((Entry*)g->child(i));
  2355.       e->selected = false;
  2356.       e->editMode = 0;
  2357.     }
  2358.   lastSelected = 0;
  2359.   previewGroup->file = "";
  2360.   previewGroup->redraw();
  2361.   trashBtn->deactivate();
  2362.   redraw();
  2363. }
  2364. void Flu_File_Chooser :: select_all()
  2365. {
  2366.   if( !( selectionType & MULTI ) )
  2367.     return;
  2368.   Fl_Group *g = getEntryGroup();
  2369.   Entry *e;
  2370.   previewGroup->file = "";
  2371.   for( int i = 0; i < g->children(); i++ )
  2372.     {
  2373.       e = ((Entry*)g->child(i));
  2374.       e->selected = true;
  2375.       e->editMode = 0;
  2376.       previewGroup->file = e->filename;
  2377.       filename.value( e->filename.c_str() );
  2378.     }
  2379.   lastSelected = 0;
  2380.   previewGroup->redraw();
  2381.   trashBtn->deactivate();
  2382.   redraw();
  2383. }
  2384. void Flu_File_Chooser :: updateEntrySizes()
  2385. {
  2386.   filecolumns->W1 = detailNameBtn->w();
  2387.   filecolumns->W2 = detailTypeBtn->w();
  2388.   filecolumns->W3 = detailSizeBtn->w();
  2389.   filecolumns->W4 = detailDateBtn->w();
  2390.   // update the size of each entry because the user changed the size of each column
  2391.   filedetails->resize( filedetails->x(), filedetails->y(), filescroll->w(), filedetails->h() );
  2392.   for( int i = 0; i < filedetails->children(); i++ )
  2393.     ((Entry*)filedetails->child(i))->updateSize();
  2394. }
  2395. const char* Flu_File_Chooser :: value()
  2396. {
  2397.   if( filename.size() == 0 )
  2398.     return NULL;
  2399.   else
  2400.     return filename.value();
  2401. }
  2402. int Flu_File_Chooser :: count()
  2403. {
  2404.   if( selectionType & MULTI )
  2405.     {
  2406.       int n = 0;
  2407.       Fl_Group *g = getEntryGroup();
  2408.       for( int i = 0; i < g->children(); i++ )
  2409. {
  2410. #ifdef WIN32
  2411.   if( ((Entry*)g->child(i))->filename == "My Computer" )
  2412.     continue;
  2413. #endif
  2414.   if( ((Entry*)g->child(i))->selected )
  2415.     n++;
  2416. }
  2417.       return n;
  2418.     }
  2419.   else
  2420.     return (strlen(filename.value())==0)? 0 : 1;
  2421. }
  2422. void Flu_File_Chooser :: value( const char *v )
  2423. {
  2424.   cd( v );
  2425.   if( !v )
  2426.     return;
  2427.   // try to find the file and select it
  2428.   const char *slash = strrchr( v, '/' );
  2429.   if( slash )
  2430.     slash++;
  2431.   else
  2432.     {
  2433.       slash = strrchr( v, '\' );
  2434.       if( slash )
  2435. slash++;
  2436.       else
  2437. slash = v;
  2438.     }
  2439.   filename.value( slash );
  2440.   filename.position( filename.size(), filename.size() );
  2441.   Fl_Group *g = getEntryGroup();
  2442.   for( int i = 0; i < g->children(); i++ )
  2443.     {
  2444.       if( ((Entry*)g->child(i))->filename == slash )
  2445. {
  2446.   ((Entry*)g->child(i))->selected = true;
  2447.   filelist->scroll_to( (Entry*)g->child(i) );
  2448.   filedetails->scroll_to( (Entry*)g->child(i) );
  2449.   redraw();
  2450.   return;
  2451. }
  2452.     }
  2453. }
  2454. const char* Flu_File_Chooser :: value( int n )
  2455. {
  2456.   Fl_Group *g = getEntryGroup();
  2457.   for( int i = 0; i < g->children(); i++ )
  2458.     {
  2459. #ifdef WIN32
  2460.       if( ((Entry*)g->child(i))->filename == "My Computer" )
  2461. continue;
  2462. #endif
  2463.       if( ((Entry*)g->child(i))->selected )
  2464. {
  2465.   n--;
  2466.   if( n == 0 )
  2467.     {
  2468.       FluSimpleString s = currentDir + ((Entry*)g->child(i))->filename;
  2469.       filename.value( s.c_str() );
  2470.       filename.position( filename.size(), filename.size() );
  2471.       return value();
  2472.     }
  2473. }
  2474.     }
  2475.   return "";
  2476. }
  2477. void Flu_File_Chooser :: reloadCB()
  2478. {
  2479. #ifdef WIN32
  2480.   refreshDrives = true;
  2481. #endif
  2482.   cd( currentDir.c_str() );
  2483. }
  2484. void Flu_File_Chooser :: addToFavoritesCB()
  2485. {
  2486.   // eliminate duplicates
  2487.   bool duplicate = false;
  2488.   for( int i = 1; i <= favoritesList->size(); i++ )
  2489.     {
  2490.       if( streq( currentDir.c_str(), favoritesList->text(i) ) )
  2491. {
  2492.   duplicate = true;
  2493.   break;
  2494. }
  2495.     }
  2496.   if( !duplicate )
  2497.     favoritesList->add( currentDir.c_str() );
  2498.   // save the favorites
  2499.   FILE *f = fopen( configFilename.c_str(), "w" );
  2500.   if( f )
  2501.     {
  2502.       for( int i = 1; i <= favoritesList->size(); i++ )
  2503. fprintf( f, "%sn", favoritesList->text(i) );
  2504.       fclose( f );
  2505.     }
  2506. }
  2507. FluSimpleString Flu_File_Chooser :: formatDate( const char *d )
  2508. {
  2509.   // convert style "Wed Mar 19 07:23:11 2003" to "MM/DD/YY HH:MM AM|PM"
  2510.   int month, day, year, hour, minute, second;
  2511.   bool pm;
  2512.   char MM[16], dummy[64];
  2513.   sscanf( d, "%s %s %d %d:%d:%d %d", dummy, MM, &day, &hour, &minute, &second, &year );
  2514.   pm = ( hour >= 12 );
  2515.   if( hour == 0 )
  2516.     hour = 12;
  2517.   if( hour >= 13 )
  2518.     hour -= 12;
  2519.   if( strcmp(MM,"Jan")==0 ) month = 1;
  2520.   else if( strcmp(MM,"Feb")==0 ) month = 2;
  2521.   else if( strcmp(MM,"Mar")==0 ) month = 3;
  2522.   else if( strcmp(MM,"Apr")==0 ) month = 4;
  2523.   else if( strcmp(MM,"May")==0 ) month = 5;
  2524.   else if( strcmp(MM,"Jun")==0 ) month = 6;
  2525.   else if( strcmp(MM,"Jul")==0 ) month = 7;
  2526.   else if( strcmp(MM,"Aug")==0 ) month = 8;
  2527.   else if( strcmp(MM,"Sep")==0 ) month = 9;
  2528.   else if( strcmp(MM,"Oct")==0 ) month = 10;
  2529.   else if( strcmp(MM,"Nov")==0 ) month = 11;
  2530.   else month = 12;
  2531.   sprintf( dummy, "%d/%d/%02d %d:%02d %s", month, day, year, hour, minute, pm?"PM":"AM" );
  2532.   FluSimpleString formatted = dummy;
  2533.   return formatted;
  2534. }
  2535. void Flu_File_Chooser :: win2unix( FluSimpleString &s )
  2536. {
  2537.   int len = s.size();
  2538.   for( int i = 0; i < len; i++ )
  2539.     if( s[i] == '\' )
  2540.       s[i] = '/';
  2541. }
  2542. void Flu_File_Chooser :: cleanupPath( FluSimpleString &s )
  2543. {
  2544.   // convert all '' to '/'
  2545.   win2unix( s );
  2546.   FluSimpleString newS(s.size()+1);
  2547.   int oldPos, newPos;
  2548.   for( oldPos = 0, newPos = 0; oldPos < s.size(); oldPos++ )
  2549.     {
  2550.       // remove "./" 
  2551.       if( s[oldPos] == '.' && s[oldPos+1] == '/' )
  2552. oldPos += 2;
  2553.       // convert "//" to "/"
  2554.       else if( s[oldPos] == '/' && s[oldPos+1] == '/' )
  2555. oldPos++;
  2556. #ifdef WIN32
  2557.       // upcase "c:" to "C:"
  2558.       else if( s[oldPos+1] == ':' )
  2559. s[oldPos] = toupper( s[oldPos] );
  2560. #endif
  2561.       // remove "../" by removing everything back to the last "/"
  2562.       if( oldPos+2 < s.size() ) // bounds check
  2563. {
  2564.   if( s[oldPos] == '.' && s[oldPos+1] == '.' && s[oldPos+2] == '/' && newS != "/" )
  2565.     {
  2566.       // erase the last character, which should be a '/'
  2567.       newPos--;
  2568.       newS[newPos] = '';
  2569.       // look for the previous '/'
  2570.       const char *lastSlash = strrchr( newS.c_str(), '/' );
  2571.       // make the new string position after the slash
  2572.       newPos = (lastSlash-newS.c_str())+1;
  2573.       oldPos += 3;
  2574.     }
  2575. }
  2576.       newS[newPos] = s[oldPos];
  2577.       newPos++;
  2578.     }
  2579.   newS[newPos] = '';
  2580.   s = newS;
  2581. }
  2582. bool Flu_File_Chooser :: correctPath( FluSimpleString &path )
  2583. {
  2584.   // the path may or may not be an alias, needing corrected
  2585. #ifdef WIN32
  2586.   // point to the correct desktop
  2587.   if( path == "/Desktop/" || path == "/Desktop" )
  2588.     {
  2589.       path = userHome + "Desktop";
  2590.       return true;
  2591.     }
  2592.   else if( path == userHome+"Desktop" || path == userHome+"Desktop/" )
  2593.     return true;
  2594.   else if( path == "/Desktop/My Computer/" || path == "/Desktop/My Computer" || 
  2595.    path == userHome+"Desktop/My Computer/" || path == userHome+"Desktop/My Computer" )
  2596.     path = "/";
  2597.   else if( path == "/Desktop/My Documents/" || path == "/Desktop/My Documents" ||
  2598.    path == userHome+"Desktop/My Documents/" || path == userHome+"Desktop/My Documents" )
  2599.     path = userHome + "My Documents";
  2600. #endif
  2601.   return false;
  2602. }
  2603. void Flu_File_Chooser :: backCB()
  2604. {
  2605.   if( !currentHist ) return;
  2606.   if( currentHist->last )
  2607.     {
  2608.       currentHist = currentHist->last;
  2609.       walkingHistory = true;
  2610.       delayedCd = currentHist->path;
  2611.       Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, this );
  2612.     }
  2613. }
  2614. void Flu_File_Chooser :: forwardCB()
  2615. {
  2616.   if( !currentHist ) return;
  2617.   if( currentHist->next )
  2618.     {
  2619.       currentHist = currentHist->next;
  2620.       walkingHistory = true;
  2621.       delayedCd = currentHist->path;
  2622.       Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, this );
  2623.     }
  2624. }
  2625. void Flu_File_Chooser :: buildFilesystemsCombo()
  2626. {
  2627.   // add all filesystems
  2628.   filesystems->tree.clear();
  2629. #ifdef WIN32
  2630.   char volumeName[1024];
  2631.   Flu_Tree_Browser::Node *n;
  2632.   n = filesystems->tree.add( "Desktop/" ); n->branch_icon( &little_desktop );
  2633.   n = filesystems->tree.add( "Desktop/My Documents/" ); n->branch_icon( &documents );
  2634.   n = filesystems->tree.add( "Desktop/My Computer/" ); n->branch_icon( &computer );
  2635.   // get the filesystems and add them
  2636.   {
  2637.     if( refreshDrives )
  2638.       driveMask = GetLogicalDrives();
  2639.     DWORD mask = driveMask;
  2640.     for( int i = 0; i < 26; i++ )
  2641.       {
  2642. drives[i] = "";
  2643. driveIcons[i] = &disk_drive;
  2644. if( mask & 1 )
  2645.   {
  2646.     FluSimpleString s = "Desktop/My Computer/";
  2647.     char drive[] = "A:";
  2648.     char windrive[] = "A:\";
  2649.     windrive[0] = drive[0] = 'A' + i;
  2650.     DWORD type;
  2651.     if( refreshDrives )
  2652.       {
  2653. volumeName[0] = '';
  2654. type = driveTypes[i] = GetDriveType( windrive );
  2655. if( type != DRIVE_REMOVABLE && type != DRIVE_REMOTE )
  2656.   GetVolumeInformation( windrive, volumeName, 1024, NULL, NULL, NULL, NULL, 0 );
  2657. volumeNames[i] = volumeName;
  2658.       }
  2659.     else
  2660.       {
  2661. strncpy( volumeName, volumeNames[i].c_str(), 1024 );
  2662. type = driveTypes[i];
  2663.       }
  2664.     //s += volume
  2665.     const char *disk = "Disk";
  2666.     switch( type )
  2667.       {
  2668.       case DRIVE_REMOVABLE: 
  2669. disk = strlen(volumeName)?volumeName: ( 1 < 2 ? "Floppy Disk" : "Removable Disk" );
  2670. driveIcons[i] = &floppy_drive;
  2671. break;
  2672.       case DRIVE_FIXED: 
  2673. disk = strlen(volumeName)?volumeName:"Local Disk";
  2674. //driveIcons[i] = &disk_drive;
  2675. break;
  2676.       case DRIVE_CDROM: 
  2677. disk = strlen(volumeName)?volumeName:"Compact Disk";
  2678. driveIcons[i] = &cd_drive;
  2679. break;
  2680.       case DRIVE_REMOTE: 
  2681. disk = strlen(volumeName)?volumeName:"Network Disk";
  2682. driveIcons[i] = &network_drive;
  2683. break;
  2684.       case DRIVE_RAMDISK: 
  2685. disk = strlen(volumeName)?volumeName:"RAM Disk";
  2686. driveIcons[i] = &ram_drive;
  2687. break;
  2688.       }
  2689.     drives[i] = FluSimpleString(disk) + " (" + FluSimpleString(drive) + ")/";
  2690.     s += drives[i];
  2691.     n = filesystems->tree.add( s.c_str() ); n->branch_icon( driveIcons[i] );
  2692.     // erase the trailing '/' to make things look nicer
  2693.     drives[i][ drives[i].size()-1 ] = '';
  2694.   }
  2695. mask >>= 1;
  2696.       }
  2697.   }
  2698.   n = filesystems->tree.add( "Favorites/" ); n->branch_icon( &little_favorites );
  2699.   refreshDrives = false;
  2700. #elif defined __APPLE__
  2701.   filesystems->tree.label( "/" );
  2702.   // get all volume mount points and add to the filesystems combobox
  2703.   dirent **e;
  2704.   char *name;
  2705.   int num = fl_filename_list( "/Volumes/", &e );
  2706.   if( num > 0 )
  2707.     {
  2708.       int i;
  2709.       for( i = 0; i < num; i++ )
  2710. {
  2711.   name = e[i]->d_name;
  2712.   // ignore the "." and ".." names
  2713.   if( strcmp( name, "." ) == 0 || strcmp( name, ".." ) == 0 ||
  2714.       strcmp( name, "./" ) == 0 || strcmp( name, "../" ) == 0 ||
  2715.       strcmp( name, ".\" ) == 0 || strcmp( name, "..\" ) == 0 )
  2716.     continue;
  2717.   // if 'name' ends in '/', remove it
  2718.   if( name[strlen(name)-1] == '/' )
  2719.     name[strlen(name)-1] = '';
  2720.   FluSimpleString fullpath = "/Volumes/";
  2721.   fullpath += name;
  2722.   fullpath += "/";
  2723.   filesystems->tree.add( fullpath.c_str() );
  2724. }
  2725.     }
  2726. #else
  2727.   filesystems->tree.label( "/" );
  2728.   // get all mount points and add to the filesystems combobox
  2729.   FILE *fstab; // /etc/mtab or /etc/mnttab file
  2730.   char dummy[256], mountPoint[256], line[1024]; // Input line
  2731.   FluSimpleString mount;
  2732.   fstab = fopen( "/etc/fstab", "r" ); // Otherwise fallback to full list
  2733.   if( fstab )
  2734.     {
  2735.       while( fgets( line, 1024, fstab ) )
  2736. {
  2737.   if( line[0] == '#' || line[0] == 'n' )
  2738.     continue;
  2739.   // in fstab, mount point is second full string
  2740.   sscanf( line, "%s %s", dummy, mountPoint );
  2741.   mount = mountPoint;
  2742.   // cull some stuff
  2743.   if( mount[0] != '/' ) continue;
  2744.   if( mount == "/" ) continue;
  2745.   if( mount == "/boot" ) continue;
  2746.   if( mount == "/proc" ) continue;
  2747.   // now add the mount point
  2748.   mount += "/";
  2749.   filesystems->tree.add( mount.c_str() );
  2750. }
  2751.       fclose( fstab );
  2752.     }
  2753. #endif
  2754. }
  2755. void Flu_File_Chooser :: clear_history()
  2756. {
  2757.   currentHist = history;
  2758.   while( currentHist )
  2759.     {
  2760.       History *next = currentHist->next;
  2761.       delete currentHist;
  2762.       currentHist = next;
  2763.     }
  2764.   currentHist = history = NULL;
  2765.   backBtn->deactivate();
  2766.   forwardBtn->deactivate();
  2767. }
  2768. void Flu_File_Chooser :: addToHistory()
  2769. {
  2770.   // remember history
  2771.   // only store this path in the history if it is not the current directory
  2772.   if( currentDir.size() && !walkingHistory )
  2773.     {
  2774.       if( history == NULL )
  2775. {
  2776.   history = new History;
  2777.   currentHist = history;
  2778.   currentHist->path = currentDir;
  2779. }
  2780.       else if( currentHist->path != currentDir )
  2781. {
  2782.   // since we are adding a new path, delete everything after this path
  2783.   History *h = currentHist->next;
  2784.   while( h )
  2785.     {
  2786.       History *next = h->next;
  2787.       delete h;
  2788.       h = next;
  2789.     }
  2790.   currentHist->next = new History;
  2791.   currentHist->next->last = currentHist;
  2792.   currentHist = currentHist->next;
  2793.   currentHist->path = currentDir;
  2794. }
  2795.       History * h = history;
  2796.       while( h )
  2797. h = h->next;
  2798.     }
  2799.   walkingHistory = false;
  2800.   if( currentHist )
  2801.     {
  2802.       if( currentHist->last )
  2803. backBtn->activate();
  2804.       else
  2805. backBtn->deactivate();
  2806.       if( currentHist->next )
  2807. forwardBtn->activate();
  2808.       else
  2809. forwardBtn->deactivate();
  2810.     }
  2811. }
  2812. // treating the string as a '|' or ';' delimited sequence of patterns, strip them out and place in patterns
  2813. // return whether it is likely that "s" represents a regexp file-matching pattern
  2814. bool Flu_File_Chooser :: stripPatterns( FluSimpleString s, StringVector* patterns )
  2815. {
  2816.   if( s.size() == 0 )
  2817.     return false;
  2818.   char *tok = strtok( (char*)s.c_str(), "|;" );
  2819.   int tokens = 0;
  2820.   while( tok )
  2821.     {
  2822.       tokens++;
  2823.       if( tok[0] == ' ' )
  2824. tok++; // skip whitespace
  2825.       patterns->add( tok );
  2826.       tok = strtok( NULL, "|;" );
  2827.     }
  2828.   // if there is just a single token and it looks like it's not a pattern,
  2829.   // then it is probably JUST a filename, in which case it should not be
  2830.   // treated as a pattern
  2831.   if( _isProbablyAPattern( s.c_str() ) )
  2832.     return true;
  2833.   else if( tokens == 1 )
  2834.     {
  2835.       patterns->clear();
  2836.       return false;
  2837.     }
  2838.   else
  2839.     return true;
  2840. }
  2841. void Flu_File_Chooser :: cd( const char *path )
  2842. {
  2843.   Entry *entry;
  2844.   char cwd[1024];
  2845.   if( !path || path[0] == '' )
  2846.     {
  2847.       path = getcwd( cwd, 1024 );
  2848.       if( !path )
  2849. path = "./";
  2850.     }
  2851.   lastSelected = 0;
  2852.   previewGroup->file = "";
  2853.   previewGroup->redraw();
  2854.   filelist->scroll_to_beginning();
  2855.   filescroll->position( 0, 0 );
  2856.   bool listMode = !fileDetailsBtn->value() || streq( path, FAVORITES_UNIQUE_STRING );
  2857. #ifdef WIN32
  2858.   // refresh the drives if viewing "My Computer"
  2859.   if( strcmp( path, "/" ) == 0 )
  2860.     refreshDrives = true;
  2861. #endif
  2862.   buildFilesystemsCombo();
  2863.   filename.take_focus();
  2864.   trashBtn->deactivate();
  2865.   reloadBtn->activate();
  2866.   newDirBtn->activate();
  2867.   previewBtn->activate();
  2868.   hiddenFiles->activate();
  2869.   addFavoriteBtn->activate();
  2870.   resize( x(), y(), w(), h() );
  2871.   if( listMode )
  2872.     {
  2873.       //filecolumns->hide();
  2874.       //filescroll->hide();
  2875.       fileDetailsGroup->hide();
  2876.       filelist->show();
  2877.       filelist->parent()->resizable( filelist );
  2878.     }
  2879.   else
  2880.     {
  2881.       filelist->hide();
  2882.       //filecolumns->show();
  2883.       //filescroll->show();
  2884.       //filescroll->parent()->resizable( filescroll );
  2885.       fileDetailsGroup->show();
  2886.       fileDetailsGroup->parent()->resizable( fileDetailsGroup );
  2887.       updateEntrySizes();
  2888.     }
  2889.   FluSimpleString currentFile = filename.value();
  2890.   filescroll->position( 0, 0 );
  2891.   //Fl::focus( &filename );
  2892.   upDirBtn->activate();
  2893.   ok.activate();
  2894.   // check for favorites
  2895.   if( streq( path, FAVORITES_UNIQUE_STRING ) )
  2896.     {
  2897.       currentDir = FAVORITES_UNIQUE_STRING;
  2898.       addToHistory();
  2899.       newDirBtn->deactivate();
  2900.       previewBtn->deactivate();
  2901.       reloadBtn->deactivate();
  2902.       addFavoriteBtn->deactivate();
  2903.       hiddenFiles->deactivate();
  2904.       filesystems->input.value( "Favorites" );
  2905.       filelist->clear();
  2906.       filedetails->clear();
  2907.       if( listMode )
  2908. filelist->begin();
  2909.       else
  2910. filedetails->begin();
  2911.       for( int i = 1; i <= favoritesList->size(); i++ )
  2912. {
  2913.   entry = new Entry( favoritesList->text(i), ENTRY_FAVORITE, false/*fileDetailsBtn->value()*/, this );
  2914.   entry->updateSize();
  2915.   entry->updateIcon();
  2916. }
  2917.       if( listMode )
  2918. filelist->end();
  2919.       else
  2920. filedetails->end();
  2921.       redraw();
  2922.       ok.deactivate();
  2923.       return;
  2924.     }
  2925.   // check for the current directory
  2926.   else if( streq( path, "." ) || streq( path, "./" ) || streq( path, ".\" ) )
  2927.     {
  2928.       // do nothing. just rescan this directory
  2929.     }
  2930.   // check for parent directory
  2931.   else if( streq( path, ".." ) || streq( path, "../" ) || streq( path, "..\" ) )
  2932.     {
  2933.       // if we are viewing the favorites and want to go back a directory, go to the previous directory
  2934.       if( currentDir == FAVORITES_UNIQUE_STRING )
  2935. {
  2936.   backCB();
  2937.   return;
  2938. }
  2939. #ifdef WIN32
  2940.       // if we are at the desktop already, then we cannot go back any further
  2941.       if( currentDir == "/Desktop/" || currentDir == "/Desktop" )
  2942. {
  2943.   // do nothing
  2944. }
  2945.       else if( currentDir == userHome+"Desktop" || currentDir == userHome+"Desktop/" )
  2946. currentDir = userHome;
  2947.       // if we are viewing "My Computer" and want to go back a directory, go to the desktop
  2948.       else if( currentDir == "/" )
  2949. currentDir = userHome + "Desktop";
  2950.       // if we are at a top level drive, go to "My Computer" (i.e. "/")
  2951.       else if( currentDir[1] == ':' && currentDir[3] == '' )
  2952. currentDir = "/";
  2953.       else
  2954. #else
  2955. // if the current directory is already as far back as we can go, ignore
  2956. if( currentDir != "/" )
  2957. #endif
  2958.   {
  2959.     // strip everything off the end to the next "/"
  2960.     int end = currentDir.size()-1;
  2961.     currentDir[end] = '';
  2962.     while( currentDir[end] != '/' )
  2963.       {
  2964. currentDir[end] = '';
  2965. end--;
  2966.       }
  2967.   }
  2968.     }
  2969.   // check for absolute path
  2970. #ifdef WIN32
  2971.   else if( path[1] == ':' || path[0] == '/' )
  2972. #else
  2973.   else if( path[0] == '/' )
  2974. #endif
  2975.     {
  2976.       currentDir = path;
  2977.     }
  2978.   // else relative path
  2979.   else
  2980.     {
  2981.       // concatenate currentDir with path to make an absolute path
  2982.       currentDir += path;
  2983.     }
  2984.   int numDirs = 0, numFiles = 0;
  2985.   filelist->clear();
  2986.   filedetails->clear();
  2987.   cleanupPath( currentDir );
  2988. #ifdef WIN32
  2989.   bool isTopDesktop = ( currentDir == "/Desktop" || currentDir == "/Desktop/" );
  2990.   bool isDesktop = correctPath( currentDir );
  2991.   if( isTopDesktop )
  2992.     upDirBtn->deactivate();
  2993. #else
  2994.   correctPath( currentDir );
  2995.   if( currentDir == "/" )
  2996.     upDirBtn->deactivate();
  2997. #endif
  2998. #ifdef WIN32
  2999.   bool root = false;
  3000.   // check for my computer
  3001.   if( currentDir == "/" )
  3002.     {
  3003.       ok.deactivate();
  3004.       root = true;
  3005.       if( listMode )
  3006. filelist->begin();
  3007.       else
  3008. filedetails->begin();
  3009.       for( int i = 0; i < 26; i++ )
  3010. {
  3011.   if( drives[i][0] != '' )
  3012.     {
  3013.       char drive[] = "A:/";
  3014.       drive[0] = 'A' + i;
  3015.       entry = new Entry( drive, ENTRY_DRIVE, fileDetailsBtn->value(), this );
  3016.       switch( driveTypes[i] )
  3017. {
  3018. case DRIVE_REMOVABLE: entry->description = "Floppy Disk"; break;
  3019. case DRIVE_FIXED: entry->description = "Local Disk"; break;
  3020. case DRIVE_CDROM: entry->description = "Compact Disk"; break;
  3021. case DRIVE_REMOTE: entry->description = "Network Disk"; break;
  3022. case DRIVE_RAMDISK: entry->description = "RAM Disk"; break;
  3023. }
  3024.       entry->icon = driveIcons[i];
  3025.       entry->altname = drives[i];
  3026.       entry->updateSize();
  3027.       entry->updateIcon();
  3028.     }
  3029. }
  3030.       if( listMode )
  3031. filelist->end();
  3032.       else
  3033. filedetails->end();
  3034.       redraw();
  3035.     }
  3036.   // check for desktop. if so, add My Computer and My Documents
  3037.   else if( isDesktop )
  3038.     {
  3039.       if( listMode )
  3040. filelist->begin();
  3041.       else
  3042. filedetails->begin();
  3043.       entry = new Entry( "My Documents", ENTRY_MYDOCUMENTS, fileDetailsBtn->value(), this );
  3044.       entry->updateSize();     
  3045.       entry->updateIcon();
  3046.       entry = new Entry( "My Computer", ENTRY_MYCOMPUTER, fileDetailsBtn->value(), this );
  3047.       entry->updateSize();     
  3048.       entry->updateIcon();
  3049.       if( listMode )
  3050. filelist->end();
  3051.       else
  3052. filedetails->end();
  3053.       numDirs += 2;
  3054.     }
  3055. #endif
  3056.   // see if currentDir is in fact a directory
  3057.   // if so, make sure there is a trailing "/" and we're done
  3058.   if( fl_filename_isdir( currentDir.c_str() ) || currentDir=="/" )
  3059.     {
  3060.       if( currentDir[strlen(currentDir.c_str())-1] != '/' )
  3061. currentDir += "/";
  3062. #ifdef WIN32
  3063.       if( filename.value()[1] != ':' )
  3064. #else
  3065.       if( filename.value()[0] != '/' )
  3066. #endif
  3067. filename.value( "" );
  3068.       currentFile = "";
  3069.     }
  3070.   // now we have the current directory and possibly a file at the end
  3071.   // try to split into path and file
  3072.   if( currentDir[currentDir.size()-1] != '/' )
  3073.     {
  3074.       char *lastSlash = (char*)strrchr( currentDir.c_str(), '/' );
  3075.       if( lastSlash )
  3076. {
  3077.   currentFile = lastSlash+1;
  3078.   lastSlash[1] = '';
  3079. }
  3080.     }
  3081.   // make sure currentDir ends in '/'
  3082.   if( currentDir[currentDir.size()-1] != '/' )
  3083.     currentDir += "/";
  3084. #ifdef WIN32
  3085.   {
  3086.     FluSimpleString tmp = currentDir;
  3087.     if( isTopDesktop )
  3088.       currentDir = "/Desktop/";
  3089.     addToHistory();
  3090.     if( isTopDesktop )
  3091.       currentDir = tmp;
  3092.   }
  3093. #else
  3094.   addToHistory();
  3095. #endif
  3096.   delayedCd = "./";
  3097. #ifdef WIN32
  3098.   // set the filesystems input value
  3099.   // check for drives
  3100.   if( currentDir[1] == ':' && currentDir[3] == '' )
  3101.     {
  3102.       filesystems->input.value( currentDir.c_str() );
  3103.     }
  3104.   else if( currentDir == "/" )
  3105.     filesystems->input.value( "My Computer" );
  3106.   else
  3107. #endif
  3108.     {
  3109.       filesystems->input.value( currentDir.c_str() );
  3110. #ifdef WIN32
  3111.       FluSimpleString treePath = "/Desktop/My Computer/" + currentDir;
  3112.       Flu_Tree_Browser::Node *n = filesystems->tree.add( treePath.c_str() );
  3113.       if( currentDir == userHome + "Desktop/" )
  3114. n->branch_icon( &little_desktop );
  3115.       if( currentDir == userHome + "My Documents/" )
  3116. n->branch_icon( &documents );
  3117. #else
  3118.       filesystems->tree.add( currentDir.c_str() );
  3119. #endif
  3120.     }
  3121. #ifdef WIN32
  3122.   if( root )
  3123.     return;
  3124. #endif
  3125.   FluSimpleString pathbase, fullpath;
  3126.   bool isDir, isCurrentFile = false;
  3127.   const char *lastAddedFile = NULL, *lastAddedDir = NULL;
  3128.   pathbase = currentDir;
  3129.   // take the current pattern and make a list of filter pattern strings
  3130.   StringVector currentPatterns;
  3131.   {
  3132.     FluSimpleString pat = patterns[filePattern->list.value()-1];
  3133.     while( pat.size() )
  3134.       {
  3135. int p = pat.find( ',' );
  3136. if( p == -1 )
  3137.   {
  3138.     if( pat != "*" )
  3139.       pat = "*." + pat;
  3140.     currentPatterns.add( pat );
  3141.     break;
  3142.   }
  3143. else
  3144.   {
  3145.     FluSimpleString s = pat.c_str() + p + 1;
  3146.     pat[p] = '';
  3147.     if( pat != "*" )
  3148.       pat = "*." + pat;
  3149.     currentPatterns.add( pat );
  3150.     pat = s;
  3151.   }
  3152.       }
  3153.   }
  3154.   // add any user-defined patterns
  3155.   StringVector userPatterns;
  3156.   // if the user just hit <Tab> but the filename input area is empty,
  3157.   // then use the current patterns
  3158.   if( !filenameTabCallback || currentFile != "*" )
  3159.     stripPatterns( currentFile, &userPatterns );
  3160.   // read the directory
  3161.   dirent **e;
  3162.   char *name;
  3163.   int num = fl_filename_list( pathbase.c_str(), &e );
  3164.   if( num > 0 )
  3165.     {
  3166.       int i;
  3167.       for( i = 0; i < num; i++ )
  3168. {
  3169.   name = e[i]->d_name;
  3170.   // ignore the "." and ".." names
  3171.   if( strcmp( name, "." ) == 0 || strcmp( name, ".." ) == 0 ||
  3172.       strcmp( name, "./" ) == 0 || strcmp( name, "../" ) == 0 ||
  3173.       strcmp( name, ".\" ) == 0 || strcmp( name, "..\" ) == 0 )
  3174.     continue;
  3175.   // if 'name' ends in '/', remove it
  3176.   if( name[strlen(name)-1] == '/' )
  3177.     name[strlen(name)-1] = '';
  3178.   // file or directory?
  3179.   fullpath = pathbase + name;
  3180.   isDir = ( fl_filename_isdir( fullpath.c_str() ) != 0 );
  3181.   // was this file specified explicitly?
  3182.   isCurrentFile = ( currentFile == name );
  3183. #ifndef WIN32
  3184.   // filter hidden files
  3185.   if( !isCurrentFile && !hiddenFiles->value() && ( name[0] == '.' ) )
  3186.     continue;
  3187. #endif
  3188.   // only directories?
  3189.   if( (selectionType & DIRECTORY) && !isDir )
  3190.     continue;
  3191.   //if( !isDir /*!isCurrentFile*/ )
  3192.     {
  3193.       // filter according to the user pattern in the filename input
  3194.       if( userPatterns.size() )
  3195. {
  3196.   bool cull = true;
  3197.   for( unsigned int i = 0; i < userPatterns.size(); i++ )
  3198.     {
  3199.       if( flu_filename_match( name, userPatterns[i].c_str() ) != 0 )
  3200. {
  3201.   cull = false;
  3202.   break;
  3203. }
  3204.     }
  3205.   if( cull )
  3206.     {
  3207.       // only filter directories if someone just hit <TAB>
  3208.       if( !isDir || ( isDir && filenameTabCallback ) )
  3209. continue;
  3210.     }
  3211. }
  3212.       // filter files according to the current pattern
  3213.       else
  3214. {
  3215.   bool cull = true;
  3216.   for( unsigned int i = 0; i < currentPatterns.size(); i++ )
  3217.     {
  3218.       if( flu_filename_match( name, currentPatterns[i].c_str() ) != 0 )
  3219. {
  3220.   cull = false;
  3221.   break;
  3222. }
  3223.     }
  3224.   if( cull )
  3225.     {
  3226.       // only filter directories if someone just hit <TAB>
  3227.       if( !isDir || ( isDir && filenameTabCallback ) )
  3228. continue;
  3229.     }
  3230. }
  3231.     }
  3232.   // add directories at the beginning, and files at the end
  3233.   entry = new Entry( name, isDir?ENTRY_DIR:ENTRY_FILE, fileDetailsBtn->value(), this );
  3234.   if( isDir )
  3235.     {
  3236.       if( listMode )
  3237. filelist->insert( *entry, 0 );
  3238.       else
  3239. filedetails->insert( *entry, 0 );
  3240.       numDirs++;
  3241.       lastAddedDir = entry->filename.c_str();
  3242.     }
  3243.   else
  3244.     {
  3245.       if( listMode )
  3246. filelist->add( entry );
  3247.       else
  3248. filedetails->add( entry );
  3249.       numFiles++;
  3250.       lastAddedFile = entry->filename.c_str();
  3251.     }
  3252.   // get some information about the file
  3253.   struct stat s;
  3254.   ::stat( fullpath.c_str(), &s );
  3255.   // store size as human readable and sortable integer
  3256.   entry->isize = s.st_size;
  3257.   if( isDir && entry->isize == 0 )
  3258.     entry->filesize = "";
  3259.   else
  3260.     {
  3261.       char buf[32];
  3262.       /*
  3263. if( (entry->isize >> 40) > 0 ) // terrabytes
  3264. {
  3265. double TB = double(entry->isize)/double(1<<40);
  3266. sprintf( buf, "%.1f TB", TB );
  3267. }
  3268.       */
  3269.       if( (entry->isize >> 30) > 0 ) // gigabytes
  3270. {
  3271.   double GB = double(entry->isize)/double(1<<30);
  3272.   sprintf( buf, "%.1f GB", GB );
  3273. }
  3274.       else if( (entry->isize >> 20) > 0 ) // megabytes
  3275. {
  3276.   double MB = double(entry->isize)/double(1<<20);
  3277.   sprintf( buf, "%.1f MB", MB );
  3278. }
  3279.       else if( (entry->isize >> 10) > 0 ) // kilabytes
  3280. {
  3281.   double KB = double(entry->isize)/double(1<<10);
  3282.   sprintf( buf, "%.1f KB", KB );
  3283. }
  3284.       else // bytes
  3285. {
  3286.   sprintf( buf, "%d bytes", (int)entry->isize );
  3287. }
  3288.       entry->filesize = buf;
  3289.     }
  3290.   // store date as human readable and sortable integer
  3291.   entry->date = formatDate( ctime( &s.st_mtime ) );//ctime( &s.st_mtime );
  3292.   entry->idate = s.st_mtime;
  3293.   // convert the permissions into UNIX style rwx-rwx-rwx (user-group-others)
  3294.   /*
  3295.     unsigned int p = s.st_mode;
  3296.     entry->pU = bool(p&S_IRUSR)<<2 | bool(p&S_IWUSR)<<1 | bool(p&S_IXUSR);
  3297.     entry->pG = bool(p&S_IRGRP)<<2 | bool(p&S_IWGRP)<<1 | bool(p&S_IXGRP);
  3298.     entry->pO = bool(p&S_IROTH)<<2 | bool(p&S_IWOTH)<<1 | bool(p&S_IXOTH);
  3299.     char* perms[8] = { "---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx" };
  3300.     entry->permissions = perms[entry->pU];
  3301.     entry->permissions += perms[entry->pG];
  3302.     entry->permissions += perms[entry->pO];
  3303.   */
  3304.   entry->updateSize();
  3305.   entry->updateIcon();
  3306.   if( isCurrentFile )
  3307.     {
  3308.       filename.value( name );
  3309.       entry->selected = true;
  3310.       lastSelected = entry;
  3311.       if( entry->type == ENTRY_FILE )
  3312. previewGroup->file = currentDir + name;
  3313.       previewGroup->redraw();
  3314.       filelist->scroll_to( entry );
  3315.       filedetails->scroll_to( entry );
  3316.       //break;
  3317.     }
  3318. }
  3319.       for( i = 0; i < num; i++ )
  3320. free((void*)(e[i]));
  3321.       free((void*)e);
  3322.     }
  3323.   // sort the files: directories first, then files
  3324.   if( listMode )
  3325.     filelist->sort( numDirs );
  3326.   else
  3327.     filedetails->sort( numDirs );
  3328.   // see if the user pushed <Tab> in the filename input field
  3329.   if( filenameTabCallback )
  3330.     {
  3331.       filenameTabCallback = false;
  3332.       FluSimpleString prefix = commonStr();
  3333.       if( numDirs == 1 && 
  3334.   currentFile == (FluSimpleString(lastAddedDir)+"*") )
  3335. {
  3336.   delayedCd = lastAddedDir;
  3337.   Fl::add_timeout( 0.0f, Flu_File_Chooser::delayedCdCB, this );
  3338. }
  3339.       if( numDirs == 1 && numFiles == 0 )
  3340. {
  3341. #ifdef WIN32
  3342.   if( filename.value()[1] == ':' )
  3343. #else
  3344.   if( filename.value()[0] == '/' )
  3345. #endif
  3346.     {
  3347.       FluSimpleString s = currentDir + lastAddedDir + "/";
  3348.       filename.value( s.c_str() );
  3349.     }
  3350.   else
  3351.     filename.value( lastAddedDir );
  3352. }
  3353.       else if( numFiles == 1 && numDirs == 0 )
  3354. {
  3355. #ifdef WIN32
  3356.   if( filename.value()[1] == ':' )
  3357. #else
  3358.   if( filename.value()[0] == '/' )
  3359. #endif
  3360.     {
  3361.       FluSimpleString s = currentDir + lastAddedFile;
  3362.       filename.value( s.c_str() );
  3363.     }
  3364.   else
  3365.     filename.value( lastAddedFile );
  3366. }
  3367.       else if( prefix.size() >= currentFile.size() )
  3368. {
  3369. #ifdef WIN32
  3370.   if( filename.value()[1] == ':' )
  3371. #else
  3372.   if( filename.value()[0] == '/' )
  3373. #endif
  3374.     {
  3375.       FluSimpleString s = currentDir + prefix;
  3376.       filename.value( s.c_str() );
  3377.     }
  3378.   else
  3379.     filename.value( prefix.c_str() );
  3380. }
  3381.       if( currentFile == "*" && 
  3382. #ifdef WIN32
  3383.   filename.value()[1] != ':' )
  3384. #else
  3385.   filename.value()[0] != '/' )
  3386. #endif
  3387.         {
  3388.   filename.value( "" );
  3389. }
  3390.     }
  3391.   // see if the user pushed <Enter> in the filename input field
  3392.   if( filenameEnterCallback )
  3393.     {
  3394.       filenameEnterCallback = false;
  3395. #ifdef WIN32
  3396.       if( filename.value()[1] == ':' )
  3397. #else
  3398.       if( filename.value()[0] == '/' )
  3399. #endif
  3400. filename.value( "" );
  3401.       //if( isCurrentFile && numFiles == 1 )
  3402.       if( !_isProbablyAPattern( filename.value() ) )
  3403. okCB();
  3404.     }
  3405.   if( _isProbablyAPattern( filename.value() ) )
  3406.     filename.position( 0, filename.size() );
  3407.   else
  3408.     filename.position( filename.size(), filename.size() );
  3409.   filename.take_focus();
  3410.   redraw();
  3411. }
  3412. // find the prefix string that is common to all entries in the list
  3413. FluSimpleString Flu_File_Chooser :: commonStr()
  3414. {
  3415.   FluSimpleString common;
  3416.   int index = 0;
  3417.   const char* name;
  3418.   int len, i;
  3419.   Fl_Group *g = getEntryGroup();
  3420.   for(;;)
  3421.     {
  3422.       bool allSkipped = true;
  3423.       for( i = 0; i < g->children(); i++ )
  3424. {
  3425.   name = ((Entry*)g->child(i))->filename.c_str();
  3426.   len = strlen( name );
  3427.   if( index >= len )
  3428.     continue;
  3429.   allSkipped = false;
  3430.   if( i == 0 )
  3431.     common.push_back( name[index] );
  3432.   else if( toupper(common[index]) != toupper(name[index]) )
  3433.     {
  3434.       common[index] = '';
  3435.       return common;
  3436.     }
  3437. }
  3438.       if( allSkipped )
  3439. break;
  3440.       index++;
  3441.     }
  3442.   return common;
  3443. }
  3444. static const char* _flu_file_chooser( const char *message, const char *pattern, const char *filename, int type )
  3445. {
  3446.   static Flu_File_Chooser *fc = NULL;
  3447.   static FluSimpleString retname;
  3448.   if( !fc )
  3449.     {
  3450.       fc = new Flu_File_Chooser( filename, pattern, type, message );
  3451.     }
  3452.   else
  3453.     {
  3454.       fc->clear_history();
  3455.       fc->label( message );
  3456.       if( !filename || filename[0] == '' )
  3457. {
  3458.   if( (!pattern || !fc->filter() || strcmp(pattern,fc->filter())) && fc->value() )
  3459.     {
  3460.       // if pattern is different, remove name but leave old directory:
  3461.       retname = fc->value();
  3462.       char *p = (char*)strrchr( retname.c_str(), '/' );
  3463.       if( p )
  3464. {
  3465.   // If the filename is "/foo", then the directory will be "/", not ""
  3466.   if( p == retname.c_str() )
  3467.     retname[1] = '';
  3468.   else
  3469.     p[1] = '';
  3470. }
  3471.     }
  3472.   fc->filter( pattern );
  3473.   fc->value( retname.c_str() );
  3474. }
  3475.       else
  3476. {
  3477.   fc->filter( pattern );
  3478.   fc->value( filename );
  3479. }
  3480.     }
  3481.   fc->set_modal();
  3482.   fc->show();
  3483.   while( fc->shown() )
  3484.     Fl::wait( 0.01 );
  3485.   if( fc->value() )
  3486.     {
  3487.       retname = fc->value();
  3488.       return retname.c_str();
  3489.     }
  3490.   else
  3491.     return 0;
  3492. }
  3493. const char* flu_file_chooser( const char *message, const char *pattern, const char *filename )
  3494. {
  3495.   return _flu_file_chooser( message, pattern, filename, Flu_File_Chooser::SINGLE );
  3496. }
  3497. const char* flu_dir_chooser( const char *message, const char *filename )
  3498. {
  3499.   return _flu_file_chooser( message, "*", filename, Flu_File_Chooser::DIRECTORY );
  3500. }