home *** CD-ROM | disk | FTP | other *** search
/ CD Actual Thematic 7: Programming / CDAT7.iso / Share / Editores / Perl5 / perl / lib / site / Tk / FileDialog.pm < prev    next >
Encoding:
Text File  |  1997-08-10  |  24.4 KB  |  968 lines

  1. ##################################################
  2. ##################################################
  3. ##                        ##
  4. ##    FileDialog - a reusable Tk-widget    ##
  5. ##             login screen        ##
  6. ##                        ##
  7. ##    Version 1.1                ##
  8. ##                        ##
  9. ##                        ##
  10. ##    Brent B. Powers                ##
  11. ##    Merrill Lynch                ##
  12. ##    powers@swaps-comm.ml.com        ##
  13. ##                        ##
  14. ##                        ##
  15. ##################################################
  16. ##################################################
  17.  
  18. # Change History:
  19. #   Version 1.0 - Initial implementation
  20. #   96 Jan 15    ringger@cs.rochester.edu - Fixed dialogue box creation.
  21. #   96 Jan 15    ringger - Added option for selecting directories.
  22. #   96 Feb 29    powers - Rewrote and componentized, and added a bunch of
  23. #        options.  Now requires perl 5.002gamma
  24. #
  25.  
  26. =head1 NAME
  27.  
  28. Tk::FileDialog - A highly configurable File Dialog widget for Perl/Tk.  
  29.  
  30. =head1 DESCRIPTION
  31.  
  32. The widget is composed of a number
  33. of sub-widgets, namely, a listbox for files and (optionally) directories, an entry
  34. for filename, an (optional) entry for pathname, an entry for a filter pattern, a 'ShowAll'
  35. checkbox (for enabling display of .* files and directories), and three buttons, namely
  36. OK, Rescan, and Cancel.  Note that the labels for all subwidgets (including the text
  37. for the buttons and Checkbox) are configurable for foreign language support.
  38.  
  39. =head1 SYNOPSIS
  40.  
  41. =over 4
  42.  
  43. =head2 Usage Description
  44.  
  45. To use FileDialog, simply create your FileDialog objects during initialization (or at
  46. least before a Show).  When you wish to display the FileDialog, invoke the 'Show' method
  47. on the FileDialog object;  The method will return either a file name, a path name, or
  48. undef.  undef is returned only if the user pressed the Cancel button.
  49.  
  50. =head2 Example Code
  51.  
  52. The following code creates a FileDialog and calls it.  Note that perl5.002gamma is
  53. required.
  54.  
  55. =over 4
  56.  
  57. =item
  58.  
  59.  #!/usr/local/bin/perl -w
  60.  
  61.  use Tk;
  62.  use Tk::FileDialog;
  63.  use strict;
  64.  
  65.  my($main) = MainWindow->new;
  66.  my($Horiz) = 1;
  67.  my($fname);
  68.  
  69.  my($LoadDialog) = $main->FileDialog(-Title =>'This is my title',
  70.                      -Create => 0);
  71.  
  72.  $LoadDialog->configure(-FPat => '*pl',
  73.                 -ShowAll => 'NO');
  74.  
  75.  $main->Entry(-textvariable => \$fname)
  76.      ->pack(-expand => 1,
  77.             -fill => 'x');
  78.  
  79.  $main->Button(-text => 'Kick me!',
  80.            -command => sub {
  81.            $fname = $LoadDialog->Show(-Horiz => $Horiz);
  82.            if (!defined($fname)) {
  83.                $fname = "Fine,Cancel, but no Chdir anymore!!!";
  84.                $LoadDialog->configure(-Chdir =>'NO');
  85.            }
  86.            })
  87.      ->pack(-expand => 1,
  88.             -fill => 'x');
  89.  
  90.  $main->Checkbutton(-text => 'Horizontal',
  91.             -variable => \$Horiz)
  92.      ->pack(-expand => 1,
  93.             -fill => 'x');
  94.  
  95.  $main->Button(-text => 'Exit',
  96.            -command => sub {
  97.            $main->destroy;
  98.            })
  99.      ->pack(-expand => 1,
  100.             -fill => 'x');
  101.  
  102.  MainLoop;
  103.  
  104.  print "Exit Stage right!\n";
  105.  
  106.  exit;
  107.  
  108.  
  109. =back
  110.  
  111. =back
  112.  
  113. =head1 METHODS
  114.  
  115. =over 4
  116.  
  117. =item
  118.  
  119. The following non-standard method may be used with a FileDialog object
  120.  
  121. =item
  122.  
  123. =head2 Show
  124.  
  125. =over 4
  126.  
  127. Displays the file dialog box for the user to operate.  Additional configuration
  128. items may be passed in at Show-time In other words, this code snippet:
  129.  
  130.   $fd->Show(-Title => 'Ooooh, Preeeeeety!');
  131.  
  132. is the same as this code snippet:
  133.  
  134.   $fd->configure(-Title => 'Ooooh, Preeeeeety!');
  135.   $fd->Show;
  136.  
  137.  
  138. =back
  139.  
  140. =back
  141.  
  142. =head1 CONFIGURATION
  143.  
  144. Any of the following configuration items may be set via the configure (or Show) method,
  145. or retrieved via the cget method.
  146.  
  147. =head2 I<Flags>
  148.  
  149. Flags may be configured with either 1,'true', or 'yes' for 1, or 0, 'false', or 'no'
  150. for 0. Any portion of 'true', 'yes', 'false', or 'no' may be used, and case does not
  151. matter.
  152.  
  153. =over 4
  154.  
  155. =item
  156.  
  157. =head2 -Chdir
  158.  
  159. =over 8
  160.  
  161. =item
  162.  
  163. Enable the user to change directories. The default is 1. If disabled, the directory
  164. list box will not be shown.
  165.  
  166. =back
  167.  
  168. =head2 -Create
  169.  
  170. =over 8
  171.  
  172. =item
  173.  
  174. Enable the user to specify a file that does not exist. If not enabled, and the user
  175. specifies a non-existent file, a dialog box will be shown informing the user of the
  176. error (This Dialog Box is configurable via the EDlg* switches, below).
  177.  
  178. default: 1
  179.  
  180. =back
  181.  
  182. =head2 -ShowAll
  183.  
  184. =over 8
  185.  
  186. =item
  187.  
  188. Determines whether hidden files (.*) are displayed in the File and Directory Listboxes.
  189. The default is 0. The Show All Checkbox reflects the setting of this switch.
  190.  
  191. =back
  192.  
  193. =head2 -DisableShowAll
  194.  
  195. =over 8
  196.  
  197. =item
  198.  
  199. Disables the ability of the user to change the status of the ShowAll flag. The default
  200. is 0 (the user is by default allowed to change the status).
  201.  
  202. =back
  203.  
  204. =head2 -Grab
  205.  
  206. =over 8
  207.  
  208. =item
  209.  
  210. Enables the File Dialog to do an application Grab when displayed. The default is 1.
  211.  
  212. =back
  213.  
  214. =head2 -Horiz
  215.  
  216. =over 8
  217.  
  218. =item
  219.  
  220. True sets the File List box to be to the right of the Directory List Box. If 0, the
  221. File List box will be below the Directory List box. The default is 1.
  222.  
  223. =back
  224.  
  225. =head2 -SelDir
  226.  
  227. =over 8
  228.  
  229. =item
  230.  
  231. If True, enables selection of a directory rather than a file, and disables the
  232. actions of the File List Box. The default is 0.
  233.  
  234. =back
  235.  
  236. =back
  237.  
  238. =head2 I<Special>
  239.  
  240. =over 4
  241.  
  242. =item
  243.  
  244. =head2 -FPat
  245.  
  246. =over 8
  247.  
  248. =item
  249.  
  250. Sets the default file selection pattern. The default is '*'. Only files matching
  251. this pattern will be displayed in the File List Box.
  252.  
  253. =back
  254.  
  255. =head2 -Geometry
  256.  
  257. =over 8
  258.  
  259. =item
  260.  
  261. Sets the geometry of the File Dialog. Setting the size is a dangerous thing to do.
  262. If not configured, or set to '', the File Dialog will be centered.
  263.  
  264. =back
  265.  
  266. =head2 -SelHook
  267.  
  268. =over 8
  269.  
  270. =item
  271.  
  272. SelHook is configured with a reference to a routine that will be called when a file
  273. is chosen. The file is called with a sole parameter of the full path and file name
  274. of the file chosen. If the Create flag is disabled (and the user is not allowed
  275. to specify new files), the file will be known to exist at the time that SelHook is
  276. called. Note that SelHook will also be called with directories if the SelDir Flag
  277. is enabled, and that the FileDialog box will still be displayed. The FileDialog box
  278. should B<not> be destroyed from within the SelHook routine, although it may generally
  279. be configured.
  280.  
  281. SelHook routines return 0 to reject the selection and allow the user to reselect, and
  282. any other value to accept the selection. If a SelHook routine returns non-zero, the
  283. FileDialog will immediately be withdrawn, and the file will be returned to the caller.
  284.  
  285. There may be only one SelHook routine active at any time. Configuring the SelHook
  286. routine replaces any existing SelHook routine. Configuring the SelHook routine with
  287. 0 removes the SelHook routine. The default SelHook routine is undef.
  288.  
  289. =back
  290.  
  291. =back
  292.  
  293. =head2 I<Strings>
  294.  
  295. The following two switches may be used to set default variables, and to get final
  296. values after the Show method has returned (but has not been explicitly destroyed
  297. by the caller)
  298.  
  299. =over 4
  300.  
  301. =item
  302.  
  303. B<-File>  The file selected, or the default file. The default is ''.
  304.  
  305. B<-Path>  The path of the selected file, or the initial path. The default is $ENV{'HOME'}.
  306.  
  307. =back
  308.  
  309. =head2 I<Labels and Captions>
  310.  
  311. For support of internationalization, the text on any of the subwidgets may be
  312. changed.
  313.  
  314. =over 4
  315.  
  316. =item
  317.  
  318. B<-Title>  The Title of the dialog box. The default is 'Select File:'.
  319.  
  320. B<-DirLBCaption>  The Caption above the Directory List Box. The default is 'Directories'.
  321.  
  322. B<-FileLBCaption>  The Caption above the File List Box. The default is 'Files'.
  323.  
  324. B<-FileEntryLabel>  The label to the left of the File Entry. The Default is 'Filename:'.
  325.  
  326. B<-PathEntryLabel>  The label to the left of the Path Entry. The default is 'Pathname:'.
  327.  
  328. B<-FltEntryLabel>  The label to the left of the Filter entry. The default is 'Filter:'.
  329.  
  330. B<-ShowAllLabel>  The text of the Show All Checkbutton. The default is 'Show All'.
  331.  
  332. =back
  333.  
  334. =head2 I<Button Text>
  335.  
  336. For support of internationalization, the text on the three buttons may be changed.
  337.  
  338. =over 4
  339.  
  340. =item
  341.  
  342. B<-OKButtonLabel>  The text for the OK button. The default is 'OK'.
  343.  
  344. B<-RescanButtonLabel>  The text for the Rescan button. The default is 'Rescan'.
  345.  
  346. B<-CancelButtonLabel>  The text for the Cancel button. The default is 'Cancel'.
  347.  
  348. =back
  349.  
  350. =head2 I<Error Dialog Switches>
  351.  
  352. If the Create switch is set to 0, and the user specifies a file that does not exist,
  353. a dialog box will be displayed informing the user of the error. These switches allow
  354. some configuration of that dialog box.
  355.  
  356. =over 4
  357.  
  358. =item
  359.  
  360. =head2 -EDlgTitle
  361.  
  362. =over 8
  363.  
  364. =item
  365.  
  366. The title of the Error Dialog Box. The default is 'File does not exist!'.
  367.  
  368. =back
  369.  
  370. =head2 -EDlgText
  371.  
  372. =over 8
  373.  
  374. =item
  375.  
  376. The message of the Error Dialog Box. The variables $path, $file, and $filename
  377. (the full path and filename of the selected file) are available. The default
  378. is I<"You must specify an existing file.\n(\$filename not found)">
  379.  
  380. =back
  381.  
  382. =back
  383.  
  384. =head1 Author
  385.  
  386. B<Brent B. Powers, Merrill Lynch (B2Pi)>
  387.  
  388. powers@ml.com
  389.  
  390. This code may be distributed under the same conditions as Perl itself.
  391.  
  392. =cut
  393.  
  394. package Tk::FileDialog;
  395.  
  396. require 5.002;
  397. use Tk;
  398. use Tk::Dialog;
  399. use Carp;
  400. use strict;
  401.  
  402. @Tk::FileDialog::ISA = qw(Tk::Toplevel);
  403.  
  404. Construct Tk::Widget 'FileDialog';
  405.  
  406. ### Global Variables (Convenience only)
  407. my(@topPack) = (-side => 'top', -anchor => 'center');
  408. my(@rightPack) = (-side => 'right', -anchor => 'center');
  409. my(@leftPack) = (-side => 'left', -anchor => 'center');
  410. my(@xfill) = (-fill => 'x');
  411. my(@yfill) = (-fill => 'y');
  412. my(@bothFill) = (-fill => 'both');
  413. my(@expand) = (-expand => 1);
  414. my(@raised) = (-relief => 'raised');
  415.  
  416. sub Populate {
  417.     ## File Dialog constructor, inherits new from Toplevel
  418.     my($FDialog, @args) = @_;
  419.  
  420.     $FDialog->SUPER::Populate(@args);
  421.  
  422.     $FDialog->withdraw;
  423.  
  424.     $FDialog->protocol('WM_DELETE_WINDOW' => sub {
  425.     if (defined($FDialog->{'Can'}) && $FDialog->{'Can'}->IsWidget ) {
  426.         $FDialog->{'Can'}->invoke;
  427.     }
  428.     });
  429.     $FDialog->transient($FDialog->toplevel);
  430.  
  431.     ## Initialize variables that won't be initialized later
  432.     $FDialog->{'Retval'} = -1;
  433.     $FDialog->{'DFFrame'} = 0;
  434.  
  435.     $FDialog->{Configure}{-Horiz} = 1;
  436.  
  437.     $FDialog->BuildFDWindow;
  438.     $FDialog->{'activefore'} = $FDialog->{'SABox'}->cget(-foreground);
  439.     $FDialog->{'inactivefore'} = $FDialog->{'SABox'}->cget(-disabledforeground);
  440.  
  441.     $FDialog->ConfigSpecs(-Chdir        => ['PASSIVE', undef, undef, 1],
  442.               -Create        => ['PASSIVE', undef, undef, 1],
  443.               -DisableShowAll    => ['PASSIVE', undef, undef, 0],
  444.               -FPat            => ['PASSIVE', undef, undef, '*'],
  445.               -File            => ['PASSIVE', undef, undef, ''],
  446.               -Geometry        => ['PASSIVE', undef, undef, undef],
  447.               -Grab            => ['PASSIVE', undef, undef, 1],
  448.               -Horiz        => ['PASSIVE', undef, undef, 1],
  449.               -Path            => ['PASSIVE', undef, undef, "$ENV{'HOME'}"],
  450.               -SelDir        => ['PASSIVE', undef, undef, 0],
  451.               -DirLBCaption        => ['PASSIVE', undef, undef, 'Directories:'],
  452.               -FileLBCaption    => ['PASSIVE', undef, undef, 'File:'],
  453.               -FileEntryLabel    => ['METHOD', undef, undef, 'Filename:'],
  454.               -PathEntryLabel    => ['METHOD', undef, undef, 'Pathname:'],
  455.               -FltEntryLabel    => ['METHOD', undef, undef, 'Filter:'],
  456.               -ShowAllLabel        => ['METHOD', undef, undef, 'ShowAll'],
  457.               -OKButtonLabel    => ['METHOD', undef, undef, 'OK'],
  458.               -RescanButtonLabel    => ['METHOD', undef, undef, 'Rescan'],
  459.               -CancelButtonLabel    => ['METHOD', undef, undef, 'Cancel'],
  460.               -SelHook        => ['PASSIVE', undef, undef, undef],
  461.               -ShowAll        => ['PASSIVE', undef, undef, 0],
  462.               -Title        => ['PASSIVE', undef, undef, "Select File:"],
  463.               -EDlgTitle        => ['PASSIVE', undef, undef,
  464.                            'File does not exist!'],
  465.               -EDlgText        => ['PASSIVE', undef, undef,
  466.                             "You must specify an existing file.\n"
  467.                             . "(\$filename not found)"]);
  468. }
  469.  
  470.  
  471. ### A few methods for configuration
  472. sub OKButtonLabel {
  473.     &SetButton('OK',@_);
  474. }
  475. sub RescanButtonLabel {
  476.     &SetButton('Rescan',@_);
  477. }
  478. sub CancelButtonLabel {
  479.     &SetButton('Can',@_);
  480. }
  481.  
  482. sub SetButton {
  483.     my($widg, $self, $title) = @_;
  484.     if (defined($title)) {
  485.     ## This is a configure
  486.     $self->{$widg}->configure(-text => $title);
  487.     }
  488.     ## Return the current value
  489.     $self->{$widg}->cget(-text);
  490. }
  491.  
  492. sub FileEntryLabel {
  493.     &SetLabel('FEF', @_);
  494. }
  495. sub PathEntryLabel {
  496.     &SetLabel('PEF', @_);
  497. }
  498. sub FltEntryLabel {
  499.     &SetLabel('patFrame', @_);
  500. }
  501. sub ShowAllLabel {
  502.     &SetButton('SABox', @_);
  503. }
  504. sub SetLabel {
  505.     my($widg, $self, $title) = @_;
  506.     if (defined($title)) {
  507.     ## This is a configure
  508.     $self->{$widg}->{'Label'}->configure(-text => $title);
  509.     }
  510.     ## Return the current value
  511.     $self->{$widg}->{'Label'}->cget(-text);
  512. }
  513.  
  514. sub SetFlag {
  515.     ## Set the given flag to either 1 or 0, as appropriate
  516.     my($self, $flag, $dflt) = @_;
  517.  
  518.     $flag = "-$flag";
  519.  
  520.     ## We know it's defined as there was a ConfigDefault call after the Populate
  521.     ## call.  Therefore, all we have to do is parse the non-numerics
  522.     if (&IsNum($self->{Configure}{$flag})) {
  523.     $self->{Configure}{$flag} = 1 unless $self->{Configure}{$flag} == 0;
  524.     } else {
  525.     my($val) = $self->{Configure}{$flag};
  526.  
  527.     my($fc) = lc(substr($val,0,1));
  528.  
  529.     if (($fc eq 'y') || ($fc eq 't')) {
  530.         $val = 1;
  531.     } elsif (($fc eq 'n') || ($fc eq 'f')) {
  532.         $val = 0;
  533.     } else {
  534.         ## bad value, complain about it
  535.         carp ("\"$val\" is not a valid flag ($flag)!");
  536.         $dflt = 0 if !defined($dflt);
  537.         $val = $dflt;
  538.     }
  539.     $self->{Configure}{$flag} = $val;
  540.     }
  541.     return $self->{Configure}{$flag};
  542. }
  543.  
  544. sub Show {
  545.     my ($self) = shift;
  546.  
  547.     $self->configure(@_);
  548.  
  549.     ## Clean up flag variables
  550.     $self->SetFlag('Chdir');
  551.     $self->SetFlag('Create');
  552.     $self->SetFlag('ShowAll');
  553.     $self->SetFlag('DisableShowAll');
  554.     $self->SetFlag('Horiz');
  555.     $self->SetFlag('Grab');
  556.     $self->SetFlag('SelDir');
  557.  
  558.     ## Set up, or remove, the directory box
  559.     &BuildListBoxes($self);
  560.  
  561.     ## Enable, or disable, the show all box
  562.     if ($self->{Configure}{-DisableShowAll}) {
  563.     $self->{'SABox'}->configure(-state => 'disabled');
  564.     } else {
  565.     $self->{'SABox'}->configure(-state => 'normal');
  566.     }
  567.  
  568.     ## Enable or disable the file entry box
  569.     if ($self->{Configure}{-SelDir}) {
  570.     $self->{Configure}{-File} = '';
  571.     $self->{'FileEntry'}->configure(-state => 'disabled',
  572.                     -foreground => $self->{'inactivefore'});
  573.     $self->{'FileList'}->configure(-selectforeground => $self->{'inactivefore'});
  574.     $self->{'FileList'}->configure(-foreground => $self->{'inactivefore'});
  575.     } else {
  576.     $self->{'FileEntry'}->configure(-state => 'normal',
  577.                     -foreground => $self->{'activefore'});
  578.     $self->{'FileList'}->configure(-selectforeground => $self->{'activefore'});
  579.     $self->{'FileList'}->configure(-foreground => $self->{'activefore'});
  580.     }
  581.  
  582.     ## Set the title
  583.     $self->title($self->{Configure}{-Title});
  584.  
  585.     ## Create window position (Center unless configured)
  586.     $self->update;
  587.     if (defined($self->{Configure}{-Geometry})) {
  588.     $self->geometry($self->{Configure}{-Geometry});
  589.     } else {
  590.     my($x,$y);
  591.     $x = int(($self->screenwidth - $self->reqwidth)/2 - $self->parent->vrootx);
  592.     $y = int(($self->screenheight - $self->reqheight)/2 - $self->parent->vrooty);
  593.     $self->geometry("+$x+$y");
  594.     }
  595.  
  596.     ## Fill the list boxes
  597.     &RescanFiles($self);
  598.     ## Restore the window, and go
  599.     $self->update;
  600.     $self->deiconify;
  601.  
  602.     ## Set up the grab
  603.     $self->grab if ($self->{Configure}{-Grab});
  604.  
  605.     ## Initialize status variables
  606.     $self->{'Retval'} = 0;
  607.     $self->{'RetFile'} = "";
  608.  
  609.     my($i) = 0;
  610.     while (!$i) {
  611.     $self->tkwait('variable',\$self->{'Retval'});
  612.     $i = $self->{'Retval'};
  613.     if ($i != -1) {
  614.         ## No cancel, so call the hook if it's defined
  615.         if (defined($self->{Configure}{-SelHook})) {
  616.         ## The hook returns 0 to ignore the result,
  617.         ## non-zero to accept.  Must release the grab before calling
  618.         $self->grab('release') if (defined($self->grab('current')));
  619.  
  620.         $i = &{$self->{Configure}{-SelHook}}($self->{'RetFile'});
  621.  
  622.         $self->grab if ($self->{Configure}{-Grab});
  623.         }
  624.     } else {
  625.         $self->{'RetFile'} = undef;
  626.     }
  627.     }
  628.  
  629.     $self->grab('release') if (defined($self->grab('current')));
  630.  
  631.     $self->withdraw;
  632.  
  633.     return $self->{'RetFile'};
  634. }
  635.  
  636. ####  PRIVATE METHODS AND SUBROUTINES ####
  637. sub IsNum {
  638.     my($parm) = @_;
  639.     my($warnSave) = $;
  640.     $ = 0;
  641.     my($res) = (($parm + 0) eq $parm);
  642.     $ = $warnSave;
  643.     return $res;
  644. }
  645.  
  646. sub BuildListBox {
  647.     my($self, $fvar, $flabel, $listvar,$hpack, $vpack) = @_;
  648.  
  649.     ## Create the subframe
  650.     $self->{"$fvar"} = $self->{'DFFrame'}->Frame
  651.         ->pack(-side => $self->{Configure}{-Horiz} ? $hpack : $vpack,
  652.            -anchor => 'center',
  653.            @xfill, @expand);
  654.  
  655.     ## Create the label
  656.     $self->{"$fvar"}->Label(@raised, -text => "$flabel")
  657.         ->pack(@topPack, @xfill);
  658.  
  659.     ## Create the frame for the list box
  660.     my($fbf) = $self->{"$fvar"}->Frame
  661.         ->pack(@topPack, @bothFill, @expand);
  662.  
  663.     ## And the scrollbar and listbox in it
  664.     $self->{"$listvar"} = $fbf->Listbox(@raised, -exportselection => 0)
  665.         ->pack(@leftPack, @expand, @bothFill);
  666.  
  667.     $fbf->AddScrollbars($self->{"$listvar"});
  668.     $fbf->configure(-scrollbars => 'se');
  669. }
  670.  
  671. sub BindDir {
  672.     ### Set up the bindings for the directory selection list box
  673.     my($self) = @_;
  674.  
  675.     my($lbdir) = $self->{'DirList'};
  676.     $lbdir->bind("<Double-1>" => sub {
  677.     my($np) = $lbdir->curselection;
  678.     return if !defined($np);
  679.     $np = $lbdir->get($np);
  680.     if ($np eq "..") {
  681.         ## Moving up one directory
  682.         $_ = $self->{Configure}{-Path};
  683.         chop if m!/$!;
  684.         s!(.*/)[^/]*$!$1!;
  685.         $self->{Configure}{-Path} = $_;
  686.     } else {
  687.         ## Going down into a directory
  688.         $self->{Configure}{-Path} .= "/" . "$np/";
  689.     }
  690.     $self->{Configure}{-Path} =~ s!//*!/!g;
  691.     \&RescanFiles($self);
  692.     });
  693. }
  694.  
  695. sub BindFile {
  696.     ### Set up the bindings for the file selection list box
  697.     my($self) = @_;
  698.  
  699.     ## A single click selects the file...
  700.     $self->{'FileList'}->bind("<ButtonRelease-1>", sub {
  701.     if (!$self->{Configure}{-SelDir}) {
  702.         $self->{Configure}{-File} =
  703.             $self->{'FileList'}->get($self->{'FileList'}->curselection);
  704.     }
  705.     });
  706.     ## A double-click selects the file for good
  707.     $self->{'FileList'}->bind("<Double-1>", sub {
  708.     if (!$self->{Configure}{-SelDir}) {
  709.         my($f) = $self->{'FileList'}->curselection;
  710.         return if !defined($f);
  711.         $self->{'File'} = $self->{'FileList'}->get($f);
  712.         $self->{'OK'}->invoke;
  713.     }
  714.     });
  715.     $self->{'FileList'}->configure(-selectforeground => 'blue');
  716. }
  717.  
  718. sub BuildEntry {
  719.     ### Build the entry, label, and frame indicated.  This is a
  720.     ### convenience routine to avoid duplication of code between
  721.     ### the file and the path entry widgets
  722.     my($self, $LabelVar, $entry) = @_;
  723.     $LabelVar = "-$LabelVar";
  724.  
  725.     ## Create the entry frame
  726.     my $eFrame = $self->Frame(@raised)
  727.         ->pack(@topPack, @xfill);
  728.  
  729.     ## Now create and pack the title and entry
  730.     $eFrame->{'Label'} = $eFrame->Label(@raised)
  731.         ->pack(@leftPack);
  732.  
  733.     $self->{"$entry"} = $eFrame->Entry(@raised,
  734.                      -textvariable => \$self->{Configure}{$LabelVar})
  735.         ->pack(@rightPack, @expand, @xfill);
  736.  
  737.     $self->{"$entry"}->bind("<Return>",sub {
  738.     &RescanFiles($self);
  739.     $self->{'OK'}->focus;
  740.     });
  741.  
  742.     return $eFrame;
  743. }
  744.  
  745. sub BuildListBoxes {
  746.     my($self) = shift;
  747.  
  748.     ## Destroy both, if they're there
  749.     if ($self->{'DFFrame'} && $self->{'DFFrame'}->IsWidget) {
  750.     $self->{'DFFrame'}->destroy;
  751.     }
  752.  
  753.     $self->{'DFFrame'} = $self->Frame;
  754.     $self->{'DFFrame'}->pack(-before => $self->{'FEF'},
  755.                  @topPack, @bothFill, @expand);
  756.  
  757.     ## Build the file window before the directory window, even
  758.     ## though the file window is below the directory window, we'll
  759.     ## pack the directory window before.
  760.     &BuildListBox($self, 'FileFrame',
  761.           $self->{Configure}{-FileLBCaption},
  762.           'FileList','right','bottom');
  763.     ## Set up the bindings for the file list
  764.     &BindFile($self);
  765.  
  766.     if ($self->{Configure}{-Chdir}) {
  767.     &BuildListBox($self,'DirFrame',$self->{Configure}{-DirLBCaption},
  768.               'DirList','left','top');
  769.     &BindDir($self);
  770.     }
  771. }
  772.  
  773. sub BuildFDWindow {
  774.     ### Build the entire file dialog window
  775.     my($self) = shift;
  776.  
  777.     ### Build the filename entry box
  778.     $self->{'FEF'} = &BuildEntry($self, 'File', 'FileEntry');
  779.  
  780.     ### Build the pathname directory box
  781.     $self->{'PEF'} = &BuildEntry($self, 'Path','DirEntry');
  782.  
  783.     ### Now comes the multi-part frame
  784.     my $patFrame = $self->Frame(@raised)
  785.         ->pack(@topPack, @xfill);
  786.  
  787.     ## Label first...
  788.     $self->{'patFrame'}->{'Label'} = $patFrame->Label(@raised)
  789.         ->pack(@leftPack);
  790.  
  791.     ## Now the entry...
  792.     $patFrame->Entry(@raised, -textvariable => \$self->{Configure}{-FPat})
  793.         ->pack(@leftPack, @expand, @xfill)
  794.             ->bind("<Return>",sub {\&RescanFiles($self);});
  795.  
  796.  
  797.     ## and the radio box
  798.     $self->{'SABox'} = $patFrame->Checkbutton(-variable => \$self->{Configure}{-ShowAll},
  799.                          -command => sub {\&RescanFiles($self);})
  800.         ->pack(@leftPack);
  801.  
  802.     ### FINALLY!!! the button frame
  803.     my $butFrame = $self->Frame(@raised);
  804.     $butFrame->pack(@topPack, @xfill);
  805.  
  806.     $self->{'OK'} = $butFrame->Button(-command => sub {
  807.     \&GetReturn($self);
  808.     })
  809.         ->pack(@leftPack, @expand, @xfill);
  810.  
  811.     $self->{'Rescan'} = $butFrame->Button(-command => sub {
  812.     \&RescanFiles($self);
  813.     })
  814.         ->pack(@leftPack, @expand, @xfill);
  815.  
  816.     $self->{'Can'} = $butFrame->Button(-command => sub {
  817.     $self->{'Retval'} = -1;
  818.     })
  819.         ->pack(@leftPack, @expand, @xfill);
  820. }
  821.  
  822. sub RescanFiles {
  823.     ### Fill the file and directory boxes
  824.     my($self) = shift;
  825.  
  826.     my($fl) = $self->{'FileList'};
  827.     my($dl) = $self->{'DirList'};
  828.     my($path) = $self->{Configure}{-Path};
  829.     my($show) = $self->{Configure}{-ShowAll};
  830.     my($chdir) = $self->{Configure}{-Chdir};
  831.  
  832.     ### Remove a final / if it is there, and add it
  833.     $path = '' if !defined($path);
  834.     if ((length($path) == 0) || (substr($path,-1,1) ne '/')) {
  835.     $path .= '/';
  836.     $self->{Configure}{-Path} = $path;
  837.     }
  838.     ### path now has a trailing / no matter what
  839.     if (!-d $path) {
  840.     carp "$path is NOT a directory\n";
  841.     return 0;
  842.     }
  843.  
  844.     $self->configure(-cursor => 'watch');
  845.     my($OldGrab) = $self->grab('current');
  846.     $self->{'OK'}->grab;
  847.     $self->{'OK'}->configure(-state => 'disabled');
  848.     $self->update;
  849.     opendir(ALLFILES,$path);
  850.     my(@allfiles) = readdir(ALLFILES);
  851.     closedir(ALLFILES);
  852.  
  853.     my($direntry);
  854.  
  855.     ## First, get the directories...
  856.     if ($chdir) {
  857.     $dl->delete(0,'end');
  858.     foreach $direntry (sort @allfiles) {
  859.         next if !-d "$path$direntry";
  860.         next if $direntry eq ".";
  861.         if (   !$show
  862.         && (substr($direntry,0,1) eq ".")
  863.         && $direntry ne "..") {
  864.         next;
  865.         }
  866.         $dl->insert('end',$direntry);
  867.     }
  868.     }
  869.  
  870.     ## Now, get the files
  871.     $fl->delete(0,'end');
  872.  
  873.     $_ = $self->{Configure}{-FPat};
  874.     s/^\s*|\s*$//;
  875.     $_ = $self->{Configure}{-FPat} = '*' if $_ eq '';
  876.  
  877.     my($pat) = $_;
  878.     undef @allfiles;
  879.  
  880.     @allfiles = <$path.$pat> if $show;
  881.  
  882.     @allfiles = (@allfiles, <$path$pat>);
  883.  
  884.     foreach $direntry (sort @allfiles) {
  885.     if (-f "$direntry") {
  886.         $direntry =~ s!.*/([^/]*)$!$1!;
  887.         $fl->insert('end',$direntry);
  888.     }
  889.     }
  890.     $self->configure(-cursor => 'top_left_arrow');
  891.  
  892.     $self->{'OK'}->grab('release') if $self->grab('current') == $self->{'OK'};
  893.     $OldGrab->grab if defined($OldGrab);
  894.     $self->{'OK'}->configure(-state => 'normal');
  895.     $self->update;
  896.     return 1;
  897. }
  898.  
  899. sub GetReturn {
  900.     my ($self) = @_;
  901.  
  902.     ## Construct the filename
  903.     my $path = $self->{Configure}{-Path};
  904.     my $fname;
  905.  
  906.     $path .= "/" if (substr($path, -1, 1) ne '/');
  907.  
  908.     if ($self->{Configure}{-SelDir}) {
  909.     $fname = $self->{'DirList'};
  910.  
  911.     if (defined($fname->curselection)) {
  912.         $fname = $fname->get($fname->curselection);
  913.     } else {
  914.         $fname = '';
  915.     }
  916.     $fname = $path . $fname;
  917.     $fname =~ s/\/$//;
  918.     } else {
  919.     $fname = $path . $self->{Configure}{-File};
  920.     ## Make sure that the file exists, if the user is not allowed
  921.     ## to create
  922.     if (!$self->{Configure}{-Create} && !(-f $fname)) {
  923.         ## Put up no create dialog
  924.         my($path) = $self->{Configure}{-Path};
  925.         my($file) = $self->{Configure}{-File};
  926.         my($filename) = $fname;
  927.         eval "\$fname = \"$self->{Configure}{-EDlgText}\"";
  928.         $self->Dialog(-title => $self->{Configure}{-EDlgTitle},
  929.               -text => $fname,
  930.               -bitmap => 'error')
  931.             ->Show;
  932.         ## And return
  933.         return;
  934.     }
  935.     }
  936.  
  937.     $self->{'RetFile'} = $fname;
  938.  
  939.     $self->{'Retval'} = 1;
  940.  
  941. }
  942.  
  943. ### Return 1 to the calling  use statement ###
  944. 1;
  945. ### End of file FileDialog.pm ###
  946. __END__
  947. From  powers@swaps.ml.com  Fri Mar  1 07:49:17 1996 
  948. Return-Path: <powers@swaps.ml.com> 
  949. From: powers@swaps.ml.com (Brent B. Powers Swaps Programmer X2293)
  950. Date: Fri, 1 Mar 1996 02:48:31 -0500 
  951. Message-Id: <199603010748.CAA16488@swapsdvlp02.ny-swaps-develop.ml.com> 
  952. To: nik@tiuk.ti.com 
  953. Cc: ringger@cs.rochester.edu, powers@ml.com 
  954. Subject: New FileDialog widget 
  955. P-From: "Brent B. Powers Swaps Programmer x2293" <powers@swaps.ml.com> 
  956.  
  957. This one's a new and improved version of FileDialog.pm.  My bus error
  958. problem was finally solved via perl5.002gamma, so all now should work
  959. properly.   Could you please let me know that you did get
  960. this... We're having some trouble with mail gateways.
  961.  
  962. Cheers.
  963.  
  964.  
  965.  
  966.  
  967. Brent B. Powers             Merrill Lynch          powers@swaps.ml.com
  968.