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--;
124 } elsif ($key eq "j") {
125 $file_window->key(KEY_DOWN);
126 } elsif ($key eq "k") {
127 $file_window->key(KEY_UP);
128 } elsif ($key eq "h") {
129 $file_window->key(KEY_LEFT);
130 } elsif ($key eq "l") {
131 $file_window->key(KEY_RIGHT);
133 $focused_window->key($key);
137 ### Window Objects ###
151 push @window_list, $w;
156 my ($w,$nx,$ny,$nh,$nw) = @_;
157 if ($w->{"visible"}) {
158 if (!defined $w->{"win"} || $w->{"x"} != $nx || $w->{"y"} != $ny
159 || $w->{"w"} != $nw || $w->{"h"} != $nh) {
160 $w->{"win"} = $W->subwin($nh,$nw,$nx,$ny);
175 package VS::Window::Main;
177 BEGIN { our @ISA = qw(VS::Window); }
180 my $w = new VS::Window;
183 $w->{"lines"} = ["", "", " The Virtual Songbook 0.0\n", " (c) 2003 Martin Mares <mj\@ucw.cz>"];
184 $w->{"linetype"} = [0,0,0,0];
192 if ($w->{"file"} ne $f) {
202 if (/^(\w+):\s*(.*)/ && !defined $attrs{$1}) {
210 if (s/^!//) { push @types, 1; } else { push @types, 0; }
214 $w->{"attrs"} = \%attrs;
215 $w->{"lines"} = \@lines;
216 $w->{"linetypes"} = \@types;
218 $w->{"n"} = scalar @lines;
220 if (defined $attrs{"Name"}) {
222 $x = $attrs{"Author"} . ": $x" if defined $attrs{"Author"};
224 $status_window->tell($x);
226 $status_window->tell("Cannot open $f");
231 sub redraw_line($$) {
233 my $win = $w->{"win"};
234 my $l = $w->{"lines"}->[$i];
235 if ($w->{"linetypes"}->[$i]) { $win->attrset($attr_chord); }
236 if (length $l < $w->{"w"}) {
237 $win->addstr($i-$w->{"top"}, 0, $l);
240 $win->addstr($i-$w->{"top"}, 0, substr($l, 0, $w->{"w"}));
242 $win->attrset($attr_normal);
247 my $win = $w->{"win"};
248 my $top = $w->{"top"};
249 my $cnt = $w->{"n"} - $w->{"top"};
250 if ($cnt > $w->{"h"}) { $cnt = $w->{"h"}; }
251 for (my $i=$top; $i<$top+$cnt; $i++) { $w->redraw_line($i); }
252 if ($cnt < $w->{"h"}) {
261 my $win = $w->{"win"};
262 my $top = $w->{"top"} + $delta;
263 if ($top + $w->{"h"} > $w->{"n"}) { $top = $w->{"n"} - $w->{"h"}; }
264 if ($top < 0) { $top = 0; }
265 my $otop = $w->{"top"};
267 if ($top < $otop - $w->{"h"}/2) {
269 } elsif ($top < $otop) {
270 my $j = $otop - $top;
274 for (my $i=0; $i<$j; $i++) { $w->redraw_line($top+$i); }
275 } elsif ($top == $otop) {
277 } elsif ($top < $otop + $w->{"h"}/2) {
278 my $j = $top - $otop;
282 for (my $i=$j; $i>0; $i--) { $w->redraw_line($top+$w->{"h"}-$i); }
291 if ($key eq KEY_UP) { $w->go(-1); }
292 elsif ($key eq KEY_DOWN) { $w->go(1); }
293 elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
294 elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
295 elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
296 elsif ($key eq KEY_END) { $w->go(1000000000); }
297 else { $status_window->tell("Unknown key <$key>"); }
300 package VS::Window::File;
302 BEGIN { our @ISA = qw(VS::Window); }
305 my $w = new VS::Window;
314 my @l = `cd $p && ls`;
317 if ($p ne "./") { push @fn, "../"; push @full, "<parent>"; }
323 if ($try_full_names && open(X, "$p/$x")) {
327 if (/^Name:\s*(.*)/) {
334 push @full, $fullname;
335 } elsif (-d "$p/$x") { push @fn, "$x/"; push @full, "$x/"; }
337 $w->{"flist"} = \@fn;
338 $w->{"list"} = \@full;
339 $w->{"n"} = scalar @fn;
344 sub redraw_line($$) {
346 my $line = $i - $w->{"1st"};
347 if ($line < 0 || $line >= $w->{"h"}) { return; }
348 my $win = $w->{"win"};
349 my $item = ($i < $w->{"n"}) ? substr($w->{"list"}->[$i], 0, $w->{"w"}) : "";
350 if ($i == $w->{"i"}) { $win->bkgdset($attr_status); }
351 $win->addstr($line, 0, $item);
352 $win->clrtoeol if length $item < $w->{"w"};
353 $win->bkgdset($attr_normal);
358 my $win = $w->{"win"};
359 # Window size might have changed...
360 if ($w->{"1st"} + $w->{"h"} > $w->{"n"}) { $w->{"1st"} = $w->{"n"} - $w->{"h"}; }
361 if ($w->{"1st"} < 0) { $w->{"1st"} = 0; }
363 for (my $i=0; $i<$w->{"h"}; $i++) {
364 $w->redraw_line($w->{"1st"} + $i);
374 if ($i < 0) { $i = 0; }
375 if ($i >= $w->{"n"}) { $i = $w->{"n"}-1; }
377 if ($w->{"visible"}) {
378 $w->redraw_line($oldi);
379 if ($i < $w->{"1st"}) {
380 my $j = $w->{"1st"} - $i;
382 if ($j >= $w->{"h"}/2) {
383 $w->{"win"}->scrollok(1);
384 $w->{"win"}->scrl(-$j);
385 $w->{"win"}->scrollok(0);
386 for (my $k=0; $k<$j; $k++) { $w->redraw_line($i+$k); }
387 } else { $w->redraw; }
388 } elsif ($i >= $w->{"1st"} + $w->{"h"}) {
389 my $j = $i - $w->{"1st"} - $w->{"h"} + 1;
391 if ($j < $w->{"h"}/2) {
392 $w->{"win"}->scrollok(1);
393 $w->{"win"}->scrl($j);
394 $w->{"win"}->scrollok(0);
395 for (my $k=1; $k<=$j; $k++) { $w->redraw_line($i-$j+$k); }
396 } else { $w->redraw; }
397 } else { $w->redraw_line($i); }
398 $w->{"win"}->noutrefresh;
400 if ($auto_enter && $i < $w->{"n"} && $w->{"flist"}->[$i] !~ /\/$/) { $w->select; }
405 if ($w->{"i"} < $w->{"n"}) {
406 my $f = $w->{"flist"}->[$w->{"i"}];
407 my $x = $w->{"list"}->[$w->{"i"}];
410 $w->{"dir"} =~ s@([^/]*/)$@@;
412 $w->{"xdir"} =~ s@[^/]*/$@@;
414 for (my $i=0; $i<$w->{"n"}; $i++) {
415 if ($w->{"flist"}->[$i] eq $back) {
417 $w->{"1st"} = $i - int($w->{"h"}/2);
428 $main_window->view($w->{"dir"} . $f, $w->{"xdir"} . $x);
435 if ($key eq KEY_UP) { $w->go(-1); }
436 elsif ($key eq KEY_DOWN) { $w->go(1); }
437 elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
438 elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
439 elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
440 elsif ($key eq KEY_END) { $w->go(1000000000); }
441 elsif ($key eq "\n" || $key eq "\r" || $key eq KEY_RIGHT) { $w->select; }
442 elsif ($key eq KEY_LEFT) {
443 if ($w->{"list"}->[0] eq "<parent>") {
450 package VS::Window::Status;
451 BEGIN { our @ISA = qw(VS::Window); }
454 my $w = new VS::Window;
455 $w->{"focusable"} = 0;
462 my $win = $w->{"win"};
463 $win->bkgdset($attr_status);
464 $win->addstr(0, 0, $w->{"msg"});
471 if ($w->{"msg"} ne $m) {