home *** CD-ROM | disk | FTP | other *** search
- package Term::ReadLine::Zoid::FileBrowse;
-
- use strict;
- use base 'Term::ReadLine::Zoid';
-
- our $VERSION = 0.01;
-
- our %_keymap = ( # maybe inherit from insert ? these could be remapped
- up => 'select_previous',
- down => 'select_next',
- right => 'select_next_col',
- left => 'select_previous_col',
- page_up => 'page_up',
- page_down => 'page_down',
- return => 'accept_line',
- ctrl_C => 'return_empty_string',
- escape => 'switch_mode',
- '/' => 'fb_mini_buffer',
- ' ' => 'toggle_mark',
- '.' => 'toggle_hide_hidden',
- _default => 'self_insert',
- _on_switch => 'fb_switch',
- );
-
- sub keymap {
- my $self = shift;
- for (
- ['hide_hidden_files', 1],
- ['fb_prompt', "\e[1;37m -- \%s -- \e[0m"],
- ) {
- $$self{config}{$$_[0]} = $$_[1]
- unless defined $$self{config}{$$_[0]}
- }
- return \%_keymap;
- }
-
- # fb_item == item currently selected (pointed at)
- # fb_marks == marked _numbers_ in current dir
- # fb_marked == marked _items_ in other dirs
- # fb_items == items in current dir
- # fb_dir == current dir
-
- sub fb_switch {
- my $self = shift;
- @$self{qw/fb_item fb_marks fb_marked fb_items/} = (0, [], [], []);
- $self->fb_switch_dir('.');
- }
-
- sub fb_switch_dir { # FIXME FIXME "marked" admisnistrationis contains bugs
- my ($self, $dir) = @_;
- $dir ||= $$self{fb_dir};
- my $pwd;
- if ($dir eq '.') { $dir = $ENV{PWD}; $pwd++; }
- else {
- $dir =~ s#^\./#$ENV{PWD}#;
- $dir =~ s#/\.(/|$)|//+#/#g;
- $dir =~ s#(^|/)[^/]*/\.\.(/|$)##g;
- $dir =~ s#/?$#/#;
- }
-
- opendir DIR, $dir or return $self->bell;
- my (@marks, @marked);
- for ($self->marked()) {
- if (m#(.*/)(.*)#) {
- if ($1 eq $dir) { push @marks, $2 }
- else { push @marked, $_ }
- }
- elsif ($pwd) { push @marks, $_ }
- else { push @marked, $_ }
- }
- $$self{fb_items} = [ '../',
- map {-d "$dir/$_" ? $_.'/' : $_}
- $$self{config}{hide_hidden_files}
- ? (sort grep {$_ !~ /^\./ } readdir DIR)
- : (sort grep {$_ !~ /^\.\.?$/} readdir DIR)
- ];
- close DIR;
- $$self{fb_marked} = \@marked;
- $$self{fb_marks} = [];
- if (@marks) {
- my $i = 0;
- for my $item (@{$$self{fb_items}}) {
- push @{$$self{fb_marks}}, $i
- if grep {$_ eq $item} @marks;
- $i++;
- }
- }
- @$self{qw/fb_item fb_dir/} = (0, $dir);
- }
-
- sub draw { # Render Fu
- my $self = shift;
-
- my @pos = (1, 1);
- my @lines = map " $_", @{$$self{fb_items}};
- for (@{$$self{fb_marks}}) { $lines[$_] =~ s/^ /*/ }
- $lines[ $$self{fb_item} ] =~ s/^(.) /$1>/;
-
- @lines = $self->col_format( @lines );
- $$self{fb_rows} = scalar @lines;
- $pos[1] += ($$self{fb_item} % $$self{fb_rows}); # assuming +1 offset due to fb_prompt
-
- unshift @lines, sprintf $$self{config}{fb_prompt}, $$self{fb_dir};
-
- $self->print(\@lines, \@pos);
- }
-
- sub toggle_hide_hidden {
- $_[0]{config}{hide_hidden_files} =
- $_[0]{config}{hide_hidden_files} ? 0 : 1 ;
- $_[0]->fb_switch_dir();
- }
-
- sub self_insert {
- my ($self, $key) = @_;
- return $self->bell unless $key =~ /^\d+$/;
- #$$self{fb_item} .= $key;
- }
-
- sub accept_line {
- my $self = shift;
- my $dir = $$self{fb_dir}.'/'.$$self{fb_items}[ $$self{fb_item} ];
- return $self->fb_switch_dir($dir) if -d $dir;
-
- push @{$$self{fb_marks}}, $$self{fb_item};
- my @words = map {s/'/\\'/g; '\''.$_.'\''} $self->marked();
- $self->substring(join(' ', @words), $$self{pos});
- $self->switch_mode();
- }
-
- sub fb_mini_buffer {
- my $self = shift;
- }
-
- sub select_next { $_[0]{fb_item}++ if $_[0]{fb_item} < $#{$_[0]{fb_items}} }
-
- sub page_up {
- my $self = shift;
- my (undef, $higth) = $self->TermSize();
- my $vpos = $$self{fb_item} % $$self{fb_rows};
- if ($vpos > $higth) { $$self{fb_item} -= $higth }
- else { $$self{fb_item} -= $vpos }
- }
-
- sub page_down {
- my $self = shift;
- my (undef, $higth) = $self->TermSize();
- my $rvpos = $$self{fb_rows} - ($$self{fb_item} % $$self{fb_rows});
- if ($rvpos > $higth) { $$self{fb_item} += $higth }
- else { $$self{fb_item} += $rvpos }
- $$self{fb_item} = $#{$$self{fb_items}} if $$self{fb_item} > $#{$$self{fb_items}};
- }
-
- sub select_previous { $_[0]{fb_item}-- if $_[0]{fb_item} > 0 }
-
- sub select_next_col {
- $_[0]{fb_item} += $_[0]{fb_rows}
- unless $_[0]{fb_rows} > $#{$_[0]{fb_items}} - $_[0]{fb_item};
- }
-
- sub select_previous_col {
- $_[0]{fb_item} -= $_[0]{fb_rows}
- unless $_[0]{fb_rows} > $_[0]{fb_item};
- }
-
- sub toggle_mark { # FIXME should be toggle
- my $self = shift;
- my $l = scalar @{$$self{fb_marks}};
- @{$$self{fb_marks}} = grep {$_ != $$self{fb_item}} @{$$self{fb_marks}};
- push @{$$self{fb_marks}}, $$self{fb_item} if $l == scalar @{$$self{fb_marks}};
- $self->select_next();
- }
-
- sub marked {
- my $self = shift;
- my $dir = $$self{fb_dir};
- $dir =~ s#^\Q$ENV{PWD}\E/?##;
- $dir =~ s#/?$#/# if length $dir;
- return @{$$self{fb_marked}},
- map $dir.$_, @{$$self{fb_items}}[ @{$$self{fb_marks}} ];
- }
-
- 1;
-
- __END__
-
- =head1 NAME
-
- Term::ReadLine::Zoid::FileBrowse - a readline file browser mode
-
- =head1 SYNOPSIS
-
- This class is used as a mode under L<Term::ReadLine::Zoid>,
- see there for usage details.
-
- =head1 DESCRIPTION
-
- This module provides a "file browse" mode that lets you interactively select files
- and navigate your file-system.
-
- =head1 KEY MAPPING
-
- =over 4
-
- =item up (I<select_previous>)
-
- =item down (I<select_next>)
-
- =item right (I<select_next_col>)
-
- =item left (I<select_previous_col>)
-
- =item page_up (I<page_up>)
-
- =item page_down (I<page_down>)
-
- =item return (I<accept_line>)
-
- =item ^C (I<return_empty_string>)
-
- =item escape (I<switch_mode>)
-
- =item / (I<fb_mini_buffer>)
-
- =item space (I<toggle_mark>)
-
- =item . (I<toggle_hide_hidden>)
-
- =back
-
- =head1 TODO
-
- make fb_prompt config compatible with PS1 stuff
- and keep $ENV{CLICOLOR} in mind
-
- =head1 AUTHOR
-
- Jaap Karssenberg (Pardus) E<lt>pardus@cpan.orgE<gt>
-
- Copyright (c) 2004 Jaap G Karssenberg. All rights reserved.
- This program is free software; you can redistribute it and/or
- modify it under the same terms as Perl itself.
-
- =head1 SEE ALSO
-
- L<Term::ReadLine::Zoid>
-
- =cut
-
-