Curses-UI
view release on metacpan or search on metacpan
lib/Curses/UI/Listbox.pm view on Meta::CPAN
my %bindings = (
KEY_LEFT() => 'loose-focus',
"h" => 'loose-focus',
CUI_TAB() => 'loose-focus',
KEY_BTAB() => 'loose-focus',
KEY_ENTER() => 'option-select',
KEY_RIGHT() => 'option-select',
"l" => 'option-select',
CUI_SPACE() => 'option-select',
"1" => 'option-check',
"y" => 'option-check',
"0" => 'option-uncheck',
"n" => 'option-uncheck',
KEY_DOWN() => 'option-next',
"j" => 'option-next',
KEY_NPAGE() => 'option-nextpage',
KEY_UP() => 'option-prev',
"k" => 'option-prev',
KEY_PPAGE() => 'option-prevpage',
KEY_HOME() => 'option-first',
"\cA" => 'option-first',
KEY_END() => 'option-last',
"\cE" => 'option-last',
"/" => 'search-forward',
"?" => 'search-backward',
);
sub new ()
{
my $class = shift;
my %userargs = @_;
keys_to_lowercase(\%userargs);
my %args = (
-values => [], # values to show
-labels => {}, # optional labels for the values
-active => 0, # the activated value
-width => undef, # the width of the listbox
-height => undef, # the height of the listbox
-x => 0, # the hor. pos. rel. to parent
-y => 0, # the vert. pos. rel. to parent
-multi => 0, # multiselection possible?
-radio => 0, # show radio buttons? Only for ! -multi
-selected => undef, # the selected item
-wraparound => 0, # wraparound on first/last item
-onchange => undef, # onChange event handler
-onselchange=> undef, # onSelectionChange event handler
-bg => -1,
-fg => -1,
%userargs,
-routines => {%routines},
-bindings => {%bindings},
-yscrpos => 0, # Value init
-focus => 0, # Value init
-nocursor => 1, # This widget does not use a cursor
);
if ($args{-multi})
{
$args{-radio} = 0;
$args{-selected} = {}
unless ref $args{-selected} eq 'HASH';
$args{-ypos} = 0;
} else {
$args{-ypos} = defined $args{-selected} ? $args{-selected} : 0;
}
my $this = $class->SUPER::new( %args );
$this->layout_content();
if ($Curses::UI::ncurses_mouse) {
$this->set_mouse_binding('mouse-button1', BUTTON1_CLICKED());
$this->set_mouse_binding('mouse-button1', BUTTON1_DOUBLE_CLICKED());
}
return $this;
}
sub onChange(;$) { shift()->set_event('-onchange', shift()) }
sub onSelectionChange(;$) { shift()->set_event('-onselchange', shift()) };
sub values(;$)
{
my $this = shift;
my $values = shift;
if (defined $values && ! ref $values) {
$values = [ $values, @_ ];
}
if (defined $values and ref $values eq 'ARRAY') {
# Clear and go to first item if we get new data
$this->clear_selection();
$this->{-values} = $values;
$this->option_first() if defined $values;
# Make this widget non-focusable if there are
# no values in it.
$this->focusable(scalar(@{$values}));
}
return $this->{-values}
}
sub insert_at()
{
my $this = shift;
my $pos = shift;
my $values = shift;
# Clear and go to first item if we get new data
$this->clear_selection();
lib/Curses/UI/Listbox.pm view on Meta::CPAN
my $value = $this->{-values}->[$idx];
my $label = $value;
$label = $this->{-labels}->{$label}
if defined $this->{-labels}->{$label};
$label =~ s/\t/ /g; # do not show TABs
return $label;
}
sub get_active_value($;)
{
my $this = shift;
my $id = $this->{-ypos};
my $value = $this->{'-values'}->[$id];
return $value;
}
sub get_active_id($;)
{
my $this = shift;
return $this->{-ypos};;
}
sub draw(;$)
{
my $this = shift;
my $no_doupdate = shift || 0;
# Draw the widget
$this->SUPER::draw(1) or return $this;
$this->layout_content;
# Let there be color
if ($Curses::UI::color_support) {
my $co = $Curses::UI::color_object;
my $pair = $co->get_color_pair(
$this->{-fg},
$this->{-bg});
$this->{-canvasscr}->attron(COLOR_PAIR($pair));
}
# No values?
if (not @{$this->{-values}})
{
$this->{-canvasscr}->attron(A_DIM);
$this->{-canvasscr}->addstr(0,0,'- no values -');
$this->{-canvasscr}->attroff(A_DIM);
# There are values. Show them!
} else {
my $start_idx = $this->{-yscrpos};
my $end_idx = $this->{-yscrpos} + $this->canvasheight - 1;
$end_idx = $this->{-max_selected}
if $end_idx > $this->{-max_selected};
my $y = 0;
my $cursor_y = 0;
my $cursor_x = 0;
for my $i ($start_idx .. $end_idx)
{
# The label to print.
my $label = $this->getlabel($i);
# Clear up label.
$label =~ s/\n|\r//g;
# Needed space for prefix.
my $prefix_len =
(($this->{-multi} or $this->{-radio}) ? 4 : 0);
# Chop length if needed.
$label = $this->text_chop($label, ($this->canvaswidth-$prefix_len));
# Show current entry in reverse mode and
# save cursor position.
if ($this->{-ypos} == $i and $this->{-focus})
{
$this->{-canvasscr}->attron(A_REVERSE);
$cursor_y = $y;
$cursor_x = $this->canvaswidth-1;
}
# Show selected element bold.
if ( ( not $this->{-multi}
and defined $this->{-selected}
and $this->{-selected} == $i)
or ( $this->{-multi}
and defined $this->{-selected}
and $this->{-selected}->{$i}) ) {
$this->{-canvasscr}->attron(A_BOLD);
}
# Make full line reverse or blank
$this->{-canvasscr}->addstr(
$y, $prefix_len,
" "x($this->canvaswidth-$prefix_len)
);
# Show label
$this->text_draw($y, $prefix_len, $label);
$this->{-canvasscr}->attroff(A_REVERSE);
$this->{-canvasscr}->attroff(A_BOLD);
# Place a [X] for selected value in multi mode.
$this->{-canvasscr}->attron(A_BOLD) if $this->{-focus};
if ($this->{-multi}) {
if (defined $this->{-selected} and
$this->{-selected}->{$i}) {
$this->{-canvasscr}->addstr($y, 0, '[X]');
} else {
$this->{-canvasscr}->addstr($y, 0, '[ ]');
}
}
# Place a <o> for selected value in radio mode.
elsif ($this->{-radio}) {
if (defined $this->{-selected}
and $i == $this->{-selected}) {
$this->{-canvasscr}->addstr($y, 0, '<o>');
} else {
$this->{-canvasscr}->addstr($y, 0, '< >');
}
}
$this->{-canvasscr}->attroff(A_BOLD) if $this->{-focus};
$y++;
}
$cursor_x = 1 if $this->{-multi} or $this->{-radio};
$this->{-canvasscr}->move($cursor_y, $cursor_x);
}
$this->{-canvasscr}->noutrefresh();
doupdate() unless $no_doupdate;
return $this;
}
sub option_last()
{
my $this = shift;
$this->{-ypos} = $this->{-max_selected};
$this->run_event('-onselchange');
$this->schedule_draw(1);
return $this;
}
sub option_nextpage()
{
my $this = shift;
if ($this->{-ypos} >= $this->{-max_selected}) {
$this->dobeep;
return $this;
}
if ($this->{-ypos} + $this->canvasheight - 1 >= $this->{-max_selected}) {
$this->{-ypos} = $this->{-max_selected};
} else {
$this->{-ypos} += $this->canvasheight - 1;
}
$this->run_event('-onselchange');
$this->schedule_draw(1);
return $this;
}
sub option_prevpage()
{
my $this = shift;
if ($this->{-ypos} <= 0) {
$this->dobeep;
return $this;
}
if ($this->{-ypos} - $this->canvasheight - 1 < 0) {
$this->{-ypos} = 0;
} else {
$this->{-ypos} -= $this->canvasheight - 1;
}
$this->run_event('-onselchange');
$this->schedule_draw(1);
return $this;
}
sub clear_selection()
{
my $this = shift;
if ($this->{-multi}) {
my $selection = $this->{-selected};
return unless defined $selection;
foreach my $id (keys %$selection) {
$selection->{$id} = 0;
}
lib/Curses/UI/Listbox.pm view on Meta::CPAN
=item * B<get_active_value> ( )
This method will return the value of the currently active (i.e
highlighted line).
=item * B<get_active_id> ( )
This method will return the index of the currently active (i.e
highlighted line).
=item * B<set_selection> ( LIST )
This method marks the items at the positions specified in LIST
as selected. In a multi-select listbox you can set multiple items
with giving multiple values, in a single-select listbox only the
last item in LIST will be selected
=item * B<clear_selection> ( )
This method clears the selected objects of a multi and radiobutton
listbox.
=item * B<values> ( ARRAYREF )
This method sets the values to use.
=item * B<insert_at> < POS, ARRAYREF|SCALAR >
This method adds ARRAYREF or SCALAR into the list of values at
pos.
=item * B<labels> [ HASHREF ]
This method sets the labels to use.
=item * B<add_labels> [ HASHREF ]
This method adds the given labels to the already defined ones.
=item * B<onChange> ( CODEREF )
This method can be used to set the B<-onchange> event handler
(see above) after initialization of the listbox.
=item * B<onSelectionChange> ( CODEREF )
This method can be used to set the B<-onselchange> event handler
(see above) after initialization of the listbox.
=back
=head1 DEFAULT BINDINGS
=over 4
=item * <B<cursor-left>>, <B<h>>, <B<tab>>
Call the 'loose-focus' routine. This will have the widget
loose its focus.
=item * <B<cursor-right>, <B<l>>, <B<enter>>, <B<space>>
Call the 'option-select' routine. This will select the
active item in the listbox.
=item * <B<1>>, <B<y>>
Call the 'option-check' routine. If the listbox is a
multi-select listbox, the active item will become checked
and the next item will become active.
=item * <B<0>>, <B<n>>
Call the 'option-uncheck' routine. If the listbox is a
multi-select listbox, the active item will become unchecked
and the next item will become active.
=item * <B<cursor-down>>, <B<j>>
Call the 'option-next' routine. This will make the next
item of the list active.
=item * <B<cursor-up>>, <B<k>>
Call the 'option-prev' routine. This will make the previous
item of the list active.
=item * <B<page-up>>
Call the 'option-prevpage' routine. This will make the item
on the previous page active.
=item * <B<page-down>>
Call the 'option-nextpage' routine. This will make the item
on the next page active.
=item * <B<home>>, <B<CTRL+A>>
Call the 'option-first' routine. This will make the first
item of the list active.
=item * <B<end>>, <B<CTRL+E>>
Call the 'option-last' routine. This will make the last
item of the list active.
=item * <B</>>
Call the 'search-forward' routine. This will make a 'less'-like
search system appear in the listbox. A searchstring can be
entered. After that the user can search for the next occurance
using the 'n' key or the previous occurance using the 'N' key.
=item * <B<?>>
Call the 'search-backward' routine. This will do the same as
the 'search-forward' routine, only it will search in the
opposite direction.
=back
=head1 SEE ALSO
L<Curses::UI|Curses::UI>,
L<Curses::UI::Widget|Curses::UI::Widget>,
L<Curses::UI::Common|Curses::UI::Common>
=head1 AUTHOR
Copyright (c) 2001-2002 Maurice Makaay. All rights reserved.
Maintained by Marcus Thiesen (marcus@cpan.thiesenweb.de)
This package is free software and is provided "as is" without express
or implied warranty. It may be used, redistributed and/or modified
( run in 0.827 second using v1.01-cache-2.11-cpan-39bf76dae61 )