# $Id: CPronto.pm,v 1.2 2001/02/03 14:56:34 muhri Exp $
# -*- perl -*-
# coypright (c) 2000 Charlie Schmidt <ishamael@themes.org>
package Pronto::CPronto;
use Curses;
use strict;
1;
sub new {
my $class = shift;
my $self = {};
my ($r,$c,$w);
initscr();
start_color();
init_pair(0,COLOR_WHITE,COLOR_BLACK);
init_pair(1,COLOR_RED,COLOR_BLACK);
init_pair(2,COLOR_BLUE,COLOR_BLACK);
init_pair(3,COLOR_GREEN,COLOR_BLACK);
init_pair(4,COLOR_CYAN,COLOR_BLACK);
raw();
cbreak();
noecho();
nonl();
nodelay(0);
intrflush(Curses::stdscr,0);
keypad(1);
getmaxyx($r,$c);
if ($r < 24 || $c < 80) {
print _("Your terminal must have at least 24 rows and 80 columns to run Pronto::CPronto\n");
exit(1);
}
$w = new Curses;
$self = { rows => $r,
cols => $c,
window => $w,
current => 0,
num_widgets => 0,
widgets => ()};
bless ($self,$class);
return $self;
}
sub add_widget {
my $self = shift;
my ($widget) = @_;
$self->{widgets}[$self->{num_widgets}] = $widget;
$self->{num_widgets}++;
return;
}
sub remove_widget {
my $self = shift;
my ($widget) = @_;
my $i;
for ($i = 0; $i < $self->{num_widgets}; $i++) {
if ($self->{widgets}[$i] == $widget) {
splice(@{$self->{widgets}},$i,1);
$self->{num_widgets}--;
return;
}
}
return;
}
sub next_widget {
my $self = shift;
if ($self->{widgets}[$self->{current}]->{unfocus}) {
&{$self->{widgets}[$self->{current}]->{unfocus}}($self);
}
$self->{widgets}[$self->{current}]->unfocus();
$self->{current}++;
if ($self->{current} >= $self->{num_widgets}) {
$self->{current} = 0;
}
if ($self->{widgets}[$self->{current}]->{isntwid}) {
$self->next_widget();
}
if ($self->{widgets}[$self->{current}]->{focus}) {
&{$self->{widgets}[$self->{current}]->{focus}}($self);
}
$self->{widgets}[$self->{current}]->focus();
refresh();
return;
}
sub change_widget {
my $self = shift;
my ($widget) = @_;
my $i;
for ($i = 0; $i <= $self->{num_widgets}; $i++) {
if ($self->{widgets}[$i] == $widget) {
$self->{current} = $i;
}
}
return;
}
sub addstring {
my $self = shift;
my ($y,$x,$string) = @_;
move($y,$x);
addstr("$string");
refresh();
return;
}
sub show_widgets {
my $self = shift;
print "Widgets: ",$self->{num_widgets},"\n";
foreach (@{$self->{widgets}}) {
print "\t",$_,"\n";
}
return;
}
sub main_loop {
my $self = shift;
while (1) {
my $key = getch();
my $nkey = unpack('c',$key);
if ($nkey == 13) {
$self->{widgets}[$self->{current}]->activate();
next;
}
if ($nkey == 9) {
$self->next_widget();
next;
}
if (!$self->{widgets}[$self->{current}]->{istext}) {
if ($self->{"signal_$key"}) {
&{$self->{"signal_$key"}}($self);
next;
}
if ($self->{"signal_n$nkey"}) {
&{$self->{"signal_n$nkey"}}($self);
next;
}
}
$self->{widgets}[$self->{current}]->key($key);
$self->{widgets}[$self->{current}]->redraw();
}
}
sub quit {
my $self = shift;
$self->{window}->delwin();
endwin();
}
sub signal_connect {
my $self = shift;
my ($signal,$func) = @_;
$self->{$signal} = $func;
return;
}
sub clear {
my $self = shift;
my ($i,$j);
for ($i = 0; $i <= $self->{cols}; $i++) {
for ($j = 0; $j <= $self->{rows}; $j++) {
move($j,$i);
addch(" ");
}
}
refresh();
return;
}
package Pronto::CPronto::Button;
use Curses;
use strict;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { label => $params{label},
x => $params{x},
y => $params{y}};
bless($self,$class);
return $self;
}
sub show {
my $self = shift;
move($self->{y},$self->{x});
addstr("|$self->{label}|");
refresh();
return;
}
sub hide {
my $self = shift;
my $i;
for ($i = 0; $i <= length($self->{label})+1; $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
}
refresh();
return;
}
sub remove {
my $self = shift;
my $i;
for ($i = 0; $i <= length($self->{label})+2; $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
}
refresh();
return;
}
sub unfocus {
my $self = shift;
move($self->{y},$self->{x});
addstr("|$self->{label}|");
refresh();
return;
}
sub focus {
my $self = shift;
move($self->{y},$self->{x});
attron(A_BOLD);
addstr("*$self->{label}*");
attroff(A_BOLD);
refresh();
return;
}
sub key {
return;
}
sub redraw {
return;
}
sub activate {
my $self = shift;
if ($self->{activate}) {
&{$self->{activate}}($self);
}
return;
}
sub signal_connect {
my $self = shift;
my ($signal,$func) = @_;
$self->{$signal} = $func;
return;
}
package Pronto::CPronto::Label; # not a widget
use Curses;
1;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { text => $params{text},
x => $params{x},
y => $params{y},
isntwid => 1};
bless($self,$class);
return $self;
}
sub show {
my $self = shift;
move($self->{y},$self->{x});
addstr("$self->{text}");
refresh();
return;
}
sub hide {
my $self = shift;
my $i;
move($self->{y},$self->{x});
for($i = 0; $i < length($self->{text}); $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
}
return;
}
sub unfocus {
return;
}
sub focus {
return;
}
sub redraw {
my $self = shift;
$self->show();
return;
}
package Pronto::CPronto::Entry;
use Curses;
1;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { text => $params{text},
position=> length($params{text}),
x => $params{x},
y => $params{y},
limit => $params{limit},
editable=> $params{editable},
istext => 1};
if ($self->{limit} == 0 or !$self->{limit}) {
$self->{limit} = -1;
}
bless($self,$class);
return $self;
}
sub show {
my $self = shift;
move($self->{y},$self->{x});
addch(">");
$self->redraw();
return;
}
sub redraw {
my $self = shift;
$self->clear();
move($self->{y},$self->{x}+1);
addstr($self->{text});
move($self->{y},$self->{x}+1+$self->{position});
refresh();
return;
}
sub hide {
my $self = shift;
my $i;
for ($i = 0; $i <= length($self->{text}); $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
}
refresh();
return;
}
sub clear {
my $self = shift;
my $i;
for ($i = 0; $i <= length($self->{text}); $i++) {
move($self->{y},$self->{x}+1+$i);
addch(" ");
}
refresh();
}
sub set_text {
my $self = shift;
my ($text) = @_;
$text = &fix_length($text,$self->{limit});
$self->{text} = $text;
$self->{position} = length($text);
return;
}
sub get_text {
my $self = shift;
my $text = $self->{text};
return $text;
}
sub fix_length {
my ($string,$length) = @_;
if (length($string) > $length) {
substr($string,$length) = "";
}
return $string;
}
sub activate {
my $self = shift;
if ($self->{activate}) {
&{$self->{activate}}($self);
}
return;
}
sub focus {
my $self = shift;
move($self->{y},$self->{x});
attron(A_BOLD);
addch(">");
attroff(A_BOLD);
move($self->{y},$self->{x}+length($self->{text})+1);
refresh();
return;
}
sub unfocus {
my $self = shift;
move($self->{y},$self->{x});
addch(">");
refresh();
return;
}
sub key {
my $self = shift;
my ($key) = @_;
if ($key eq KEY_DOWN or $key eq KEY_UP) {
return;
}
if ($key eq KEY_BACKSPACE) {
if (length($self->{text}) == 0) {
return;
}
$self->{text} = (substr($self->{text},0,$self->{position}-1)).(substr($self->{text},$self->{position}));
$self->{position}--;
return;
}
if ($key eq KEY_DC) {
if (length($self->{text}) == 0 || $self->{position} == length($self->{text})) {
return;
}
$self->{text} = (substr($self->{text},0,$self->{position})).(substr($self->{text},$self->{position}+1));
return;
}
if ($key eq KEY_LEFT) {
if ($self->{position} == 0) {
return;
}
$self->{position}--;
return;
}
if ($key eq KEY_RIGHT) {
if ($self->{position} == length($self->{text})) {
return;
}
$self->{position}++;
return;
}
if ($self->{limit} == length($self->{text})) {
return;
}
my @text;
$self->{text} = (substr($self->{text},0,$self->{position})).($key).(substr($self->{text},$self->{position}));
$self->{position}++;
return;
}
sub signal_connect {
my $self = shift;
my ($signal,$func) = @_;
$self->{$signal} = $func;
return;
}
package Pronto::CPronto::Box; #not a widget
use Curses;
1;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { rows => $params{rows},
cols => $params{cols},
x => $params{x},
y => $params{y}};
bless($self,$class);
return($self);
}
sub show {
my $self = shift;
my $i;
for ($i = 1; $i < $self->{cols}; $i++) {
move($self->{y},$self->{x}+$i);
addch("-");
move($self->{y}+$self->{rows},$self->{x}+$i);
addch("-");
}
for ($i = 1; $i < $self->{rows}; $i++) {
move($self->{y}+$i,$self->{x});
addch("|");
move($self->{y}+$i,$self->{x}+$self->{cols});
addch("|");
}
refresh();
return;
}
sub hide {
my $self = shift;
my $i;
for ($i = 1; $i < $self->{cols}; $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
move($self->{y}+$self->{rows},$self->{x}+$i);
addch(" ");
}
for ($i = 1; $i < $self->{rows}; $i++) {
move($self->{y}+$i,$self->{x});
addch(" ");
move($self->{y}+$i,$self->{x}+$self->{cols});
addch(" ");
}
refresh();
return;
}
package Pronto::CPronto::List;
use Curses;
1;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { titles => $params{titles},
x => $params{x},
y => $params{y},
height => $params{height},
widths => $params{widths},
shown_current => 0,
shown_top => 0,
current => 0,
num_rows => 0,
rows => (())};
bless($self,$class);
return $self;
}
sub show {
my $self = shift;
my ($i,$j,$k);
$j = 0;
$k = 0;
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
$j = $j + $self->{widths}[$i];
$k = $k + $self->{widths}[$i+1];
move($self->{y},$self->{x}+$j);
addstr("$self->{titles}[$i]");
}
for ($i = 0; $i <= $k; $i++) {
move($self->{y}+1,$self->{x}+$i);
addch("-");
}
$self->redraw();
return;
}
sub hide {
my $self = shift;
my ($i,$j);
$j = 0;
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
$j = $j + $self->{widths}[$i+1];
}
for ($i = 0; $i <= $j; $i++) {
move($self->{y},$self->{x}+$i);
addch(" ");
move($self->{y}+1,$self->{x}+$i);
addch(" ");
}
$self->clear();
refresh();
return;
}
sub focus {
my $self = shift;
my ($i,$j);
$j = 0;
attron(A_BOLD);
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
$j = $j + $self->{widths}[$i];
move($self->{y},$self->{x}+$j);
addstr("$self->{titles}[$i]");
}
attroff(A_BOLD);
$self->redraw();
return;
}
sub unfocus {
my $self = shift;
$self->show();
$self->unselect_row();
}
sub redraw {
my $self = shift;
my ($i,$j,$k,$l);
$k = 0;
$j = 0;
$self->clear();
for ($l = $self->{top}; $l < $self->{top}+$self->{height}; $l++) {
$j = 0;
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
if ($k == $self->{shown_current}) {
attron(A_BOLD);
}
$j = $j + $self->{widths}[$i];
move($self->{y}+$k+2,$self->{x}+$j);
my $string = &fix_length($self->{rows}[$l][$i],$self->{widths}[$i+1]-1);
addstr("$string");
if ($k == $self->{shown_current}) {
attroff(A_BOLD);
}
}
$k++;
}
refresh();
return;
}
sub clear {
my $self = shift;
my ($i,$j,$k);
$j = 0;
for ($i = 0; $i <= $#{$self->{titles}}+1; $i++) {
$j = $j + $self->{widths}[$i];
}
for ($i = 0; $i <= $j; $i++) {
for ($k = 0; $k < $self->{height}; $k++) {
move($self->{y}+$k+2,$self->{x}+$i);
addch(" ");
}
}
return;
}
sub select_row {
my $self = shift;
my ($row) = @_;
my ($i,$j);
attron(A_BOLD);
$j = 0;
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
$j = $j + $self->{widths}[$i];
move($self->{y}+$row+2,$self->{x}+$j);
my $string = &fix_length($self->{rows}[$row][$i],$self->{widths}[$i+1]-1);
addstr("$string");
}
attroff(A_BOLD);
$self->{current} = $row;
refresh();
return;
}
sub unselect_row {
my $self = shift;
my $row = $self->{current};
my ($i,$j);
$j = 0;
for ($i = 0; $i <= $#{$self->{titles}}; $i++) {
$j = $j + $self->{widths}[$i];
move($self->{y}+$self->{shown_current}+2,$self->{x}+$j);
my $string = &fix_length($self->{rows}[$row][$i],$self->{widths}[$i+1]-1);
addstr("$string");
}
refresh();
return;
}
sub get_item_data {
my $self = shift;
my ($row,$col) = @_;
my $data = $self->{rows}[$row][$col];
return ($data);
}
sub set_item_data {
my $self = shift;
my ($row,$col,$data) = @_;
$self->{rows}[$row][$col] = $data;
$self->redraw();
refresh();
return;
}
sub get_selected {
my $self = shift;
my $i = $self->{current};
return $i;
}
sub add_row {
my $self = shift;
my ($num,$row) = @_;
my ($i,$j);
$self->{rows}[$num] = $row;
$self->{num_rows}++;
return;
}
sub del_row {
my $self = shift;
my ($num) = @_;
my ($i,$j);
splice(@{$self->{rows}},$num,1);
$self->{num_rows}--;
return;
}
sub fix_length {
my ($string,$length) = @_;
if (length($string) > $length) {
substr($string,$length) = "";
}
return $string;
}
sub activate {
my $self = shift;
if ($self->{activate}) {
&{$self->{activate}}($self);
}
return;
}
sub key {
my $self = shift;
my ($key) = @_;
if ($key eq KEY_UP) {
if ($self->{current} == 0) {
return;
}
if ($self->{shown_current} == 0) {
$self->{top}--;
} else {
$self->{shown_current}--;
}
$self->{current}--;
return;
}
if ($key eq KEY_DOWN) {
if ($self->{current} >= $self->{num_rows}-1) {
return;
}
if ($self->{shown_current}+1 == $self->{height}) {
$self->{top}++;
} else {
$self->{shown_current}++;
}
$self->{current}++;
return;
}
if ($key eq KEY_NPAGE) { #page down
if ($self->{current} >= $self->{num_rows}-1) {
return;
}
my $i;
for ($i = 0; $i <= $self->{height}-1; $i++) {
$self->key(KEY_DOWN);
}
return;
}
if ($key eq KEY_PPAGE) { #page up
if ($self->{current} == 0) {
return;
}
my $i;
for ($i = 0; $i <= $self->{height}-1; $i++) {
$self->key(KEY_UP);
}
return;
}
return;
}
sub signal_connect {
my $self = shift;
my ($signal,$func) = @_;
$self->{$signal} = $func;
return;
}
package Pronto::CPronto::Text;
use Curses;
1;
sub new {
my $class = shift;
my %params = @_;
my $self = {};
$self = { text => $params{text},
x => $params{x},
y => $params{y},
height => $params{height},
width => $params{width},
editable => $params{editable},
xpos => $params{xpos},
ypos => $params{ypos},
top_line => $params{topline},
current_line => $params{current_line},
position => $params{position},
istext => 1};
bless($self,$class);
return $self;
}
sub show {
my $self = shift;
$self->redraw();
return;
}
sub hide {
my $self = shift;
$self->clear();
refresh();
return;
}
sub activate {
my $self = shift;
$self->key("\n");
return;
}
sub get_text {
my $self = shift;
my $text;
$text = $self->{text};
return $text;
}
sub set_text {
my $self = shift;
my ($text) = @_;
$self->{text} = $text;
return;
}
sub redraw {
my $self = shift;
my (@text,$i,$j,$k);
$self->clear();
move($self->{y},$self->{x});
@text = split(/\n/,$self->{text});
$j = $self->{top_line}+$self->{height};
$k = 0;
for ($i = $self->{top_line}; $i <= $j; $i++) {
move($self->{y}+$k,$self->{x});
addstr($text[$i]);
$k++;
}
move($self->{y}+$self->{ypos},$self->{x}+$self->{xpos});
return;
}
sub clear {
my $self = shift;
my ($i,$j);
for ($i = 0; $i <= $self->{width}; $i++) {
for ($j = 0; $j <= $self->{height}; $j++) {
move($self->{y}+$j,$self->{x}+$i);
addch(" ");
}
}
return;
}
sub focus {
my $self = shift;
move($self->{y}+$self->{ypos},$self->{x}+$self->{xpos});
refresh();
return;
}
sub unfocus {
move(0,0);
refresh();
return;
}
sub key {
my $self = shift;
my ($key) = @_;
if ($key eq KEY_DOWN) {
my @text = split(/\n/,$self->{text});
if ($self->{current_line} == $#text+1) {
return;
}
if ($self->{ypos} == $self->{height}) {
$self->{top_line}++;
$self->{current_line}++;
$self->{position} = $self->{position} + length($text[$self->{current_line}-1])+1;
return;
}
$self->{current_line}++;
$self->{ypos}++;
$self->{position} = $self->{position} + length($text[$self->{current_line}-1])+1;
until ($self->{xpos} <= length($text[$self->{ypos}])) {
$self->{xpos}--;
$self->{position}--;
}
return;
}
if ($key eq KEY_UP) {
if ($self->{current_line} == 0) {
return;
}
my @text = split(/\n/,$self->{text});
if ($self->{ypos} == 0 && $self->{current_line} == $self->{top_line}) {
$self->{top_line}--;
$self->{current_line}--;
$self->{position} = $self->{position} - length($text[$self->{current_line}])-1;
return;
}
$self->{current_line}--;
$self->{ypos}--;
$self->{position} = $self->{position} - length($text[$self->{current_line}])-1;
until ($self->{xpos} <= length($text[$self->{ypos}])) {
$self->{xpos}--;
$self->{position}--;
}
return;
}
if ($key eq KEY_LEFT) {
if ($self->{position} == 0 || $self->{xpos} == 0) {
return;
}
$self->{xpos}--;
$self->{position}--;
return;
}
if ($key eq KEY_RIGHT) {
my @text = split(/\n/,$self->{text});
if ($self->{xpos} == length($text[$self->{ypos}]) || $self->{position} == length($self->{text})) {
return;
}
$self->{xpos}++;
$self->{position}++;
return;
}
if ($key eq KEY_DC) {
if ($self->{position} == length($self->{text}) || $self->{editable} == 0) {
return;
}
$self->{text} = (substr($self->{text},0,$self->{position})).(substr($self->{text},$self->{position}+1));
return;
}
if ($key eq KEY_BACKSPACE) {
if ($self->{position} == 0 || $self->{editable} == 0) {
return;
}
$self->{text} = (substr($self->{text},0,$self->{position}-1)).(substr($self->{text},$self->{position}));
if ($self->{current_line} == $self->{top_line} && $self->{xpos} == 0) {
$self->{top_line}--;
}
$self->{position}--;
if ($self->{xpos} == 0) {
my @text = split(/\n/,$self->{text});
$self->{ypos}--;
$self->{current_line}--;
$self->{position} = $self->{position} - length($text[$self->{current_line}]);
until ($self->{xpos} == length($text[$self->{ypos}])) {
$self->{xpos}++;
$self->{position}++;
}
} else {
$self->{xpos}--;
}
return;
}
if ($self->{editable} == 1) {
if ($key eq "\n") {
if ($self->{ypos} == $self->{height}) {
$self->{top_line}++;
$self->{ypos}--;
}
$self->{xpos} = $self->{x}-2;
$self->{ypos}++;
$self->{current_line}++;
}
$self->{text} = (substr($self->{text},0,$self->{position})).($key).(substr($self->{text},$self->{position}));
$self->{position}++;
if ($self->{xpos} == $self->{width}) {
$self->key("\n");
$self->{xpos} = $self->{x}-1;
}
$self->{xpos}++;
$self->redraw();
return;
}
return;
}
sub signal_connect {
my $self = shift;
my ($signal,$func) = @_;
$self->{$signal} = $func;
return;
}
syntax highlighted by Code2HTML, v. 0.9.1