2 # The Virtual Songbook 0.0
3 # (c) 2003 Martin Mares <mj@ucw.cz>
9 ### Interface with Curses ###
17 $color_mode = (has_colors && COLORS >= 8 && COLOR_PAIRS >= 8) unless defined $color_mode;
24 sub cleanup_terminal() {
28 my ($attr_normal, $attr_status, $attr_hilite, $attr_chord);
32 init_pair(1, COLOR_YELLOW, COLOR_BLUE);
33 $attr_status = COLOR_PAIR(1) | A_BOLD;
34 init_pair(2, COLOR_YELLOW, COLOR_BLACK);
35 $attr_chord = COLOR_PAIR(2);
37 $attr_status = A_BOLD;
40 $attr_normal = A_NORMAL;
41 $attr_hilite = A_BOLD;
44 my $try_full_names = 1;
46 my $file_window_width = 20;
48 my ($term_w, $term_h);
50 my $status_window = new VS::Window::Status;
51 my $main_window = new VS::Window::Main;
52 my $file_window = new VS::Window::File;
54 my $focused_window_i = 2;
55 my $focused_window = $file_window;
58 $focused_window->{"focused"} = 0;
61 if ($focused_window_i > $#window_list) { $focused_window_i=0; }
62 $focused_window = $window_list[$focused_window_i];
63 } while (!$focused_window->{"focusable"} || !$focused_window->{"visible"});
64 $focused_window->{"focused"} = 1;
67 sub recalc_windows() {
72 $status_window->place(0, 0, 1, $w);
73 if ($file_window->{"visible"}) {
74 my $fww = $file_window_width;
75 $main_window->place(1, 0, $h-1, $w-$fww-1);
76 $W->attrset($focused_window == $file_window ? $attr_hilite : $attr_normal);
77 $W->vline(2, $w-$fww-1, ACS_VLINE, $h-3);
78 $W->hline(1, $w-$fww, ACS_HLINE, $fww);
79 $W->hline($h-1, $w-$fww, ACS_HLINE, $fww);
80 $W->addch(1, $w-$fww-1, ACS_ULCORNER);
81 $W->addch($h-1, $w-$fww-1, ACS_LLCORNER);
82 $W->attrset($attr_normal);
83 $file_window->place(2, $w-$fww, $h-3, $fww);
85 $main_window->place(1, 0, $h-1, $w);
86 $file_window->place(0, 0, 0, 0);
90 sub toggle_window($) {
92 if ($win->{"visible"} = !$win->{"visible"}) {
93 while ($focused_window != $win) { focus_next; }
95 if ($focused_window == $win) { focus_next; }
105 $W->move($term_h-1, $term_w-1);
108 if ($key eq "\033" || $key eq "q") {
111 } elsif ($key eq "f") {
112 toggle_window($file_window);
113 } elsif ($key eq "\t") {
116 } elsif ($key eq "\014") {
118 } elsif ($key eq "<" && $file_window_width < $term_w-1) {
119 $file_window_width++;
121 } elsif ($key eq ">" && $file_window_width > 1) {
122 $file_window_width--;
125 $focused_window->key($key);
129 ### Window Objects ###
143 push @window_list, $w;
148 my ($w,$nx,$ny,$nh,$nw) = @_;
149 if ($w->{"visible"}) {
150 if (!defined $w->{"win"} || $w->{"x"} != $nx || $w->{"y"} != $ny
151 || $w->{"w"} != $nw || $w->{"h"} != $nh) {
152 $w->{"win"} = $W->subwin($nh,$nw,$nx,$ny);
167 package VS::Window::Main;
169 BEGIN { our @ISA = qw(VS::Window); }
172 my $w = new VS::Window;
175 $w->{"lines"} = ["", "", " The Virtual Songbook 0.0\n", " (c) 2003 Martin Mares <mj\@ucw.cz>"];
176 $w->{"linetype"} = [0,0,0,0];
184 if ($w->{"file"} ne $f) {
194 if (/^(\w+):\s*(.*)/ && !defined $attrs{$1}) {
202 if (s/^!//) { push @types, 1; } else { push @types, 0; }
206 $w->{"attrs"} = \%attrs;
207 $w->{"lines"} = \@lines;
208 $w->{"linetypes"} = \@types;
210 $w->{"n"} = scalar @lines;
212 if (defined $attrs{"Name"}) {
214 $x = $attrs{"Author"} . ": $x" if defined $attrs{"Author"};
216 $status_window->tell($x);
218 $status_window->tell("Cannot open $f");
223 sub redraw_line($$) {
225 my $win = $w->{"win"};
226 my $l = $w->{"lines"}->[$i];
227 if ($w->{"linetypes"}->[$i]) { $win->attrset($attr_chord); }
228 if (length $l < $w->{"w"}) {
229 $win->addstr($i-$w->{"top"}, 0, $l);
232 $win->addstr($i-$w->{"top"}, 0, substr($l, 0, $w->{"w"}));
234 $win->attrset($attr_normal);
239 my $win = $w->{"win"};
240 my $top = $w->{"top"};
241 my $cnt = $w->{"n"} - $w->{"top"};
242 if ($cnt > $w->{"h"}) { $cnt = $w->{"h"}; }
243 for (my $i=$top; $i<$top+$cnt; $i++) { $w->redraw_line($i); }
244 if ($cnt < $w->{"h"}) {
253 my $win = $w->{"win"};
254 my $top = $w->{"top"} + $delta;
255 if ($top + $w->{"h"} > $w->{"n"}) { $top = $w->{"n"} - $w->{"h"}; }
256 if ($top < 0) { $top = 0; }
257 my $otop = $w->{"top"};
259 if ($top < $otop - $w->{"h"}/2) {
261 } elsif ($top < $otop) {
262 my $j = $otop - $top;
266 for (my $i=0; $i<$j; $i++) { $w->redraw_line($top+$i); }
267 } elsif ($top == $otop) {
269 } elsif ($top < $otop + $w->{"h"}/2) {
270 my $j = $top - $otop;
274 for (my $i=$j; $i>0; $i--) { $w->redraw_line($top+$w->{"h"}-$i); }
283 if ($key eq KEY_UP) { $w->go(-1); }
284 elsif ($key eq KEY_DOWN) { $w->go(1); }
285 elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
286 elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
287 elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
288 elsif ($key eq KEY_END) { $w->go(1000000000); }
289 else { $status_window->tell("Unknown key <$key>"); }
292 package VS::Window::File;
294 BEGIN { our @ISA = qw(VS::Window); }
297 my $w = new VS::Window;
306 my @l = `cd $p && ls`;
309 if ($p ne "./") { push @fn, "../"; push @full, "<parent>"; }
315 if ($try_full_names && open(X, "$p/$x")) {
319 if (/^Name:\s*(.*)/) {
326 push @full, $fullname;
327 } elsif (-d "$p/$x") { push @fn, "$x/"; push @full, "$x/"; }
329 $w->{"flist"} = \@fn;
330 $w->{"list"} = \@full;
331 $w->{"n"} = scalar @fn;
336 sub redraw_line($$) {
338 my $line = $i - $w->{"1st"};
339 if ($line < 0 || $line >= $w->{"h"}) { return; }
340 my $win = $w->{"win"};
341 my $item = ($i < $w->{"n"}) ? substr($w->{"list"}->[$i], 0, $w->{"w"}) : "";
342 if ($i == $w->{"i"}) { $win->bkgdset($attr_status); }
343 $win->addstr($line, 0, $item);
344 $win->clrtoeol if length $item < $w->{"w"};
345 $win->bkgdset($attr_normal);
350 $w->{"win"}->idlok(1);
351 for (my $i=0; $i<$w->{"h"}; $i++) {
352 $w->redraw_line($w->{"1st"} + $i);
354 $w->{"win"}->noutrefresh;
362 if ($i < 0) { $i = 0; }
363 if ($i >= $w->{"n"}) { $i = $w->{"n"}-1; }
365 if ($w->{"visible"}) {
366 $w->redraw_line($oldi);
367 if ($i < $w->{"1st"}) {
368 my $j = $w->{"1st"} - $i;
370 if ($j >= $w->{"h"}/2) {
371 $w->{"win"}->scrollok(1);
372 $w->{"win"}->scrl(-$j);
373 $w->{"win"}->scrollok(0);
374 for (my $k=0; $k<$j; $k++) { $w->redraw_line($i+$k); }
375 } else { $w->redraw; }
376 } elsif ($i >= $w->{"1st"} + $w->{"h"}) {
377 my $j = $i - $w->{"1st"} - $w->{"h"} + 1;
379 if ($j < $w->{"h"}/2) {
380 $w->{"win"}->scrollok(1);
381 $w->{"win"}->scrl($j);
382 $w->{"win"}->scrollok(0);
383 for (my $k=1; $k<=$j; $k++) { $w->redraw_line($i-$j+$k); }
384 } else { $w->redraw; }
385 } else { $w->redraw_line($i); }
386 $w->{"win"}->noutrefresh;
388 if ($auto_enter && $i < $w->{"n"} && $w->{"flist"}->[$i] !~ /\/$/) { $w->select; }
393 if ($w->{"i"} < $w->{"n"}) {
394 my $f = $w->{"flist"}->[$w->{"i"}];
395 my $x = $w->{"list"}->[$w->{"i"}];
398 $w->{"dir"} =~ s@([^/]*/)$@@;
400 $w->{"xdir"} =~ s@[^/]*/$@@;
402 for (my $i=0; $i<$w->{"n"}; $i++) {
403 if ($w->{"flist"}->[$i] eq $back) {
405 if ($i > $w->{"h"}/2) { $w->{"1st"} = $i - int($w->{"h"}/2); }
406 if ($w->{"1st"} + $w->{"h"} > $w->{"n"}) { $w->{"1st"} = $w->{"n"} - $w->{"h"}; }
407 if ($w->{"1st"} < 0) { $w->{"1st"} = 0; }
418 $main_window->view($w->{"dir"} . $f, $w->{"xdir"} . $x);
425 if ($key eq KEY_UP) { $w->go(-1); }
426 elsif ($key eq KEY_DOWN) { $w->go(1); }
427 elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
428 elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
429 elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
430 elsif ($key eq KEY_END) { $w->go(1000000000); }
431 elsif ($key eq "\n" || $key eq "\r" || $key eq KEY_RIGHT) { $w->select; }
432 elsif ($key eq KEY_LEFT) {
433 if ($w->{"list"}->[0] eq "<parent>") {
440 package VS::Window::Status;
441 BEGIN { our @ISA = qw(VS::Window); }
444 my $w = new VS::Window;
445 $w->{"focusable"} = 0;
452 my $win = $w->{"win"};
453 $win->bkgdset($attr_status);
454 $win->addstr(0, 0, $w->{"msg"});
461 if ($w->{"msg"} ne $m) {