File Coverage

blib/lib/Tk/RemoteFileSelect.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Tk::RemoteFileSelect;
2             $VERSION=0.60;
3             my $RCSRevKey = '$Revision: 1.13 $';
4             $RCSRevKey =~ /Revision: (.*?) /;
5 1     1   5344 use vars qw($VERSION @EXPORT_OK);
  1         1  
  1         107  
6             @EXPORT_OK = qw(glob_to_re);
7              
8             =head1 NAME
9              
10             RemoteFileSelect.pm--Browse directories with FTP.
11              
12             =head1 SYNOPSIS
13              
14             use Tk::RemoteFileSelect;
15              
16             my $dialog; # Reference to RemoteFileSelect widget.
17             my $file; # File selected in the widget.
18              
19             $dialog = $mw -> RemoteFileSelect( -directory => '.' );
20             $file = $dialog -> Show;
21              
22             =head1 DESCRIPTION
23              
24             A RemoteFileSelect dialog displays list boxes for subdirectories and
25             files, a directory entry and a file name entry, and buttons for each
26             of the widget's operations.
27              
28             Tk::RemoteFileSelect -> Show () returns the file's path name when
29             clicking on the, "Accept," button. Show () also returns a file name
30             after pressing, "Enter," after typing a name in the file entry, or after
31             double clicking on a file name.
32              
33             If Net::FTP is installed, RemoteFileSelect enables the, "Host,"
34             button. Clicking on, "Host," prompts for the name of a remote system,
35             and user name and password. After logging in with FTP, the dialog can
36             browse and select files using the remote FTP server.
37              
38             When selecting files without logging in to a remote system,
39             RemoteFileSelect returns the file's path like a Tk::FileSelect dialog.
40              
41             After logging in to a FTP server, however, RemoteFileSelect returns
42             path names of files in the the following format.
43              
44             host:/full-pathname-of-file
45              
46             If RemoteFileSelect cannot find and load Net::FTP, then the widget
47             behaves like a FileSelect dialog, and the, "Host," button is grayed
48             out.
49              
50             =head1 OPTIONS
51              
52             =item Name: B
53              
54             =item Switch: B<-width>
55              
56             Width of the file and directory list boxes.
57              
58             =item Name: B
59              
60             =item Switch: B<-height>
61              
62             Height of the file and directory list boxes.
63              
64             =item Name: B
65              
66             =item Switch: B<-directory>
67              
68             =item Name: B
69              
70             =item Switch: B<-initialdir>
71              
72             Specifies the initial directory when selecting files on a local system.
73              
74             =item Name: B
75              
76             =item Switch: B<-remotedirectory>
77              
78             Specifies the initial directory to start in after connecting to remote
79             hosts. Value may be a directory or a hash reference which specifies
80             host and directory; e.g.
81              
82             -remotedirectory => { host1 => /some/directory, host2 => /other/directory }
83              
84             =item Name: B
85              
86             =item Switch: B<-filelabel>
87              
88             Text of the file entry label.
89              
90             =item Name: B
91              
92             =item Switch: B<-filelistlabel>
93              
94             Text of the file list box title.
95              
96             =item Name: B
97              
98             =item Switch: B<-filter>
99              
100             Display file names that match the file glob pattern of the value.
101              
102             =item Name: B
103              
104             =item Switch: B<-hostname>
105              
106             Initial value of the remote host name.
107              
108             =item Name: B
109              
110             =item Switch: B<-transcript>
111              
112             Print a transcript of the FTP session to standard output.
113              
114             =item Name: B
115              
116             =item Switch: B<-userid>
117              
118             The initial value of the remote user ID.
119              
120             =item Name: B
121              
122             =item Switch: B<-dirlistlabel>
123              
124             Text of the directory list title.
125              
126             =item Name: B
127              
128             =item Switch: B<-dirlabel>
129              
130             Text of the directory entry label.
131              
132             =item Name: B
133              
134             =item Switch: B<-acceptlabel>
135              
136             Text of the Accept button label.
137              
138             =item Name: B
139              
140             =item Switch: B<-hostlabel>
141              
142             Text of the Host button label.
143              
144             =item Name: B
145              
146             =item Switch: B<-cancellabel>
147              
148             Text of the Cancel button label.
149              
150             =item Name: B
151              
152             =item Switch: B<-resetlabel>
153              
154             Text of the Reset button label.
155              
156             =item Name: B
157              
158             =item Switch: B<-homelabel>
159              
160             Text of the Home button label.
161              
162             =item Name: B
163              
164             =item Switch: B<-uidlabel>
165              
166             Text of the user name entry label.
167              
168             =item Name: B
169              
170             =item Switch: B<-pwdlabel>
171              
172             Text of the password entry label.
173              
174             =item Name: B
175              
176             =item Switch: B<-verify>
177              
178             The value is an anonymous array that contains the operators for file
179             verification, normally qw(! -d -w).
180              
181             =head1 BUGS
182              
183             The -initialfile, -userid, and -password values do not appear in the
184             entry widgets.
185              
186             Older versions of Net::FTP (for example, the version distributed with
187             Perl 5.6.1), can cause Perl/Tk windows to freeze when changing to a
188             subdirectory of a directory referred to by a symbolic link.
189              
190             =head1 VERSION INFO
191              
192             Version 0.60.
193              
194             =head1 CREDITS
195              
196             Robert Kiesling , based on Tk::FileSelect.
197              
198             Persistent login information added by dougw@cpan.org.
199              
200             Licensed under the same terms as Perl. Refer to the file, "Artistic,"
201             for information.
202              
203             =head1 SEE ALSO
204              
205             L, L
206              
207             =cut
208              
209 1     1   1294 use Tk qw(Ev);
  0            
  0            
210             use Tk::CmdLine;
211             use strict;
212             use Carp;
213             use base qw(Tk::Toplevel);
214             use Tk::widgets qw(LabEntry Button Frame Listbox Scrollbar);
215             use File::Basename;
216              
217             my $font="*-helvetica-medium-r-*-*-12-*";
218              
219             Construct Tk::Widget 'RemoteFileSelect';
220              
221             use vars qw(%error_text);
222             %error_text = (
223             '-r' => 'is not readable by effective uid/gid',
224             '-w' => 'is not writeable by effective uid/gid',
225             '-x' => 'is not executable by effective uid/gid',
226             '-R' => 'is not readable by real uid/gid',
227             '-W' => 'is not writeable by real uid/gid',
228             '-X' => 'is not executable by real uid/gid',
229             '-o' => 'is not owned by effective uid/gid',
230             '-O' => 'is not owned by real uid/gid',
231             '-e' => 'does not exist',
232             '-z' => 'is not of size zero',
233             '-s' => 'does not exists or is of size zero',
234             '-f' => 'is not a file',
235             '-d' => 'is not a directory',
236             '-l' => 'is not a link',
237             '-S' => 'is not a socket',
238             '-p' => 'is not a named pipe',
239             '-b' => 'is not a block special file',
240             '-c' => 'is not a character special file',
241             '-u' => 'is not setuid',
242             '-g' => 'is not setgid',
243             '-k' => 'is not sticky',
244             '-t' => 'is not a terminal file',
245             '-T' => 'is not a text file',
246             '-B' => 'is not a binary file',
247             '-M' => 'has no modification date/time',
248             '-A' => 'has no access date/time',
249             '-C' => 'has no inode change date/time',
250             );
251              
252             Tk::CmdLine::SetResources ('*font: ' . $font);
253              
254             sub import {
255             if (defined $_[1] and $_[1] eq 'as_default') {
256             local $^W = 0;
257             package Tk;
258             *FDialog = \&Tk::RemoteFileSelect::FDialog;
259             *MotifFDialog = \&Tk::RemoteFileSelect::FDialog;
260             }
261             }
262              
263             sub Cancel
264             {
265             my ($cw) = @_;
266             $cw->{Selected} = undef;
267             my $hostname = $cw -> cget( -hostname );
268             if( $hostname ne '' ) {
269             my $ftp = $cw -> cget( -ftp );
270             $ftp -> quit if $ftp;
271             $cw -> configure( -ftp => undef,
272             -connected => '' );
273             }
274             $cw->withdraw unless $cw->cget('-transient');
275             }
276              
277             sub host {
278             my ($cw) = @_;
279             my ($hostid, $transcript, $resp);
280             my $dlg = $cw->Subwidget('hostdialog');
281             return if ( ($resp = $dlg -> Show ) =~ /Cancel/);
282             $hostid = $dlg -> Subwidget( 'hostentry' ) -> get;
283             $transcript = $cw -> cget( '-transcript' );
284             $cw -> configure( -hostname => $hostid,
285             -transcript => $transcript );
286             my $logindlg = $cw -> Subwidget('logindialog');
287             return if ( ($resp = $logindlg -> Show ) =~ /Cancel/);
288             $cw -> configure( -userid => ($logindlg -> Subwidget( 'uidentry' ) -> get),
289             -password => ($logindlg -> Subwidget( 'pwdentry' ) -> get) );
290             my $ftp = $cw -> remoteLogin( $hostid,
291             $cw -> cget( -userid ),
292             $cw -> cget( -password ),
293             $transcript );
294              
295             if( defined $ftp ) {
296              
297             my $dir;
298             my $remote_dir = $cw -> cget("-remotedirectory");
299             if (defined $remote_dir) {
300             my $rdir = ref($remote_dir)
301             ? exists($remote_dir->{$hostid})
302             ? $remote_dir->{$hostid}
303             : undef
304             : $remote_dir;
305             if (defined $rdir) {
306             if( ! $ftp -> cwd( $rdir ) ) {
307             $cw -> Error( "Cannot cwd to $rdir." );
308              
309             $dir = '';
310             }
311             }
312             }
313             $dir = $ftp -> pwd() unless $dir;
314             $cw -> remoteDirectory( $dir );
315             }
316             }
317              
318             sub remoteLogin {
319             my ($cw, $hostid, $userid, $password, $transcript) = @_;
320              
321             my $ftp = undef;
322             my $debug = ( $transcript =~ /1/ ? 1 : 0 );
323             $ftp = Net::FTP ->
324             new( $hostid,
325             Debug => $debug );
326             if( ! defined $ftp ) {
327             my $edlg = $cw -> Subwidget( 'errormessage' );
328             $edlg -> configure( -text => $@ );
329             $edlg -> Show;
330             $cw -> configure( -hostname => '',
331             -connected => '');
332             return;
333             }
334             if( $ftp -> login( $userid, $password ) ) {
335             $cw -> configure( -ftp => $ftp,
336             -connected => '1');
337             $cw->setLoginVariables( $ftp, $userid, $password );
338             } else {
339             my $edlg = $cw -> Subwidget( 'errormessage' );
340             $edlg -> configure( -text => "Error: Could not login to $hostid\." );
341             $edlg -> Show;
342             $cw -> configure( -ftp => $ftp,
343             -connected => '');
344             }
345             return $ftp;
346             }
347              
348             sub setLoginVariables {
349             my ($cw, $ftp, $userid, $password) = @_;
350             my $ftpvar = $cw->cget('-ftpvariable');
351             $$ftpvar = $ftp if $ftpvar and ref $ftpvar;
352             my $useridvar = $cw->cget('-useridvariable');
353              
354             $$useridvar = $userid if $useridvar and ref $useridvar;
355             my $passwordvar = $cw->cget('-passwordvariable');
356             $$passwordvar = $password if $passwordvar and ref
357             $useridvar;
358             }
359              
360             sub Accept {
361              
362             # Accept the file or directory name if possible.
363             my ($cw) = @_;
364              
365             my($path, $so) = ($cw->cget('-directory'), $cw->SelectionOwner);
366             my $ftp = $cw -> cget( -ftp );
367             my $leaf = undef;
368             my $leaves;
369              
370             if (defined $so and
371             $so == $cw->Subwidget('dir_list')->Subwidget('listbox')) {
372             $leaves = [$cw->Subwidget('dir_list')->getSelected];
373             $leaves = [$cw->Subwidget('dir_entry')->get] if !scalar(@$leaves);
374             } else {
375             $leaves = [$cw->Subwidget('file_list')->getSelected];
376             $leaves = [$cw->Subwidget('file_entry')->get] if !scalar(@$leaves);
377             }
378              
379             foreach $leaf (@$leaves)
380             {
381             if (defined $leaf and $leaf ne '') {
382             if (!$cw->cget('-create') || -e "$path/$leaf")
383             {
384             foreach (@{$cw->cget('-verify')}) {
385             my $r = ref $_;
386             if (defined $r and $r eq 'ARRAY') {
387             #local $_ = $leaf; # use strict var problem here
388             return
389             if not &{$_->[0]}($cw, $path, $leaf, @{$_}[1..$#{$_}]);
390             } else {
391             no warnings;
392             ###
393             ### Avoid uninitialized value errors.
394             ###
395             my $s = eval "$_ '$path/$leaf'";
396             use warnings;
397             if (not $s) {
398             my $err;
399             if (substr($_,0,1) eq '!')
400             {
401             my $t = substr($_,1);
402             if (exists $error_text{$t})
403             {
404             $err = $error_text{$t};
405             $err =~ s/\b(?:no|not) //;
406             }
407             }
408             $err = $error_text{$_} unless defined $err;
409             $err = "failed '$_' test" unless defined $err;
410             $cw->Error("'$leaf' $err.");
411             return;
412             }
413             }
414             } # forend
415             }
416             else
417             {
418             unless (-w $path)
419             {
420             $cw->Error("Cannot write to $path");
421             return;
422             }
423             }
424             if( ( $cw -> cget( -connected ) ) eq '1' ) {
425             $path = $ftp -> pwd;
426             $leaf = ($cw -> cget( -hostname ) ).":$path/$leaf";
427             } else {
428             $leaf = $path . '/' . $leaf;
429             }
430             } else {
431             $leaf = undef;
432             }
433             }
434             if (scalar(@$leaves))
435             {
436             my $sm = $cw->Subwidget('file_list')->cget(-selectmode);
437             $cw->{Selected} = $leaves;
438             my $command = $cw->cget('-command');
439             $command->Call(@{$cw->{Selected}}) if defined $command;
440             }
441             } # end Accept
442              
443             sub Accept_dir
444             {
445             my ($cw,$new) = @_;
446             my $dir = $cw->cget('-directory');
447             $cw -> SelectionClear;
448             $cw->configure(-directory => "$dir/$new");
449             }
450              
451             sub Populate {
452              
453             my ($w, $args) = @_;
454              
455             require Tk::Listbox;
456             require Tk::Button;
457             require Tk::Dialog;
458             require Tk::DialogBox;
459             require Tk::Toplevel;
460             require Tk::LabEntry;
461             require Cwd;
462              
463             my $havenet;
464             $havenet = 1 if &requirecond( "Net::FTP" );
465              
466             $w->SUPER::Populate($args);
467             $w->protocol('WM_DELETE_WINDOW' => ['Cancel', $w ]);
468              
469             $w->{'reread'} = 0;
470             $w->withdraw;
471              
472             # Create directory/filter entry, place at the top.
473             my $e = $w->Component(
474             LabEntry => 'dir_entry',
475             -textvariable => \$w->{DirectoryString},
476             -labelVariable => \$w->{Configure}{-dirlabel},
477             );
478             $e->pack(-side => 'top', -expand => 0, -pady => 5, -padx => 5,
479             -fill => 'x');
480             $e->bind('' => [$w => 'validateDir', Ev(['get'])]);
481              
482             # Create file entry, place at the bottom.
483             $e = $w->Component(
484             LabEntry => 'file_entry',
485             -textvariable => \$w->{Configure}{-initialfile},
486             -labelVariable => \$w->{Configure}{-filelabel},
487             );
488             $e->pack(-side => 'bottom', -expand => 0, -pady => 5, -padx => 5,
489             -fill => 'x');
490             $e->bind('' => [$w => 'validateFile', Ev(['get'])]);
491              
492             # Create directory scrollbox, place at the left-middle.
493             my $b = $w->Component(
494             ScrlListbox => 'dir_list',
495             -labelVariable => \$w->{Configure}{-dirlistlabel},
496             -scrollbars => 'se',
497             );
498             $b -> Subwidget('yscrollbar') -> configure(-width=>10);
499             $b -> Subwidget('xscrollbar') -> configure(-width=>10);
500             $b->pack(-side => 'left', -expand => 1, -fill => 'both');
501             $b->bind('' => [$w => 'Accept_dir', Ev(['getSelected'])]);
502              
503             my $f = $w->Frame();
504             $f->pack(-side => 'right', -fill => 'y', -expand => 0);
505             $b = $f->Button('-textvariable' => \$w->{'Configure'}{'-acceptlabel'},
506             -underline => 0,
507             -command => [ 'Accept', $w ],
508             );
509             $w -> bind( '', [$w => 'Accept', Ev(['getSelected'])]);
510             $w -> Advertise ('AcceptButton' => $b);
511              
512             $b->pack(-side => 'top', -fill => 'x', -expand => 1);
513             $b = $f->Button('-textvariable' => \$w->{'Configure'}{'-hostlabel'},
514             -underline => 0,
515             -command => [ 'host', $w ],
516             -state => ($havenet?'normal':'disabled')
517             );
518             $w -> bind( '', [$w => 'host', $w]);
519             $b->pack(-side => 'top', -fill => 'x', -expand => 1);
520             $b = $f->Button('-textvariable' => \$w->{'Configure'}{'-cancellabel'},
521             -underline => 0,
522             -command => [ 'Cancel', $w ],
523             );
524             $w -> bind( '', [$w => 'Cancel', $w]);
525             $b->pack(-side => 'top', -fill => 'x', -expand => 1);
526             $b = $f->Button('-textvariable' => \$w->{'Configure'}{'-resetlabel'},
527             -underline => 0,
528             -command => [$w => 'configure','-directory','.'],
529             );
530             $w -> bind( '', [$w => 'configure','-directory','.']);
531             $b->pack(-side => 'top', -fill => 'x', -expand => 1);
532             $b = $f->Button('-textvariable' => \$w->{'Configure'}{'-homelabel'},
533             -underline => 2,
534             -command => [$w => 'configure','-directory',$ENV{'HOME'}],
535             );
536             $w -> bind( '', [$w => 'configure','-directory',$ENV{'HOME'}]);
537             $b->pack(-side => 'top', -fill => 'x', -expand => 1);
538              
539             # Create file scrollbox, place at the right-middle.
540              
541             $b = $w->Component(
542             ScrlListbox => 'file_list',
543             -labelVariable => \$w->{Configure}{-filelistlabel},
544             -scrollbars => 'se'
545             );
546             $b -> Subwidget('yscrollbar') -> configure(-width=>10);
547             $b -> Subwidget('xscrollbar') -> configure(-width=>10);
548             $b->pack(-side => 'right', -expand => 1, -fill => 'both');
549             $b->bind('' => [$w => 'Accept']);
550              
551             # Create -very dialog.
552              
553             my $v = $w->Component(
554             Dialog => 'dialog',
555             -title => 'Verify Error',
556             -bitmap => 'error',
557             -buttons => ['Dismiss'],
558             );
559              
560             # Host dialog
561             my $h = $w -> Component(
562             DialogBox => 'hostdialog',
563             -title => 'Select Remote Host',
564             -buttons => [ 'Ok', 'Cancel' ] );
565             $h -> Component( Label => 'toplabel',
566             -text => "Enter the name or IP address of the remote host." )
567             -> pack( -expand => '1', -fill => 'x' );
568             $h -> Component( Entry => 'hostentry',
569             -textvariable => \$w -> {'Configure'}{'-hostname'},
570             ) -> pack( -expand => '1', -fill => 'x' );
571              
572             $h -> Component( Checkbutton => 'transcriptbutton',
573             -text => 'Log the session on the terminal.',
574             -variable => \$w -> {'Configure'}{'-transcript'})
575             -> pack( -anchor => 'w' );
576              
577             # login user/password dialog
578             my $l = $w -> Component(
579             DialogBox => 'logindialog',
580             -title => 'Log in.',
581             -buttons => [ 'Ok', 'Cancel' ] );
582             $l -> Component( Label => 'useridlabel',
583             -text => 'Please enter your user name and password.'
584             ) -> pack( -expand => '1', -fill => 'x' );
585             $l -> Component ( LabEntry => 'uidentry',
586             -labelVariable => \$w -> {'Configure'}{'-uidlabel'} )
587             -> pack( -anchor => 'w', -expand => '1', -fill => 'x',
588             -padx => 5, -pady => 5);
589             $l -> Component( LabEntry => 'pwdentry',
590             -labelVariable => \$w -> {'Configure'}{'-pwdlabel'},
591             -show => '*' )
592             -> pack( -anchor => 'w', -expand => '1', -fill => 'x',
593             -padx => 5, -pady => 5);
594              
595             $l = $w -> Component( Dialog => 'errormessage',
596             -title => "Network Error",
597             -bitmap => 'error' );
598              
599             $w->ConfigSpecs(
600             -width => [ ['file_list','dir_list'], undef, undef, 20 ],
601             -height => [ ['file_list','dir_list'], undef, undef, 14 ],
602             -directory => [ 'METHOD', undef, undef, '.' ],
603             -remotedirectory => ['PASSIVE', undef, undef, '.' ],
604             -initialdir => '-directory',
605             -filelabel => [ 'PASSIVE', 'fileLabel', 'FileLabel',
606             'File name' ],
607             -initialfile => [ 'PASSIVE', undef, undef, '' ],
608             -filelistlabel => [ 'PASSIVE', undef, undef, 'Files' ],
609             -filter => [ 'METHOD', undef, undef, undef ],
610             -hostname => [ 'PASSIVE', undef, undef, '' ],
611             -transcript => [ 'PASSIVE', undef, undef, '' ],
612             -userid => [ 'PASSIVE', undef, undef, '' ],
613             -ftp => [ 'PASSIVE', undef, undef, undef ],
614             -networkerror => [ 'PASSIVE', undef, undef, undef ],
615             -password => [ 'PASSIVE', undef, undef, '' ],
616             -regexp => [ 'METHOD', undef, undef, undef ],
617             -dirlistlabel => [ 'PASSIVE', undef, undef, 'Directories'],
618             -dirlabel => [ 'PASSIVE', undef, undef, 'Directory'],
619             '-accept' => [ 'CALLBACK',undef,undef, undef ],
620             -command => [ 'CALLBACK',undef,undef, undef ],
621             -transient => [ 'PASSIVE', undef, undef, 1 ],
622             -verify => [ 'PASSIVE', undef, undef, ['!-d'] ],
623             -create => [ 'PASSIVE', undef, undef, 0 ],
624             -acceptlabel => [ 'PASSIVE', undef, undef, 'Accept'],
625             -hostlabel => [ 'PASSIVE', undef, undef, 'Host'],
626             -cancellabel => [ 'PASSIVE', undef, undef, 'Cancel'],
627             -resetlabel => [ 'PASSIVE', undef, undef, 'Reset'],
628             -homelabel => [ 'PASSIVE', undef, undef, 'Home'],
629             -uidlabel => [ 'PASSIVE', undef, undef, 'User ID'],
630             -pwdlabel => [ 'PASSIVE', undef, undef, 'Password'],
631             -connected => [ 'PASSIVE', undef, undef, '' ],
632             -ftpvariable => ['PASSIVE', undef, undef, '' ],
633             -useridvariable => ['PASSIVE', undef, undef, '' ],
634             -passwordvariable => ['PASSIVE', undef, undef, ''],
635             DEFAULT => [ 'file_list' ],
636             );
637             $w->Delegates(DEFAULT => 'file_list');
638              
639             return $w;
640              
641             } # end Populate
642              
643             sub translate
644             {
645             my ($bs,$ch) = @_;
646             return "\\$ch" if (length $bs);
647             return '.*' if ($ch eq '*');
648             return '.' if ($ch eq '?');
649             return "\\." if ($ch eq '.');
650             return "\\/" if ($ch eq '/');
651             return "\\\\" if ($ch eq '\\');
652             return $ch;
653             }
654              
655             sub glob_to_re
656             {
657             my $regex = shift;
658             $regex =~ s/(\\?)(.)/&translate($1,$2)/ge;
659             return sub { shift =~ /^${regex}$/ };
660             }
661              
662             sub filter
663             {
664             my ($cw,$val) = @_;
665             my $var = \$cw->{Configure}{'-filter'};
666             if (@_ > 1 || !defined($$var))
667             {
668             $val = '*' unless defined $val;
669             $$var = $val;
670             $cw->{'match'} = glob_to_re($val) unless defined $cw->{'match'};
671             unless ($cw->{'reread'}++)
672             {
673             $cw->Busy;
674             if( ( $cw -> cget( '-connected' ) ) =~ /1/ ) {
675             $cw->afterIdle(['rereadRemote',$cw,$cw->cget('-directory')])
676             } else {
677             $cw->afterIdle(['reread',$cw,$cw->cget('-directory')])
678             }
679             }
680             }
681             return $$var;
682             }
683              
684             sub regexp
685             {
686             my ($cw,$val) = @_;
687             my $var = \$cw->{Configure}{'-regexp'};
688             if (@_ > 1)
689             {
690             $$var = $val;
691             $cw->{'match'} = sub { shift =~ m|^${val}$| };
692             unless ($cw->{'reread'}++)
693             {
694             $cw->Busy;
695             $cw->afterIdle(['reread',$cw])
696             }
697             }
698             return $$var;
699             }
700              
701             sub defaultextension
702             {
703             my ($cw,$val) = @_;
704             if (@_ > 1)
705             {
706             $val = ".$val" if ($val !~ /^\./);
707             $cw->filter("*$val");
708             }
709             else
710             {
711             $val = $cw->filter;
712             my ($ext) = $val =~ /(\.[^\.]*)$/;
713             return $ext;
714             }
715             }
716              
717             sub remoteDirectory {
718             my ($cw, $dir) = @_;
719             return if ( ($cw -> cget( -connected ) ) ne '1' );
720             my $ftp = $cw -> cget( -ftp );
721             return if( ! $ftp );
722             my $current = $ftp -> pwd;
723             my $ndir;
724             if( @_ > 1 && defined $dir ) {
725             if( $current eq $dir ) {
726             $cw->{Configure}{'-directory'} = "$dir";
727             $cw -> rereadRemote;
728             return;
729             }
730             if( ! $ftp -> cwd( "$current/$dir" ) ) {
731             $cw -> Error( "Cannot cwd to $current/$dir." );
732             $cw -> rereadRemote;
733             return;
734             }
735             $ndir = $ftp -> pwd;
736             $cw->{Configure}{'-directory'} = "$ndir";
737             $cw -> rereadRemote;
738             }
739             }
740              
741             sub directory
742             {
743             my ($cw,$dir) = @_;
744              
745             my ($var, $c);
746              
747             $c = $cw -> cget ( '-connected' );
748             if ( $c && $c =~ /1/ ) {
749             $var = \$cw->{Configure}{'-directory'};
750             $cw -> remoteDirectory( $dir );
751             $$var = $dir;
752             return $$var;
753             }
754             $var = \$cw->{Configure}{'-directory'};
755             if (@_ > 1 && defined $dir)
756             {
757             if (substr($dir,0,1) eq '~')
758             {
759             if (substr($dir,1,1) eq '/')
760             {
761             $dir = $ENV{'HOME'} . substr($dir,1);
762             }
763             else
764             {my ($uid,$rest) = ($dir =~ m#^~([^/]+)(/.*$)#);
765             $dir = (getpwnam($uid))[7] . $rest;
766             }
767             }
768             $dir =~ s#([^/\\])[\\/]+$#$1#;
769             if (-d $dir)
770             {
771             unless (Tk::tainting())
772             {
773             my $pwd = Cwd::getcwd();
774             if (chdir( (defined($dir) ? $dir : '') ) )
775             {
776             my $new = Cwd::getcwd();
777             if ($new)
778             {
779             $dir = $new;
780             }
781             else
782             {
783             carp "Cannot getcwd in '$dir'";
784             }
785             chdir($pwd) || carp "Cannot chdir($pwd) : $!";
786             $cw->{Configure}{'-directory'} = $dir;
787             }
788             else
789             {
790             $cw->BackTrace("Cannot chdir($dir) :$!");
791             }
792             }
793             $$var = $dir;
794             unless ($cw->{'reread'}++)
795             {
796             $cw->Busy;
797             $cw->afterIdle(['reread',$cw])
798             }
799             }
800             }
801             return $$var;
802             }
803              
804             sub rereadRemote {
805             my $w = shift;
806             if( ( $w -> cget( -connected ) ) eq '1' ) {
807             $w -> Busy;
808             my ($name, $filter);
809             my $dl = $w->Subwidget('dir_list');
810             $dl->delete(0, 'end');
811             my $fl = $w->Subwidget('file_list');
812             $fl->delete(0, 'end');
813             my $ftp = $w -> cget( -ftp );
814             my $dir = $ftp -> pwd;
815             my @files = $ftp -> dir;
816             $dl -> insert( 'end', '..' );
817             foreach my $f ( @files ) {
818             next if $f =~ /^total/;
819             $name = $f;
820             if ( $f =~ /^l/ ) {
821             $name =~ s/.* (.*) \-\> .*/$1/;
822             if ( $ftp -> cwd($name) ) {
823             substr($f, 0, 1) = "d";
824             if( ! $ftp -> cwd( $dir ) ) {
825             $w -> Error( "Cannot cwd to $dir." );
826             }
827             }
828             } else {
829             $name =~ s/.* //;
830             }
831             if( $f =~ /^d/ ) {
832             $dl -> insert( 'end', $name );
833             } else {
834             $fl -> insert( 'end', $name );
835             }
836             }
837             my $host = $w -> cget( '-hostname' );
838             $w -> {DirectoryString} = "$host\:$dir" . '/' . $w -> cget( '-filter' );
839             $w -> Unbusy;
840             }
841             }
842              
843             sub reread
844             {
845             my ($w) = @_;
846             my $dir = $w->cget('-directory');
847             if (defined $dir)
848             {
849             if (!defined $w->cget('-filter') or $w->cget('-filter') eq '')
850             {
851             $w->configure('-filter', '*');
852             }
853             my $dl = $w->Subwidget('dir_list');
854             $dl->delete(0, 'end');
855             my $fl = $w->Subwidget('file_list');
856             $fl->delete(0, 'end');
857             local *DIR;
858             my $h;
859             if( ( $w -> cget( -connected ) ) eq '1' ) {
860             return $w -> rereadRemote( $dir );
861             } else { # ! $w -> connected
862             if (opendir(DIR, $dir))
863             {
864             my $file = $w->cget('-initialfile');
865             my $seen = 0;
866             my $accept = $w->cget('-accept');
867             foreach my $f (sort(readdir(DIR)))
868             {
869             next if ($f eq '.');
870             my $path = "$dir/$f";
871             if (-d $path)
872             {
873             $dl->insert('end', $f);
874             }
875             else
876             {
877             if (&{$w->{match}}($f))
878             {
879             if (!defined($accept) || $accept->Call($path))
880             {
881             $seen = $fl->index('end') if ($file && $f eq $file);
882             $fl->insert('end', $f)
883             }
884             }
885             }
886             }
887             closedir(DIR);
888             if ($seen)
889             {
890             $fl->selectionSet($seen);
891             $fl->see($seen);
892             }
893             else
894             {
895             $w->configure(-initialfile => undef) unless $w->cget('-create');
896             }
897             }
898             $w->{DirectoryString} = $dir . '/' . $w->cget('-filter');
899             }
900             $w->{'reread'} = 0;
901             $w->Unbusy;
902             }
903             }
904              
905             sub validateDir
906             {
907             my ($cw,$name) = @_;
908             if( ( $cw -> cget( '-connected' ) ) =~ /1/ ) {
909             $name =~ s/^.*\://;
910             }
911             my ($leaf,$base) = fileparse($name);
912             if ($leaf =~ /[*?]/)
913             {
914             $cw->configure('-directory' => $base,'-filter' => $leaf);
915             }
916             else
917             {
918             $cw->configure('-directory' => $name);
919             }
920             }
921              
922             sub validateFile
923             {
924             my ($cw,$name) = @_;
925             my $i = 0;
926             my $n = $cw->index('end');
927             # See if it is an existing file
928             for ($i= 0; $i < $n; $i++)
929             {
930             my $f = $cw->get($i);
931             if ($f eq $name)
932             {
933             $cw->selection('set',$i);
934             $cw->Accept;
935             }
936             }
937             # otherwise allow if -create is set, directory is writable
938             # and it passes filter and accept criteria
939             if ($cw->cget('-create'))
940             {
941             my $path = $cw->cget('-directory');
942             if (-w $path)
943             {
944             if (&{$cw->{match}}($name))
945             {
946             my $accept = $cw->cget('-accept');
947             my $full = "$path/$name";
948             if (!defined($accept) || $accept->Call($full))
949             {
950             $cw->{Selected} = [$full];
951             $cw->Callback(-command => @{$cw->{Selected}});
952             }
953             else
954             {
955             $cw->Error("$name is not 'acceptable'");
956             }
957             }
958             else
959             {
960             $cw->Error("$name does not match '".$cw->cget('-filter').'\'');
961             }
962             }
963             else
964             {
965             $cw->Error("Directory '$path' is not writable");
966             return;
967             }
968             }
969             }
970              
971             sub Error
972             {
973             my $cw = shift;
974             my $msg = shift;
975             my $dlg = $cw->Subwidget('dialog');
976             $dlg->configure(-text => $msg);
977             $dlg->Show;
978             }
979              
980             sub Show
981             {
982             my ($cw,@args) = @_;
983             if ($cw->cget('-transient')) {
984             $cw->Popup(@args);
985             $cw->focus;
986             $cw->waitVariable(\$cw->{Selected});
987             $cw->withdraw;
988             return defined($cw->{Selected})
989             ? (wantarray) ? @{$cw->{Selected}} : $cw->{Selected}[0]
990             : undef;
991             } else {
992             $cw->Popup(@args);
993             }
994             }
995              
996             sub FDialog
997             {
998             my($cmd, %args) = @_;
999             if ($cmd =~ /Save/)
1000             {
1001             $args{-create} = 1;
1002             $args{-verify} = [qw(!-d -w)];
1003             }
1004             delete $args{-filetypes};
1005             delete $args{-force};
1006             Tk::DialogWrapper('FileSelect',$cmd, %args);
1007             }
1008              
1009             sub requirecond {
1010             my ($modulename) = @_;
1011             my ($filename, $fullname, $result);
1012             $filename = $modulename;
1013             $filename .= '.pm' if $filename !~ /.pm$/;
1014             $filename =~ s/\:\:/\//;
1015             foreach my $prefix ( @INC ) {
1016             $fullname = "$prefix/$filename";
1017             if( -f $fullname ) { return do $fullname; }
1018             }
1019             return 0;
1020             }
1021              
1022             1;
1023              
1024             __END__