]> mj.ucw.cz Git - vs.git/commitdiff
Initial revision
authorMartin Mares <mj@ucw.cz>
Sun, 19 Jan 2003 12:04:07 +0000 (12:04 +0000)
committerMartin Mares <mj@ucw.cz>
Sun, 19 Jan 2003 12:04:07 +0000 (12:04 +0000)
vs.pl [new file with mode: 0755]

diff --git a/vs.pl b/vs.pl
new file mode 100755 (executable)
index 0000000..0063391
--- /dev/null
+++ b/vs.pl
@@ -0,0 +1,465 @@
+#!/usr/bin/perl
+# The Virtual Songbook 0.0
+# (c) 2003 Martin Mares <mj@ucw.cz>
+
+use Curses;
+use strict;
+use warnings;
+
+### Interface with Curses ###
+
+my $W;
+my $color_mode;
+
+sub init_terminal() {
+       $W = new Curses;
+       start_color;
+       $color_mode = (has_colors && COLORS >= 8 && COLOR_PAIRS >= 8) unless defined $color_mode;
+       cbreak; noecho;
+       $W->intrflush(0);
+       $W->keypad(1);
+       $W->meta(1);
+}
+
+sub cleanup_terminal() {
+       endwin;
+}
+
+my ($attr_normal, $attr_status, $attr_hilite, $attr_chord);
+
+sub setup_attrs() {
+       if ($color_mode) {
+               init_pair(1, COLOR_YELLOW, COLOR_BLUE);
+               $attr_status = COLOR_PAIR(1) | A_BOLD;
+               init_pair(2, COLOR_YELLOW, COLOR_BLACK);
+               $attr_chord = COLOR_PAIR(2);
+       } else {
+               $attr_status = A_BOLD;
+               $attr_chord = A_BOLD;
+       }
+       $attr_normal = A_NORMAL;
+       $attr_hilite = A_BOLD;
+}
+
+my $try_full_names = 1;
+my $auto_enter = 1;
+my $file_window_width = 20;
+
+my ($term_w, $term_h);
+my @window_list = ();
+my $status_window = new VS::Window::Status;
+my $main_window = new VS::Window::Main;
+my $file_window = new VS::Window::File;
+$file_window->reload;
+my $focused_window_i = 2;
+my $focused_window = $file_window;
+
+sub focus_next() {
+       $focused_window->{"focused"} = 0;
+       do {
+               $focused_window_i++;
+               if ($focused_window_i > $#window_list) { $focused_window_i=0; }
+               $focused_window = $window_list[$focused_window_i];
+       } while (!$focused_window->{"focusable"} || !$focused_window->{"visible"});
+       $focused_window->{"focused"} = 1;
+}
+
+sub recalc_windows() {
+       my $w = COLS;
+       my $h = LINES;
+       $term_w = $w;
+       $term_h = $h;
+       $status_window->place(0, 0, 1, $w);
+       if ($file_window->{"visible"}) {
+               my $fww = $file_window_width;
+               $main_window->place(1, 0, $h-1, $w-$fww-1);
+               $W->attrset($focused_window == $file_window ? $attr_hilite : $attr_normal);
+               $W->vline(2, $w-$fww-1, ACS_VLINE, $h-3);
+               $W->hline(1, $w-$fww, ACS_HLINE, $fww);
+               $W->hline($h-1, $w-$fww, ACS_HLINE, $fww);
+               $W->addch(1, $w-$fww-1, ACS_ULCORNER);
+               $W->addch($h-1, $w-$fww-1, ACS_LLCORNER);
+               $W->attrset($attr_normal);
+               $file_window->place(2, $w-$fww, $h-3, $fww);
+       } else {
+               $main_window->place(1, 0, $h-1, $w);
+               $file_window->place(0, 0, 0, 0);
+       }
+}
+
+sub toggle_window($) {
+       my $win = shift;
+       if ($win->{"visible"} = !$win->{"visible"}) {
+               while ($focused_window != $win) { focus_next; }
+       } else {
+               if ($focused_window == $win) { focus_next; }
+       }
+       recalc_windows;
+}
+
+init_terminal;
+setup_attrs;
+recalc_windows;
+
+for(;;) {
+       $W->move($term_h-1, $term_w-1);
+       $W->refresh;
+       my $key = $W->getch;
+       if ($key eq "\033" || $key eq "q") {
+               cleanup_terminal;
+               exit 0;
+       } elsif ($key eq "f") {
+               toggle_window($file_window);
+       } elsif ($key eq "\t") {
+               focus_next;
+               recalc_windows;
+       } elsif ($key eq "\014") {
+               $curscr->clearok(1);
+       } elsif ($key eq "<" && $file_window_width < $term_w-1) {
+               $file_window_width++;
+               recalc_windows;
+       } elsif ($key eq ">" && $file_window_width > 1) {
+               $file_window_width--;
+               recalc_windows;
+       } else {
+               $focused_window->key($key);
+       }
+}
+
+### Window Objects ###
+
+package VS::Window;
+
+sub new($) {
+       my $w = {
+               "visible" => 1,
+               "focusable" => 1,
+               "focused" => 0,
+               "x" => -1,
+               "y" => -1,
+               "w" => -1,
+               "h" => -1
+       };
+       push @window_list, $w;
+       return bless $w;
+}
+
+sub place($$$$$) {
+       my ($w,$nx,$ny,$nh,$nw) = @_;
+       if ($w->{"visible"}) {
+               if (!defined $w->{"win"} || $w->{"x"} != $nx || $w->{"y"} != $ny
+                || $w->{"w"} != $nw || $w->{"h"} != $nh) {
+                       $w->{"win"} = $W->subwin($nh,$nw,$nx,$ny);
+                       $w->{"x"} = $nx;
+                       $w->{"y"} = $ny;
+                       $w->{"w"} = $nw;
+                       $w->{"h"} = $nh;
+                       $w->redraw;
+               }
+       } else {
+               delete $w->{"win"};
+       }
+}
+
+sub key($) { }
+sub redraw($) { }
+
+package VS::Window::Main;
+use Curses;
+BEGIN { our @ISA = qw(VS::Window); }
+
+sub new($) {
+       my $w = new VS::Window;
+       $w->{"file"} = "";
+       $w->{"attrs"} = {};
+       $w->{"lines"} = ["", "", "   The Virtual Songbook 0.0\n", "   (c) 2003 Martin Mares <mj\@ucw.cz>"];
+       $w->{"linetype"} = [0,0,0,0];
+       $w->{"n"} = 4;
+       $w->{"top"} = 0;
+       return bless $w;
+}
+
+sub view($$$) {
+       my ($w,$f,$x) = @_;
+       if ($w->{"file"} ne $f) {
+               $w->{"file"} = $f;
+               $w->{"xfile"} = $x;
+               $f =~ s@^./@@;
+               $x =~ s@^./@@;
+               if (open X, $f) {
+                       my %attrs = ();
+                       while (<X>) {
+                               chomp;
+                               /^$/ && last;
+                               if (/^(\w+):\s*(.*)/ && !defined $attrs{$1}) {
+                                       $attrs{$1} = $2;
+                               }
+                       }
+                       my @lines = ();
+                       my @types = ();
+                       while (<X>) {
+                               chomp;
+                               if (s/^!//) { push @types, 1; } else { push @types, 0; }
+                               push @lines, $_;
+                       }
+                       close X;
+                       $w->{"attrs"} = \%attrs;
+                       $w->{"lines"} = \@lines;
+                       $w->{"linetypes"} = \@types;
+                       $w->{"top"} = 0;
+                       $w->{"n"} = scalar @lines;
+                       $w->redraw;
+                       if (defined $attrs{"Name"}) {
+                               $x = $attrs{"Name"};
+                               $x = $attrs{"Author"} . ": $x" if defined $attrs{"Author"};
+                       }
+                       $status_window->tell($x);
+               } else {
+                       $status_window->tell("Cannot open $f");
+               }
+       }
+}
+
+sub redraw_line($$) {
+       my ($w,$i) = @_;
+       my $win = $w->{"win"};
+       my $l = $w->{"lines"}->[$i];
+       if ($w->{"linetypes"}->[$i]) { $win->attrset($attr_chord); }
+       if (length $l < $w->{"w"}) {
+               $win->addstr($i-$w->{"top"}, 0, $l);
+               $win->clrtoeol;
+       } else {
+               $win->addstr($i-$w->{"top"}, 0, substr($l, 0, $w->{"w"}));
+       }
+       $win->attrset($attr_normal);
+}
+
+sub redraw($) {
+       my $w = shift @_;
+       my $win = $w->{"win"};
+       my $top = $w->{"top"};
+       my $cnt = $w->{"n"} - $w->{"top"};
+       if ($cnt > $w->{"h"}) { $cnt = $w->{"h"}; }
+       for (my $i=$top; $i<$top+$cnt; $i++) { $w->redraw_line($i); }
+       if ($cnt < $w->{"h"}) {
+               $win->move($cnt, 0);
+               $win->clrtobot;
+       }
+       $win->noutrefresh;
+}
+
+sub go($$) {
+       my ($w,$delta) = @_;
+       my $win = $w->{"win"};
+       my $top = $w->{"top"} + $delta;
+       if ($top + $w->{"h"} > $w->{"n"}) { $top = $w->{"n"} - $w->{"h"}; }
+       if ($top < 0) { $top = 0; }
+       my $otop = $w->{"top"};
+       $w->{"top"} = $top;
+       if ($top < $otop - $w->{"h"}/2) {
+               $w->redraw;
+       } elsif ($top < $otop) {
+               my $j = $otop - $top;
+               $win->scrollok(1);
+               $win->scrl(-$j);
+               $win->scrollok(0);
+               for (my $i=0; $i<$j; $i++) { $w->redraw_line($top+$i); }
+       } elsif ($top == $otop) {
+               # Nothing happens
+       } elsif ($top < $otop + $w->{"h"}/2) {
+               my $j = $top - $otop;
+               $win->scrollok(1);
+               $win->scrl($j);
+               $win->scrollok(0);
+               for (my $i=$j; $i>0; $i--) { $w->redraw_line($top+$w->{"h"}-$i); }
+       } else {
+               $w->redraw;
+       }
+       $win->noutrefresh;
+}
+
+sub key($$) {
+       my ($w,$key) = @_;
+       if ($key eq KEY_UP) { $w->go(-1); }
+       elsif ($key eq KEY_DOWN) { $w->go(1); }
+       elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
+       elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
+       elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
+       elsif ($key eq KEY_END) { $w->go(1000000000); }
+       else { $status_window->tell("Unknown key <$key>"); }
+}
+
+package VS::Window::File;
+use Curses;
+BEGIN { our @ISA = qw(VS::Window); }
+
+sub new($) {
+       my $w = new VS::Window;
+       $w->{"dir"} = "./";
+       $w->{"xdir"} = "./";
+       return bless $w;
+}
+
+sub reload($) {
+       my $w = shift;
+       my $p = $w->{"dir"};
+       my @l = `cd $p && ls`;
+       my @fn = ();
+       my @full = ();
+       if ($p ne "./") { push @fn, "../"; push @full, "<parent>"; }
+       foreach my $x (@l) {
+               chomp $x;
+               if (-f "$p/$x") {
+                       push @fn, $x;
+                       my $fullname = $x;
+                       if ($try_full_names && open(X, "$p/$x")) {
+                               while (<X>) {
+                                       chomp;
+                                       /^$/ && last;
+                                       if (/^Name:\s*(.*)/) {
+                                               $fullname = $1;
+                                               last;
+                                       }
+                               }
+                               close X;
+                       }
+                       push @full, $fullname;
+               } elsif (-d "$p/$x") { push @fn, "$x/"; push @full, "$x/"; }
+       }
+       $w->{"flist"} = \@fn;
+       $w->{"list"} = \@full;
+       $w->{"n"} = scalar @fn;
+       $w->{"i"} = 0;
+       $w->{"1st"} = 0;
+}
+
+sub redraw_line($$) {
+       my ($w,$i) = @_;
+       my $line = $i - $w->{"1st"};
+       if ($line < 0 || $line >= $w->{"h"}) { return; }
+       my $win = $w->{"win"};
+       my $item = ($i < $w->{"n"}) ? substr($w->{"list"}->[$i], 0, $w->{"w"}) : "";
+       if ($i == $w->{"i"}) { $win->bkgdset($attr_status); }
+       $win->addstr($line, 0, $item);
+       $win->clrtoeol if length $item < $w->{"w"};
+       $win->bkgdset($attr_normal);
+}
+
+sub redraw($) {
+       my $w = shift @_;
+       $w->{"win"}->idlok(1);
+       for (my $i=0; $i<$w->{"h"}; $i++) {
+               $w->redraw_line($w->{"1st"} + $i);
+       }
+       $w->{"win"}->noutrefresh;
+}
+
+sub go($$) {
+       my ($w,$delta) = @_;
+       my $i = $w->{"i"};
+       my $oldi = $i;
+       $i += $delta;
+       if ($i < 0) { $i = 0; }
+       if ($i >= $w->{"n"}) { $i = $w->{"n"}-1; }
+       $w->{"i"} = $i;
+       if ($w->{"visible"}) {
+               $w->redraw_line($oldi);
+               if ($i < $w->{"1st"}) {
+                       my $j = $w->{"1st"} - $i;
+                       $w->{"1st"} = $i;
+                       if ($j >= $w->{"h"}/2) {
+                               $w->{"win"}->scrollok(1);
+                               $w->{"win"}->scrl(-$j);
+                               $w->{"win"}->scrollok(0);
+                               for (my $k=0; $k<$j; $k++) { $w->redraw_line($i+$k); }
+                       } else { $w->redraw; }
+               } elsif ($i >= $w->{"1st"} + $w->{"h"}) {
+                       my $j = $i - $w->{"1st"} - $w->{"h"} + 1;
+                       $w->{"1st"} += $j;
+                       if ($j < $w->{"h"}/2) {
+                               $w->{"win"}->scrollok(1);
+                               $w->{"win"}->scrl($j);
+                               $w->{"win"}->scrollok(0);
+                               for (my $k=1; $k<=$j; $k++) { $w->redraw_line($i-$j+$k); }
+                       } else { $w->redraw; }
+               } else { $w->redraw_line($i); }
+               $w->{"win"}->noutrefresh;
+       }
+       if ($auto_enter && $i < $w->{"n"} && $w->{"flist"}->[$i] !~ /\/$/) { $w->select; }
+}
+
+sub select($) {
+       my ($w) = @_;
+       if ($w->{"i"} < $w->{"n"}) {
+               my $f = $w->{"flist"}->[$w->{"i"}];
+               my $x = $w->{"list"}->[$w->{"i"}];
+               if ($f =~ /\/$/) {
+                       if ($f eq "../") {
+                               $w->{"dir"} =~ s@([^/]*/)$@@;
+                               my $back = $1;
+                               $w->{"xdir"} =~ s@[^/]*/$@@;
+                               $w->reload;
+                               for (my $i=0; $i<$w->{"n"}; $i++) {
+                                       if ($w->{"flist"}->[$i] eq $back) {
+                                               $w->{"i"} = $i;
+                                               if ($i > $w->{"h"}/2) { $w->{"1st"} = $i - int($w->{"h"}/2); }
+                                               if ($w->{"1st"} + $w->{"h"} > $w->{"n"}) { $w->{"1st"} = $w->{"n"} - $w->{"h"}; }
+                                               if ($w->{"1st"} < 0) { $w->{"1st"} = 0; }
+                                               last;
+                                       }
+                               }
+                       } else {
+                               $w->{"dir"} .= $f;
+                               $w->{"xdir"} .= $x;
+                               $w->reload;
+                       }
+                       $w->redraw;
+               } else {
+                       $main_window->view($w->{"dir"} . $f, $w->{"xdir"} . $x);
+               }
+       }
+}
+
+sub key($$) {
+       my ($w,$key) = @_;
+       if ($key eq KEY_UP) { $w->go(-1); }
+       elsif ($key eq KEY_DOWN) { $w->go(1); }
+       elsif ($key eq KEY_PPAGE) { $w->go(-$w->{"h"}-1); }
+       elsif ($key eq KEY_NPAGE) { $w->go($w->{"h"}-1); }
+       elsif ($key eq KEY_HOME) { $w->go(-1000000000); }
+       elsif ($key eq KEY_END) { $w->go(1000000000); }
+       elsif ($key eq "\n" || $key eq "\r" || $key eq KEY_RIGHT) { $w->select; }
+       elsif ($key eq KEY_LEFT) {
+               if ($w->{"list"}->[0] eq "<parent>") {
+                       $w->{"i"} = 0;
+                       $w->select;
+               }
+       }
+}
+
+package VS::Window::Status;
+BEGIN { our @ISA = qw(VS::Window); }
+
+sub new($) {
+       my $w = new VS::Window;
+       $w->{"focusable"} = 0;
+       $w->{"msg"} = "";
+       return bless $w;
+}
+
+sub redraw($) {
+       my $w = shift @_;
+       my $win = $w->{"win"};
+       $win->bkgdset($attr_status);
+       $win->addstr(0, 0, $w->{"msg"});
+       $win->clrtoeol;
+       $win->refresh;
+}
+
+sub tell($$) {
+       my ($w,$m) = @_;
+       if ($w->{"msg"} ne $m) {
+               $w->{"msg"} = $m;
+               $w->redraw;
+       }
+}