use vars
qw($VERSION @EXPORT_OK);
$VERSION = '3.047'; # $Id: //depot/Tk8/Tk/FileSelect.pm#47 $
@EXPORT_OK = qw(glob_to_re);
use base
qw(Tk::Toplevel);
use Tk
::widgets
qw(LabEntry Button Frame Listbox Scrollbar);
Construct Tk
::Widget
'FileSelect';
use vars
qw(%error_text);
'-r' => 'is not readable by effective uid/gid',
'-w' => 'is not writeable by effective uid/gid',
'-x' => 'is not executable by effective uid/gid',
'-R' => 'is not readable by real uid/gid',
'-W' => 'is not writeable by real uid/gid',
'-X' => 'is not executable by real uid/gid',
'-o' => 'is not owned by effective uid/gid',
'-O' => 'is not owned by real uid/gid',
'-e' => 'does not exist',
'-z' => 'is not of size zero',
'-s' => 'does not exists or is of size zero',
'-d' => 'is not a directory',
'-S' => 'is not a socket',
'-p' => 'is not a named pipe',
'-b' => 'is not a block special file',
'-c' => 'is not a character special file',
'-t' => 'is not a terminal file',
'-T' => 'is not a text file',
'-B' => 'is not a binary file',
'-M' => 'has no modification date/time',
'-A' => 'has no access date/time',
'-C' => 'has no inode change date/time',
# Documentation after __END__
if (defined $_[1] and $_[1] eq 'as_default') {
*FDialog = \&Tk::FileSelect::FDialog;
*MotifFDialog = \&Tk::FileSelect::FDialog;
$cw->withdraw unless $cw->cget('-transient');
# Accept the file or directory name if possible.
my($path, $so) = ($cw->cget('-directory'), $cw->SelectionOwner);
$so == $cw->Subwidget('dir_list')->Subwidget('listbox')) {
$leaves = [$cw->Subwidget('dir_list')->getSelected];
$leaves = [$cw->Subwidget('dir_entry')->get] if !scalar(@$leaves);
$leaves = [$cw->Subwidget('file_list')->getSelected];
$leaves = [$cw->Subwidget('file_entry')->get] if !scalar(@$leaves);
if (defined $leaf and $leaf ne '') {
if (!$cw->cget('-create') || -e "$path/$leaf")
foreach (@{$cw->cget('-verify')}) {
if (defined $r and $r eq 'ARRAY') {
#local $_ = $leaf; # use strict var problem here
return if not &{$_->[0]}($cw, $path, $leaf, @{$_}[1..$#{$_}]);
my $s = eval "$_ '$path/$leaf'";
if (substr($_,0,1) eq '!')
if (exists $error_text{$t})
$err =~ s/\b(?:no|not) //;
$err = $error_text{$_} unless defined $err;
$err = "failed '$_' test" unless defined $err;
$cw->Error("'$leaf' $err.");
$cw->Error("Cannot write to $path");
$leaf = $path . '/' . $leaf;
my $sm = $cw->Subwidget('file_list')->cget(-selectmode);
$cw->{Selected} = $leaves;
my $command = $cw->cget('-command');
$command->Call(@{$cw->{Selected}}) if defined $command;
my $dir = $cw->cget('-directory');
$cw->configure(-directory => "$dir/$new");
$w->SUPER::Populate($args);
$w->protocol('WM_DELETE_WINDOW' => ['Cancel', $w ]);
# Create directory/filter entry, place at the top.
-textvariable => \$w->{DirectoryString},
-labelVariable => \$w->{Configure}{-dirlabel},
$e->pack(-side => 'top', -expand => 0, -fill => 'x');
$e->bind('<Return>' => [$w => 'validateDir', Ev(['get'])]);
# Create file entry, place at the bottom.
LabEntry => 'file_entry',
-textvariable => \$w->{Configure}{-initialfile},
-labelVariable => \$w->{Configure}{-filelabel},
$e->pack(-side => 'bottom', -expand => 0, -fill => 'x');
$e->bind('<Return>' => [$w => 'validateFile', Ev(['get'])]);
# Create directory scrollbox, place at the left-middle.
ScrlListbox => 'dir_list',
-labelVariable => \$w->{Configure}{-dirlistlabel},
$b->pack(-side => 'left', -expand => 1, -fill => 'both');
$b->bind('<Double-Button-1>' => [$w => 'Accept_dir', Ev(['getSelected'])]);
$f->pack(-side => 'right', -fill => 'y', -expand => 0);
$b = $f->Button('-textvariable' => \$w->{'Configure'}{'-acceptlabel'},
-command => [ 'Accept', $w ],
$b->pack(-side => 'top', -fill => 'x', -expand => 1);
$b = $f->Button('-textvariable' => \$w->{'Configure'}{'-cancellabel'},
-command => [ 'Cancel', $w ],
$b->pack(-side => 'top', -fill => 'x', -expand => 1);
$b = $f->Button('-textvariable' => \$w->{'Configure'}{'-resetlabel'},
-command => [$w => 'configure','-directory','.'],
$b->pack(-side => 'top', -fill => 'x', -expand => 1);
$b = $f->Button('-textvariable' => \$w->{'Configure'}{'-homelabel'},
-command => [$w => 'configure','-directory',$ENV{'HOME'}],
$b->pack(-side => 'top', -fill => 'x', -expand => 1);
# Create file scrollbox, place at the right-middle.
ScrlListbox => 'file_list',
-labelVariable => \$w->{Configure}{-filelistlabel},
$b->pack(-side => 'right', -expand => 1, -fill => 'both');
$b->bind('<Double-1>' => [$w => 'Accept']);
-title => 'Verify Error',
-width => [ ['file_list','dir_list'], undef, undef, 14 ],
-height => [ ['file_list','dir_list'], undef, undef, 14 ],
-directory => [ 'METHOD', undef, undef, '.' ],
-initialdir => '-directory',
-filelabel => [ 'PASSIVE', 'fileLabel', 'FileLabel', 'File' ],
-initialfile => [ 'PASSIVE', undef, undef, '' ],
-filelistlabel => [ 'PASSIVE', undef, undef, 'Files' ],
-filter => [ 'METHOD', undef, undef, undef ],
-defaultextension => [ 'METHOD', undef, undef, undef ],
-regexp => [ 'METHOD', undef, undef, undef ],
-dirlistlabel => [ 'PASSIVE', undef, undef, 'Directories'],
-dirlabel => [ 'PASSIVE', undef, undef, 'Directory'],
'-accept' => [ 'CALLBACK',undef,undef, undef ],
-command => [ 'CALLBACK',undef,undef, undef ],
-transient => [ 'PASSIVE', undef, undef, 1 ],
-verify => [ 'PASSIVE', undef, undef, ['!-d'] ],
-create => [ 'PASSIVE', undef, undef, 0 ],
-acceptlabel => [ 'PASSIVE', undef, undef, 'Accept'],
-cancellabel => [ 'PASSIVE', undef, undef, 'Cancel'],
-resetlabel => [ 'PASSIVE', undef, undef, 'Reset'],
-homelabel => [ 'PASSIVE', undef, undef, 'Home'],
DEFAULT => [ 'file_list' ],
$w->Delegates(DEFAULT => 'file_list');
return "\\$ch" if (length $bs);
return '.*' if ($ch eq '*');
return '.' if ($ch eq '?');
return "\\." if ($ch eq '.');
return "\\/" if ($ch eq '/');
return "\\\\" if ($ch eq '\\');
$regex =~ s/(\\?)(.)/&translate($1,$2)/ge;
return sub { shift =~ /^${regex}$/ };
my $var = \$cw->{Configure}{'-filter'};
if (@_ > 1 || !defined($$var))
$val = '*' unless defined $val;
$cw->{'match'} = glob_to_re($val) unless defined $cw->{'match'};
unless ($cw->{'reread'}++)
$cw->afterIdle(['reread',$cw,$cw->cget('-directory')])
my $var = \$cw->{Configure}{'-regexp'};
$cw->{'match'} = sub { shift =~ m|^${val}$| };
unless ($cw->{'reread'}++)
$cw->afterIdle(['reread',$cw])
$val = ".$val" if ($val !~ /^\./);
my ($ext) = $val =~ /(\.[^\.]*)$/;
my $var = \$cw->{Configure}{'-directory'};
if (@_ > 1 && defined $dir)
if (substr($dir,0,1) eq '~')
if (substr($dir,1,1) eq '/')
$dir = $ENV{'HOME'} . substr($dir,1);
{my ($uid,$rest) = ($dir =~ m#^~([^/]+)(/.*$)#);
$dir = (getpwnam($uid))[7] . $rest;
$dir =~ s#([^/\\])[\\/]+$#$1#;
if (chdir( (defined($dir) ? $dir : '') ) )
carp "Cannot getcwd in '$dir'";
chdir($pwd) || carp "Cannot chdir($pwd) : $!";
$cw->{Configure}{'-directory'} = $dir;
$cw->BackTrace("Cannot chdir($dir) :$!");
unless ($cw->{'reread'}++)
$cw->afterIdle(['reread',$cw])
my $dir = $w->cget('-directory');
if (!defined $w->cget('-filter') or $w->cget('-filter') eq '')
$w->configure('-filter', '*');
my $dl = $w->Subwidget('dir_list');
my $fl = $w->Subwidget('file_list');
my $file = $w->cget('-initialfile');
my $accept = $w->cget('-accept');
foreach my $f (sort(readdir(DIR)))
if (!defined($accept) || $accept->Call($path))
$seen = $fl->index('end') if ($file && $f eq $file);
$fl->selectionSet($seen);
$w->configure(-initialfile => undef) unless $w->cget('-create');
$w->{DirectoryString} = $dir . '/' . $w->cget('-filter');
my ($leaf,$base) = fileparse($name);
$cw->configure('-directory' => $base,'-filter' => $leaf);
$cw->configure('-directory' => $name);
my $n = $cw->index('end');
# See if it is an existing file
for ($i= 0; $i < $n; $i++)
$cw->selection('set',$i);
# otherwise allow if -create is set, directory is writable
# and it passes filter and accept criteria
if ($cw->cget('-create'))
my $path = $cw->cget('-directory');
if (&{$cw->{match}}($name))
my $accept = $cw->cget('-accept');
my $full = "$path/$name";
if (!defined($accept) || $accept->Call($full))
$cw->{Selected} = [$full];
$cw->Callback(-command => @{$cw->{Selected}});
$cw->Error("$name is not 'acceptable'");
$cw->Error("$name does not match '".$cw->cget('-filter').'\'');
$cw->Error("Directory '$path' is not writable");
my $dlg = $cw->Subwidget('dialog');
$dlg->configure(-text => $msg);
if ($cw->cget('-transient')) {
$cw->waitVariable(\$cw->{Selected});
return defined($cw->{Selected})
? (wantarray) ? @{$cw->{Selected}} : $cw->{Selected}[0]
$args{-verify} = [qw(!-d -w)];
delete $args{-filetypes
};
Tk
::DialogWrapper
('FileSelect',$cmd, %args);