3 use vars qw($cui $list $win $VERSION);
5 #5.12 introduced a warning about prototypes that afflicts old Curses::UI
6 #BEGIN{ if( $^V lt v5.12.0 ){eval "use Curses::UI"}else{ eval "no warnings 'illegalproto'; use Curses::UI" } }
12 my %opts = configure();
13 my $exit = sprintf("marked sequence %s in +%s\n",
14 ($opts{newsequence}||$opts{sequence}), $opts{'+'});
15 $SIG{CONT} = sub{ $cui->leave_curses(); $cui->reset_curses() };
17 $cui = new Curses::UI;
18 $cui->add('status', 'Window', -y=>-1, -height=>1)->
20 add('explain', 'Label', -reverse=>1, -bold=>1, -text=>
21 ' H)elp I)nvert V)iew Q)uit S)ave '. # </reverse>
22 "+$opts{'+'} -seq $opts{sequence}".
23 ($opts{cull}?' -cull':'').($opts{zero}?' -zero':'').
24 ($opts{newsequence}?" -new $opts{newsequence}":''));
26 $cui->set_binding(\&help, 'h');
27 $cui->set_binding($SIG{CONT}, "\cL");
28 $cui->set_binding(\&setSequence, 's');
29 $cui->set_binding(sub{ kill "STOP", $$}, "\cZ");
32 my(@values, %labels, %indices);
33 scan(\@values, \%labels, \%indices);
35 $win = $cui->add('window', 'Window', -padbottom=>1);
37 add('mylistbox', 'Listbox', -multi=>1, -values=>\@values, -labels=>\%labels, -htmltext=>0);
39 #XXX use -bindings and -routines as in Widget to clean up code?
40 $list->set_binding(sub{ $exit=''; exit }, "\cC", 'q');
41 $list->set_binding(\&invert=>'i');
42 $list->set_binding(\&view, 'v', 'm');
43 $list->set_binding(\&pgdn, "\cV", ' ' );
44 $list->set_binding(\&pgup, "\eV", 'b');
45 $list->set_selection(@indices{getSequence()}) unless $opts{zero};
58 my %args = (_MBMHR=>(eval "use Mail::Box::MH::Resource 0.06", !$@),
59 _NR =>(eval "use Number::Range", !$@) );
65 $_ = $args{_MBMHR} ? Mail::Box::MH::Resource->new()->get($nom) :
67 unshift(@ARGV, split) if defined;
70 Getopt::Long::Configure qw(auto_abbrev pass_through);
71 GetOptions (\%args, 'sequence=s', 'newsequence:s',
72 'cull!', 'reverse!', 'zero!');
73 $args{'+'} = (grep {/^\+/} @ARGV)[0] || '';
74 $args{'+'} =~ s/^\+//;
75 unless( $args{sequence} ){
76 $args{sequence}='vpick';
80 $args{newsequence} ||= 'vpick' if defined $args{newsequence};
83 my $MHR = Mail::Box::MH::Resource->new();
84 $args{_Path} = $MHR->get('Path');
85 $args{_Path} = File::Spec->file_name_is_absolute($args{_Path}) ?
87 File::Spec->catfile($ENV{HOME}, $args{_Path});
88 $MHR = Mail::Box::MH::Resource->new(File::Spec->catfile($args{_Path},
90 $args{'+'} ||= $MHR->get('Current-Folder');
99 -title =>'Navigation',
103 ^A <Home> Top of list
107 l <Right> <Enter> Mark message
112 ^V <PgDn> <Space> Page down
113 ^E <End> Bottom of list
124 my @F = getSequence();
125 foreach($list->get(), @values){ $count{$_}++ }
126 foreach(keys %count){ delete($count{$_}) if $count{$_} > 1 }
127 $list->clear_selection();
128 $list->set_selection(@indices{%count});
134 if ($this->{-ypos} >= $this->{-max_selected}) {
140 $this->{-ypos} = $this->canvasheight *
141 ( 2 + $this->{-ypos}/$this->canvasheight );
143 $this->run_event('-onselchange');
145 #This is critical for to end up at the top of the screen,
146 #and why jumped two pages
148 $this->{-ypos} -= $this->canvasheight;
150 $this->run_event('-onselchange');
151 $this->schedule_draw(1);
157 if ($this->{-ypos} <= 0) {
161 $this->{-ypos} -= $this->canvasheight
162 +$this->{-ypos}%$this->canvasheight;
163 $this->run_event('-onselchange');
164 $this->schedule_draw(1);
169 #Add back space for wide msg number, less the space used by the checkbox
170 #XXX #my $width = q'-width '. (qx'scan -format "%(width)" -noheader last'+6);
171 my $width = q'-width '. ($ENV{COLS} + 6);
172 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}>>%>'`;
173 my $args = "+$opts{'+'} " if $opts{_MBMHR};
174 $args.= $opts{reverse} ? '-reverse' : '-noreverse';
176 $args.= " $opts{sequence}" if $opts{cull};
179 foreach( split/\n/, qx/scan -noheader $width $format $args/ ){
180 my (undef, $value, $label) = split(/\s+/, $_, 3);
181 push @{$_[0]}, $value;
182 $_[1]->{$value} = $label;
183 $_[2]->{$value} = $i++;
190 if( $view = $win->getobj('view') ){
194 $view = $win->add('view', 'TextViewer', -vscrollbar=>1, -wrapping=>1);
195 $view->set_binding(sub{ $list->focus(); $win->delete('view') },
196 "\cC", 'q', 'v', 'm');
197 # $view->set_binding(sub{ $list->focus() }, "\cC", 'q', 'v', 'm');
198 $view->set_binding('cursor-pagedown'=>"\cV");
199 $view->set_binding('cursor-pageup' =>"\eV", 'b');
203 my $msg = $list->get_active_value();
205 $path = File::Spec->catfile($opts{_Path},$opts{'+'},$msg); }
207 $path = qx(mhpath $msg); }
208 $view->text("Message: $msg\n". do{ local $/=undef; open(MSG, $path); <MSG>});
214 my $MHR = Mail::Box::MH::Resource->new(
215 File::Spec->catfile($opts{_Path}, $opts{'+'}, '.mh_sequences') );
216 local $_ = $MHR->get($opts{sequence});
217 s/(?=\s)/,/g; s/-/\.\./g;
222 if( $opts{_NR} && $opts{_MBMHR} ){
223 my $seq = scalar Number::Range->new($list->get())->range();
226 my $MHR = Mail::Box::MH::Resource->new(
227 File::Spec->catfile($opts{_Path},$opts{'+'},'.mh_sequences'));
228 $MHR->set($opts{newsequence}||$opts{sequence}=>$seq);
229 exit $MHR->close(); }
231 exit system('mark', '-zero', '-seq', $opts{sequence}, $list->get())>>8;}
239 vpick - visual pick, mark a message sequence by eye
243 vpick [+folder] [B<-sequence> name] [B<-newsequence> [name]] [B<-cull>] [B<-zero>] [B<-reverse>] [B<sequence>]
249 <img src="vpick.png" height=464 width=992>
255 =item The script source.
257 <a href="vpick">vpick</a>
259 =item A binary for i686 Linux (Perl 5.8.0) for testing the software without ins\
260 talling the L<dependencies|/REQUIREMENTS>.
262 <a href="ftp://pthbb.org/pub/MH/vpick.gz">vpick.gz</a>
270 A nifty little tool for those dyed-in-the-wool MH users whom occasionally envy
271 those pine/elm/mutt users. For those times when you'll know what you want when
272 you see it and mark just won't cut it. vpick allows you to check boxes for
273 individual messages to save them in a sequence.
275 If you're lucky and your local curses library has mouse support you might even
276 be the envy of your friends.
284 Online navigation help.
288 Invert the current message selections.
292 Toggle a display of the highlighted message.
296 Save the sequence and exit.
300 Exit without saving changes to the sequence.
308 =item * C<Right>, C<l>, C<Enter>
310 Add the current message to the sequence.
314 Add the current message to the sequence and advance to the next message.
318 Remove the current message from the sequence and advance to the next message.
320 =item * C<Down>, C<j>
322 Advance to the next message.
326 Select previous message.
328 =item * C<PageUp>, C<b>, C<M-v>
332 =item * C<PageDown>, C<C-v>
336 =item * C<Home>, C<C-a>
338 Select the first message in the folder.
340 =item * C<End>, C<C-e>
342 Select the last message in the folder.
346 Search message entries with a 'less'-like search system.
347 A searchstring is entered and selection advances to the first match.
348 After that the C<n> will search for the next occurance,
349 and C<N> the previous.
353 The same as C</>, only it will search in the opposite direction.
359 Like MH commands, vpick options can be abbreviated to a shortest unique string,
360 and any option that does not take an argument can be prefixed by I<no> to
361 override earlier options.
367 The name of the folder to edit a sequence for. Defaults to the current folder.
369 =item B<-sequence> name
371 The name of the sequence to edit. Defaults to I<vpick>.
375 Only display the messages in the specified sequence.
376 You probably want B<-cull> instead.
382 =item B<-cull>, B<-nocull>
384 Only display the messages currently in B<-sequence>. Shorthand for:
388 =item B<-reverse>, B<-noreverse>
390 View the folder in reverse order.
392 =item B<-newsequence> name
394 Save the altered message sequence as this new name, rather than clobber the
395 existing sequence. The sequence name is otpional, and defaults to I<vpick>.
397 This is useful for catching up on a backlog of unread messages like so:
399 -seq unseen -new -cull
401 This preserves your I<unseen> sequence, so that when you C<rmm> the chaff
402 in I<vpick>, I<unseen> contains only wheat.
404 =item B<-zero>, B<-nozero>
406 Zeroing loads an empty sequence, B<-nozero> flags existing messages from
407 the sequence. Default is B<-nozero> unless B<-sequence> defaults to I<vpick>.
408 See L</REQUIREMENTS>.
418 To provide the nifty visual interface.
422 Required to summarize folder contents. But then you use MH, you knew that.
424 =item C<mark> OR C<Mail::Box::MH::Resource> + C<Number::Range>
426 Required to preserve sequences.
427 The latter is preferred, without it some functionality will be disabled i.e;
428 vpick will be forced to run as if -zero were given.
430 Strictly speaking, you can also use vpick with MBMHR and not Number::Range.
436 If using C<mark> instead of MBMHR and Number::Range with large sequences
437 you may loose; blame the shell.
439 You should not do anything to alter message ordering/numbering while vpick
440 is running e.g; L<sortm(1)>, L<folder(1)> -pack Other operations are fine.
444 The scan format is embedded in the program, it doesn't seem worthwhile to
445 abstract it to a user configurable setting.
449 L<mark(1)>, L<pick(1)>, L<scan(1)>
453 Jerrad Pierce <jpierce@cpan.org>.
459 =item * Thou shalt not claim ownership of unmodified materials.
461 =item * Thou shalt not claim whole ownership of modified materials.
463 =item * Thou shalt grant the indemnity of the provider of materials.
465 =item * Thou shalt use and dispense freely without other restrictions.
475 =item Removed warning under modern perls
477 5.12 introduced a warning about prototypes that Curses::UI has not yet fixed
485 =item Fix inclusion of year in scan format.
487 =item Documentation clean-up.
489 =item Check profile for default switches, using the name we're invoked as.
491 =item Added display of B<-cull> and B<-newsequence> to the status line
493 =item Fixed exit status message, which didn't account for B<-newsequence>.
495 =item Removed closing reverse tag from status
497 Curses::UI implies support for that feature but it doesn't offer it yet :-/
505 =item Added -cull & -newsquence options
513 =item Fixed status bar, it always showed -zero
521 =item Fixed paging behavior, it only DWIMd if you were on the first item of a page.
529 =item Changed paging behavior to more closely Do What I Mean
537 =item Added support for ^Z
545 =item Added message viewing
553 =item shift for self or root to kill use vars
555 =item switch scan from qx to pipe read to minimize delay?
557 =item M-< / M-> for end/begin?
559 =item Add back </reverse> to status once Curses::UI better supports htmltext