From 1175ec66e2dcc72e09617e2b853025af75fb0f7c Mon Sep 17 00:00:00 2001 From: Jerrad Pierce Date: Sun, 2 Dec 2012 21:02:11 -0600 Subject: [PATCH] Added docs/contrib/vpick. --- docs/contrib/vpick | 563 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 563 insertions(+) create mode 100755 docs/contrib/vpick diff --git a/docs/contrib/vpick b/docs/contrib/vpick new file mode 100755 index 0000000..a3eb939 --- /dev/null +++ b/docs/contrib/vpick @@ -0,0 +1,563 @@ +#!/usr/bin/perl -w +use strict; +use vars qw($cui $list $win $VERSION); + +#5.12 introduced a warning about prototypes that afflicts old Curses::UI +#BEGIN{ if( $^V lt v5.12.0 ){eval "use Curses::UI"}else{ eval "no warnings 'illegalproto'; use Curses::UI" } } + +use Curses::UI 0.9608; +use Getopt::Long; + +$VERSION = 0.14; +my %opts = configure(); +my $exit = sprintf("marked sequence %s in +%s\n", + ($opts{newsequence}||$opts{sequence}), $opts{'+'}); +$SIG{CONT} = sub{ $cui->leave_curses(); $cui->reset_curses() }; + +$cui = new Curses::UI; +$cui->add('status', 'Window', -y=>-1, -height=>1)-> +#, -htmltext=>1 + add('explain', 'Label', -reverse=>1, -bold=>1, -text=> + ' H)elp I)nvert V)iew Q)uit S)ave '. # + "+$opts{'+'} -seq $opts{sequence}". + ($opts{cull}?' -cull':'').($opts{zero}?' -zero':''). + ($opts{newsequence}?" -new $opts{newsequence}":'')); + +$cui->set_binding(\&help, 'h'); +$cui->set_binding($SIG{CONT}, "\cL"); +$cui->set_binding(\&setSequence, 's'); +$cui->set_binding(sub{ kill "STOP", $$}, "\cZ"); +#XXX clear on sleep? + +my(@values, %labels, %indices); +scan(\@values, \%labels, \%indices); + +$win = $cui->add('window', 'Window', -padbottom=>1); +$list = $win-> + add('mylistbox', 'Listbox', -multi=>1, -values=>\@values, -labels=>\%labels, -htmltext=>0); + +#XXX use -bindings and -routines as in Widget to clean up code? +$list->set_binding(sub{ $exit=''; exit }, "\cC", 'q'); +$list->set_binding(\&invert=>'i'); +$list->set_binding(\&view, 'v', 'm'); +$list->set_binding(\&pgdn, "\cV", ' ' ); +$list->set_binding(\&pgup, "\eV", 'b'); +$list->set_selection(@indices{getSequence()}) unless $opts{zero}; +$list->focus(); +$cui->mainloop; + + +##Heavy Lifting +END{ + $cui->leave_curses(); + print "\n", $exit; + exit; +} + +sub configure{ + my %args = (_MBMHR=>(eval "use Mail::Box::MH::Resource 0.06", !$@), + _NR =>(eval "use Number::Range", !$@) ); + + {#Get default values + local $_; + my $nom = $0; + $nom =~ s%^.+/%%; + $_ = $args{_MBMHR} ? Mail::Box::MH::Resource->new()->get($nom) : + `mhparam $nom`; + unshift(@ARGV, split) if defined; + } + + Getopt::Long::Configure qw(auto_abbrev pass_through); + GetOptions (\%args, 'sequence=s', 'newsequence:s', + 'cull!', 'reverse!', 'zero!'); + $args{'+'} = (grep {/^\+/} @ARGV)[0] || ''; + $args{'+'} =~ s/^\+//; + unless( $args{sequence} ){ + $args{sequence}='vpick'; + $args{zero} = 1; + } + + $args{newsequence} ||= 'vpick' if defined $args{newsequence}; + + if( $args{_MBMHR} ){ + my $MHR = Mail::Box::MH::Resource->new(); + $args{_Path} = $MHR->get('Path'); + $args{_Path} = File::Spec->file_name_is_absolute($args{_Path}) ? + $args{_Path} : + File::Spec->catfile($ENV{HOME}, $args{_Path}); + $MHR = Mail::Box::MH::Resource->new(File::Spec->catfile($args{_Path}, + 'context')); + $args{'+'} ||= $MHR->get('Current-Folder'); + } + else{ + $args{zero} = 1; } + return %args; +} + +sub help{ + $cui->dialog( + -title =>'Navigation', + -message =>< Top of list + b Page up + k Scroll up + + l Mark message + y 1 Mark & advance + n 0 Unmark & advance + + j Scroll down + ^V Page down + ^E Bottom of list + + / Search forward + ? Search backward +\0 +MESSAGE + ); +} + +sub invert{ + my %count; + my @F = getSequence(); + foreach($list->get(), @values){ $count{$_}++ } + foreach(keys %count){ delete($count{$_}) if $count{$_} > 1 } + $list->clear_selection(); + $list->set_selection(@indices{%count}); + $list->draw(); +} + +sub pgdn{ + my $this = shift; + if ($this->{-ypos} >= $this->{-max_selected}) { + $this->dobeep; + return $this; + } + { + use integer; + $this->{-ypos} = $this->canvasheight * + ( 2 + $this->{-ypos}/$this->canvasheight ); + } + $this->run_event('-onselchange'); + + #This is critical for to end up at the top of the screen, + #and why jumped two pages + $this->draw(); + $this->{-ypos} -= $this->canvasheight; + + $this->run_event('-onselchange'); + $this->schedule_draw(1); + return $this; +} + +sub pgup{ + my $this = shift; + if ($this->{-ypos} <= 0) { + $this->dobeep; + return $this; + } + $this->{-ypos} -= $this->canvasheight + +$this->{-ypos}%$this->canvasheight; + $this->run_event('-onselchange'); + $this->schedule_draw(1); + return $this; +} + +sub scan{ + #Add back space for wide msg number, less the space used by the checkbox + #XXX #my $width = q'-width '. (qx'scan -format "%(width)" -noheader last'+6); + my $width = q'-width '. ($ENV{COLS} + 6); + my $format= q`-format '%9(msg) %02(mon{date})/%02(mday{date})/%(void(year{date}))%02(modulo 100)%<{date}%> %<(mymbox{from})%<{to}To:%21(addr{to})%>%>%<(zero)%24(addr{from})%> %{subject}%<{body}<<%{body}>>%>'`; + my $args = "+$opts{'+'} " if $opts{_MBMHR}; + $args.= $opts{reverse} ? '-reverse' : '-noreverse'; + + $args.= " $opts{sequence}" if $opts{cull}; + + my $i=0; + foreach( split/\n/, qx/scan -noheader $width $format $args/ ){ + my (undef, $value, $label) = split(/\s+/, $_, 3); + push @{$_[0]}, $value; + $_[1]->{$value} = $label; + $_[2]->{$value} = $i++; + } + +} + +sub view{ + my $view; + if( $view = $win->getobj('view') ){ + $view->focus(); + } + else{ + $view = $win->add('view', 'TextViewer', -vscrollbar=>1, -wrapping=>1); + $view->set_binding(sub{ $list->focus(); $win->delete('view') }, + "\cC", 'q', 'v', 'm'); +# $view->set_binding(sub{ $list->focus() }, "\cC", 'q', 'v', 'm'); + $view->set_binding('cursor-pagedown'=>"\cV"); + $view->set_binding('cursor-pageup' =>"\eV", 'b'); + } + + my $path; + my $msg = $list->get_active_value(); + if( $opts{_MBMHR} ){ + $path = File::Spec->catfile($opts{_Path},$opts{'+'},$msg); } + else{ + $path = qx(mhpath $msg); } + $view->text("Message: $msg\n". do{ local $/=undef; open(MSG, $path); }); + #XXX || Error + $view->focus(); +} + +sub getSequence{ + my $MHR = Mail::Box::MH::Resource->new( + File::Spec->catfile($opts{_Path}, $opts{'+'}, '.mh_sequences') ); + local $_ = $MHR->get($opts{sequence}); + s/(?=\s)/,/g; s/-/\.\./g; + eval; +} + +sub setSequence{ + if( $opts{_NR} && $opts{_MBMHR} ){ + my $seq = scalar Number::Range->new($list->get())->range(); + $seq =~ s/\.\./-/g; + $seq =~ y/,/ /; + my $MHR = Mail::Box::MH::Resource->new( + File::Spec->catfile($opts{_Path},$opts{'+'},'.mh_sequences')); + $MHR->set($opts{newsequence}||$opts{sequence}=>$seq); + exit $MHR->close(); } + else{ + exit system('mark', '-zero', '-seq', $opts{sequence}, $list->get())>>8;} +} + +__END__ +=pod + +=head1 NAME + +vpick - visual pick, mark a message sequence by eye + +=head1 SYNOPSIS + +vpick [+folder] [B<-sequence> name] [B<-newsequence> [name]] [B<-cull>] [B<-zero>] [B<-reverse>] [B] + +=begin html + +=head1 SCREENSHOT + + + +=head1 DOWNLOAD + +=over + +=item The script source. + +vpick + +=item A binary for i686 Linux (Perl 5.8.0) for testing the software without ins\ +talling the L. + +vpick.gz + +=back + +=end html + +=head1 DESCRIPTION + +A nifty little tool for those dyed-in-the-wool MH users whom occasionally envy +those pine/elm/mutt users. For those times when you'll know what you want when +you see it and mark just won't cut it. vpick allows you to check boxes for +individual messages to save them in a sequence. + +If you're lucky and your local curses library has mouse support you might even +be the envy of your friends. + +=head2 INTERFACE + +=over + +=item * C + +Online navigation help. + +=item * C + +Invert the current message selections. + +=item * C, C + +Toggle a display of the highlighted message. + +=item * C + +Save the sequence and exit. + +=item * C + +Exit without saving changes to the sequence. + +=back + +=head3 Navigation + +=over + +=item * C, C, C + +Add the current message to the sequence. + +=item * C<1>, C + +Add the current message to the sequence and advance to the next message. + +=item * C<0>, C + +Remove the current message from the sequence and advance to the next message. + +=item * C, C + +Advance to the next message. + +=item * C, C + +Select previous message. + +=item * C, C, C + +Scroll up a page. + +=item * C, C + +Scroll down a page. + +=item * C, C + +Select the first message in the folder. + +=item * C, C + +Select the last message in the folder. + +=item * C + +Search message entries with a 'less'-like search system. +A searchstring is entered and selection advances to the first match. +After that the C will search for the next occurance, +and C the previous. + +=item * C + +The same as C, only it will search in the opposite direction. + +=back + +=head1 OPTIONS + +Like MH commands, vpick options can be abbreviated to a shortest unique string, +and any option that does not take an argument can be prefixed by I to +override earlier options. + +=over + +=item B<+folder> + +The name of the folder to edit a sequence for. Defaults to the current folder. + +=item B<-sequence> name + +The name of the sequence to edit. Defaults to I. + +=item B + +Only display the messages in the specified sequence. +You probably want B<-cull> instead. + +=back + +=over + +=item B<-cull>, B<-nocull> + +Only display the messages currently in B<-sequence>. Shorthand for: + + -seq foo foo + +=item B<-reverse>, B<-noreverse> + +View the folder in reverse order. + +=item B<-newsequence> name + +Save the altered message sequence as this new name, rather than clobber the +existing sequence. The sequence name is otpional, and defaults to I. + +This is useful for catching up on a backlog of unread messages like so: + + -seq unseen -new -cull + +This preserves your I sequence, so that when you C the chaff +in I, I contains only wheat. + +=item B<-zero>, B<-nozero> + +Zeroing loads an empty sequence, B<-nozero> flags existing messages from +the sequence. Default is B<-nozero> unless B<-sequence> defaults to I. +See L. + +=back + +=head1 REQUIREMENTS + +=over + +=item C + +To provide the nifty visual interface. + +=item C + +Required to summarize folder contents. But then you use MH, you knew that. + +=item C OR C + C + +Required to preserve sequences. +The latter is preferred, without it some functionality will be disabled i.e; +vpick will be forced to run as if -zero were given. + +Strictly speaking, you can also use vpick with MBMHR and not Number::Range. + +=back + +=head1 CAVEATS + +If using C instead of MBMHR and Number::Range with large sequences +you may loose; blame the shell. + +You should not do anything to alter message ordering/numbering while vpick +is running e.g; L, L -pack Other operations are fine. + +=head1 RESTRICTIONS + +The scan format is embedded in the program, it doesn't seem worthwhile to +abstract it to a user configurable setting. + +=head1 SEE ALSO + +L, L, L + +=head1 AUTHOR + +Jerrad Pierce . + +=head1 LICENSE + +=over + +=item * Thou shalt not claim ownership of unmodified materials. + +=item * Thou shalt not claim whole ownership of modified materials. + +=item * Thou shalt grant the indemnity of the provider of materials. + +=item * Thou shalt use and dispense freely without other restrictions. + +=back + +=head1 CHANGES + +0.14 + +=over + +=item Removed warning under modern perls + +5.12 introduced a warning about prototypes that Curses::UI has not yet fixed + +=back + +0.13 + +=over + +=item Fix inclusion of year in scan format. + +=item Documentation clean-up. + +=item Check profile for default switches, using the name we're invoked as. + +=item Added display of B<-cull> and B<-newsequence> to the status line + +=item Fixed exit status message, which didn't account for B<-newsequence>. + +=item Removed closing reverse tag from status + + Curses::UI implies support for that feature but it doesn't offer it yet :-/ + +=back + +0.12 + +=over + +=item Added -cull & -newsquence options + +=back + +0.11 + +=over + +=item Fixed status bar, it always showed -zero + +=back + +0.10 + +=over + +=item Fixed paging behavior, it only DWIMd if you were on the first item of a page. + +=back + +0.09 + +=over + +=item Changed paging behavior to more closely Do What I Mean + +=back + +0.08 + +=over + +=item Added support for ^Z + +=back + +0.07 + +=over + +=item Added message viewing + +=back + +=head1 TODO + +=over + +=item shift for self or root to kill use vars + +=item switch scan from qx to pipe read to minimize delay? + +=item M-< / M-> for end/begin? + +=item Add back to status once Curses::UI better supports htmltext + +=back + +=cut -- 1.7.10.4