package Lire::UI::ListWidget; use strict; use base qw/ Curses::UI::Container Lire::UI::Widget /; use Carp; use Curses; use Curses::UI::Common; use Locale::TextDomain 'lire'; use Lire::Utils qw/ check_object_param text_for_width /; use Lire::UI::Utils qw/ layout_buttonbox /; use Lire::UI::ScalarListWidget; use Lire::UI::CompoundListWidget; use Lire::UI::PolyListWidget; use vars qw/@CARP_NOT/; @CARP_NOT = qw/Curses::UI::Container/; my %bindings = ( KEY_IC() => 'add-element', "\cc" => "copy-element", KEY_DC() => 'del-element', "J" => 'move-element-down', "K" => 'move-element-up', ); my %routines = ( 'add-element' => \&add_element, 'copy-element' => \©_element, 'move-element' => \&move_element, 'move-element-up' => \&move_element_up, 'move-element-down' => \&move_element_down, 'del-element' => \&del_element ); sub new { my $class = shift; my %userargs = @_; keys_to_lowercase(\%userargs); check_object_param( $userargs{'value'}, 'value', 'Lire::Config::List' ); my $spec = $userargs{'value'}->spec(); my @comp_names = $spec->component_names(); my $component = @comp_names ? $spec->get( $comp_names[0] ) : undef; if ( $class eq __PACKAGE__) { croak "'" . $spec->name() . "' should contain at least one component" unless $component; if ( @comp_names > 1 ) { return new Lire::UI::PolyListWidget( @_ ); } elsif ( $component->isa( 'Lire::Config::ScalarSpec' ) ) { return new Lire::UI::ScalarListWidget( @_ ); } else { return new Lire::UI::CompoundListWidget( @_ ); } } my %userlistargs = (); foreach my $p ( qw/-title -titlereverse -titlefullwidth / ) { if ( exists $userargs{$p} ) { $userlistargs{$p} = $userargs{$p}; delete $userargs{$p}; } } my $self = $class->Curses::UI::Container::new( %userargs, 'component' => $component, '-bindings' => {%bindings}, '-routines' => {%routines}, '-releasefocus' => 1, ); my $list_widget = $self->add( 'list', 'Listbox', %userlistargs, '-onselchange' => \&_sel_change_cb, '-vscrollbar' => 1, '-border' => 1, '-selected' => 0 ); $self->refresh_view(); $self->add_contained_widgets(); $self->layout(); return $self; } sub extra_widget_height { return 0; } sub layout_contained_objects { my $self = $_[0]; return $self unless $self->getobj( 'list' ); my $list = $self->getobj( 'list' ); $list->{'-width'} = $self->canvaswidth(); $list->{'-height'} = $self->canvasheight() - $self->extra_widget_height() - 1; $list->{'-title'} = text_for_width( $self->title(), $self->canvaswidth() - 4 ) if ( $self->title() ); $self->layout_buttons(); return $self->SUPER::layout_contained_objects(); } sub layout_buttons { my $self = $_[0]; my $buttons = $self->getobj( 'buttons' ); $buttons->{'-buttons'}[0]{'-label'} = __( 'Add' ); $buttons->{'-buttons'}[2]{'-label'} = __( 'Copy' ); $buttons->{'-buttons'}[3]{'-label'} = __( 'Delete' ); $buttons->{'-buttons'}[4]{'-label'} = __( 'Up' ); $buttons->{'-buttons'}[5]{'-label'} = __( 'Down' ); layout_buttonbox( $self, $buttons, 1, 1 ); $buttons->{'-y'} = $self->canvasheight() - 1; return; } sub add_contained_widgets { my $self = $_[0]; $self->add_buttons_widget(); return; } sub add_buttons_widget { my $self = $_[0]; $self->add( 'buttons', 'Buttonbox', '-releasefocus' => 1, '-buttons' => [ { '-label' => __( 'Add' ), '-onpress' => sub { $self->do_routine( 'add-element' ) } }, { '-label' => __( 'Copy' ), '-onpress' => sub { $self->do_routine( 'copy-element' ) } }, { '-label' => __( 'Delete' ), '-onpress' => sub { $self->do_routine( 'del-element' ) } }, { '-label' => __( 'Up' ), '-onpress' => sub { $self->do_routine( 'move-element-up' ) } }, { '-label' => __( 'Down' ), '-onpress' => sub { $self->do_routine( 'move-element-down' ) } }, ] ); return; } sub title { return shift->getobj( 'list' )->title( @_ ); } # Routines sub add_element { my $self = $_[0]; my $instance = $self->new_value(); if ( $instance ) { my $list = $self->getobj( 'list' ); $self->{'value'}->append( $instance, $list->{'-selected'} ); $list->{'-ypos'} = ( defined $list->{'-selected'} ? $list->{'-selected'} + 1 : 0 ); $self->refresh_view(); $self->run_event( 'onvaluechanged' ); } return; } sub copy_element { my $self = $_[0]; my $list = $self->getobj( 'list' ); return unless defined $list->{'-selected'}; my $instance = $self->{'value'}->get( $list->{'-selected'} )->clone(); $self->{'value'}->append( $instance, $list->{'-selected'} ); $list->{'-ypos'} = $list->{'-selected'} + 1; $self->refresh_view(); $self->run_event( 'onvaluechanged' ); return; } sub move_element_up { $_[0]->move_element( -1 ); return; } sub move_element_down { $_[0]->move_element( 1 ); return; } sub move_element { my ( $self, $offset ) = @_; my $list = $self->getobj( 'list' ); return unless defined $list->{'-selected'}; my $new_idx = $list->{'-selected'} + $offset; $new_idx = 0 if $new_idx < 0; $new_idx = $#{$list->{'-values'}} if $new_idx > $#{$list->{'-values'}}; return if $new_idx == $list->{'-selected'}; $self->{'value'}->move( $list->{'-selected'}, $new_idx ); $list->{'-ypos'} = $new_idx; $self->refresh_view(); $self->run_event( 'onvaluechanged' ); return; } sub refresh_view { my $self = $_[0]; my $list_widget = $self->getobj( 'list' ); my $values = [ $self->{'value'}->elements() ]; my %labels = map { $_, $_->as_label() } $self->{'value'}->elements(); if ( scalar @$values == 0 ) { $values = [ __( '-- empty list --' ) ]; $list_widget->focusable( 0 ); $list_widget->{'-selected'} = undef; } else { $list_widget->focusable( 1 ); $list_widget->{'-selected'} = $list_widget->{'-ypos'}; } $list_widget->{'-values'} = $values; $list_widget->{'-labels'} = \%labels; $list_widget->draw(); return; } sub del_element { my $self = $_[0]; my $list_widget = $self->getobj( 'list' ); return unless $list_widget->focusable(); $self->{'value'}->remove( $list_widget->{'-ypos'} ); my $nbr = scalar $self->{'value'}->elements() - 1; if ( $list_widget->{'-selected'} > $nbr ) { $list_widget->{'-ypos'} = $nbr; } $self->refresh_view(); $self->run_event( 'onvaluechanged' ); return; } sub _sel_change_cb { my $list_widget = $_[0]; $list_widget->{'-selected'} = $list_widget->{'-ypos'}; $list_widget->run_event( '-onchange' ); return; } 1;