package Lire::UI::StoreWindow;
use strict;
use base qw/ Curses::UI::Window /;
use Carp;
use Curses::UI::Common;
use Locale::TextDomain 'lire';
use Time::Local;
use Lire::Utils qw/ check_param check_object_param max text_for_width /;
use Lire::UI::Utils qw/ button_box_width /;
use POSIX qw/strftime/;
use vars qw/ @CARP_NOT /;
@CARP_NOT = qw/ Curses::UI::Container /;
my %routines = ( 'configure-stream' => \&configure_stream,
'clean-stream' => \&clean_stream,
);
sub new {
my $class = shift;
my %userargs = @_;
keys_to_lowercase( \%userargs );
check_object_param( $userargs{'store'}, 'store',
'Lire::DlfStore' );
my $self = $class->SUPER::new( %userargs,
'-titlereverse' => 0,
'-border' => 1,
'-routines' => \%routines,
'-ipad' => 1,
'-ipadbottom' => 0,
);
$self->add( 'import_jobs', 'Lire::UI::Widget',
'value' => $self->{'store'}->config()->get( 'import_jobs' ),
'-title' => __( 'Import Jobs' ),
'-titlereverse' => 0 );
$self->add( 'report_jobs', 'Lire::UI::Widget',
'value' => $self->{'store'}->config()->get( 'report_jobs' ),
'-title' => __( 'Report Jobs' ),
'-titlereverse' => 0 );
$self->getobj( 'import_jobs' )->onValueChanged( sub { $self->_update_streams(); $self->getobj( 'streams' )->intellidraw() } );
$self->add( 'reports', 'Lire::UI::ReportConfigListWidget',
'value' => $self->{'store'}->config()->get( 'reports' ),
'-title' => __( 'Report Configurations' ),
'-titlereverse' => 0 );
$self->_add_stream_widgets();
$self->layout();
return $self;
}
sub layout_contained_objects {
my $self = $_[0];
# Contained widgets weren't added yet.
return $self unless $self->getobj( 'import_jobs' );
# Window contains 4 quadrants
my $win_w = $self->canvaswidth();
my $w = int( ( $win_w - 1) / 2 );
my $h = int( ($self->canvasheight() - 1) / 2 );
my $store_label = __( 'Store: ' );
$self->title( $store_label
. text_for_width( $self->{'store'}->path(),
$win_w - ( 4 + length( $store_label ) ) ));
my $import_jobs = $self->getobj( 'import_jobs' );
$import_jobs->{'-width'} = $w;
$import_jobs->{'-height'} = $h;
my $report_jobs = $self->getobj( 'report_jobs' );
$report_jobs->{'-x'} = $w + 1;
$report_jobs->{'-width'} = $w;
$report_jobs->{'-height'} = $h;
my $reports = $self->getobj( 'reports' );
$reports->{'-y'} = $h + 1;
$reports->{'-width'} = $w;
$reports->{'-height'} = $h;
$self->_set_stream_widgets_coor( $h, $w );
return $self->SUPER::layout_contained_objects();
}
sub _set_stream_widgets_coor {
my ($self, $h, $w ) = @_;
my $streams = $self->getobj( 'streams' );
$streams->{'-width'} = $w - 1; # To align with ListWidget
$streams->{'-height'} = $h - 4;
$streams->{'-y'} = $h + 1;
$streams->{'-x'} = $w + 1;
my $y = $h + ( $h - 4 ) + 1;
my $buttons = $self->getobj( 'streams_buttons' );
$buttons->{'-x'} = $w + 2;
$buttons->{'-y'} = $y++;
$y++;
my $max_label_w = max( $self->getobj( 'stream_start_label' )->width(),
$self->getobj( 'stream_end_label' )->width(),
$self->getobj( 'nrecords_label' )->width() );
foreach my $name ( qw/stream_start stream_end nrecords/ )
{
my $label = $self->getobj( "${name}_label" );
my $value = $self->getobj( "${name}_value" );
$label->{'-x'} = $w + 2;
$value->{'-x'} = $w + $max_label_w + 2;
$label->{'-y'} = $value->{'-y'} = $y++;
}
return;
}
sub _add_stream_widgets {
my $self = $_[0];
my $streams = $self->add( 'streams', 'Listbox',
'-onchange' => \&_change_cb,
'-onselchange' => \&_sel_change_cb,
'-values' => [],
'-title' => __( 'DLF Streams' ),
'-titlereverse' => 0,
'-selected' => undef,
'-ypos' => undef,
'-vscrollbar' => 1,
'-focusable' => 0,
'-border' => 1,
);
$streams->set_binding( sub { $self->do_routine( 'configure-stream' ) },
Curses::KEY_ENTER );
$streams->set_binding( sub { $self->do_routine( 'clean-stream' ) },
Curses::KEY_DC, Curses::KEY_BACKSPACE );
$self->add( 'stream_start_label', 'Label',
'-text' => __( 'Starts:' ) );
$self->add( 'stream_end_label', 'Label',
'-text' => __( 'Ends:' ) );
$self->add( 'nrecords_label', 'Label',
'-text' => __( 'Records:' ) );
$self->add( 'stream_start_value', 'Label' );
$self->add( 'stream_end_value', 'Label' );
$self->add( 'nrecords_value', 'Label' );
$self->add( 'streams_buttons', 'Buttonbox',
'-buttons' => [ { '-label' => __( '[Configure]' ),
'-onpress' => sub { $self->do_routine( 'configure-stream' ) } },
{ '-label' => __( '[Clean]' ),
'-onpress' => sub { $self->do_routine( 'clean-stream' ) } } ],
);
$self->_update_streams();
return;
}
sub _clean_ok_cb_helper {
my ( $self, $time_field ) = @_;
my $dialog = $self->getobj( 'stream_clean' );
my $streams = $self->getobj( 'streams' );
my $stream_name = $streams->get();
my $stream = $self->{'store'}->open_dlf_stream( $stream_name, 'w' );
my $time = _date_to_epoch( $time_field->text() );
$stream->clean( $time );
$stream->close();
$self->_update_labels();
return;
}
sub clean_stream {
my $self = $_[0];
my $streams = $self->getobj( 'streams' );
return unless $streams->{'-focusable'};
my $title = __x( "Cleaning stream '{stream}'",
'stream' => $streams->get() );
my $dialog = $self->add( 'clean_stream_win', 'Window',
'-title' => $title, '-ipad' => 1,
'-x' => 7, '-y' => 8,
'-height' => 9, '-width' => 60,
'-border' => 1 );
$dialog->set_binding( sub { $dialog->loose_focus() }, CUI_ESCAPE );
$dialog->add( 'question', 'Label',
'-text' => __( 'Enter the cleaning date in the form "YYYY-MM-DD":' ),
'-x' => 0, '-y' => 0 );
my $time_field = $dialog->add( 'time_field', 'TextEntry',
'-regexp' => '/^[0-9-]*$/',
'-sbborder' => 1, '-x' => 20,
'-y' => 2, '-width' => 14 );
my $buttons = [ { '-label' => __( '< Cancel >' ),
'-onpress' => sub {
$dialog->loose_focus()
} },
{ '-label' => __( '< OK >' ),
'-onpress' => sub {
if ( _test_date ( $time_field->text() ) ) {
$self->_clean_ok_cb_helper( $time_field );
$dialog->loose_focus();
}
} } ];
my $bb_width = button_box_width( $buttons );
$dialog->add( 'buttons', 'Buttonbox',
'-buttons' => $buttons,
'-x' => -20, '-y' => 4,
'-height' => 1, '-width' => $bb_width );
$dialog->modalfocus();
$self->delete( 'clean_stream_win' );
$self->draw( 1 );
return;
}
sub configure_stream {
my $self = $_[0];
my $streams = $self->getobj( 'streams' );
return unless $streams->{'-focusable'};
my $dialog = $self->root->add( 'configure_stream_win', 'Window',
'-title' => __x( "Configuring stream '{stream}'",
'stream' => $streams->get() ),
'-ipadleft' => 1, '-ipadright' => 1,
'-ipadtop' => 1, '-pad' => 2,
'-border' => 1 );
$dialog->set_binding( sub { $dialog->loose_focus() }, CUI_ESCAPE );
my $config_value = $self->{'store'}->get_stream_config( $streams->get() )->clone();
$dialog->add( 'stream_config', 'Lire::UI::Widget',
'value' => $config_value,
'-releasefocus' => 1,
'-ipadbottom' => 2 );
my $buttons = [ { '-label' => __( '< Cancel >' ),
'-onpress' => sub { $dialog->loose_focus() } },
{ '-label' => __( '< OK >' ),
'-onpress' => sub {
$self->_configure_ok_cb_helper( $config_value );
$dialog->loose_focus();
} } ];
my $bb_width = button_box_width( $buttons );
$dialog->add( 'buttons', 'Buttonbox',
'-buttons' => $buttons,
'-x' => -$bb_width, '-y' => -1,
'-width' => $bb_width );
$dialog->modalfocus();
$self->root()->delete( 'configure_stream_win' );
$self->draw( 1 );
return;
}
sub _configure_ok_cb_helper {
my ( $self, $new_config_value ) = @_;
my $old = $self->{'store'}->get_stream_config( $self->getobj( 'streams' )->get() );
# Since we do not have access to the stream config list container, we
# copy the content.
foreach my $name ( $old->spec()->component_names() ) {
$old->set( $new_config_value->get( $name ) );
}
$self->_update_streams();
return;
}
sub _update_streams {
my $self = $_[0];
my @streams = $self->{'store'}->configured_dlf_streams();
my $values;
my $selected;
my $focusable;
if ( @streams ) {
$values = \@streams;
$selected = 0;
$focusable = 1;
} else {
$values = [ __( '-- no stream --' ) ];
$selected = undef;
$focusable = 0;
}
my $stream_widget = $self->getobj( 'streams' );
$stream_widget->{'-values'}= $values;
$stream_widget->{'-selected'} = $selected;
$stream_widget->{'-ypos'} = 0;
$stream_widget->focusable( $focusable );
$self->_update_labels();
return;
}
sub _update_labels {
my $self = $_[0];
my $stream_name = $self->getobj( 'streams' )->get();
my $start_value;
my $end_value;
my $nrecords;
if ( defined $stream_name
&& $self->{'store'}->has_dlf_stream( $stream_name ) )
{
my $stream = $self->{'store'}->open_dlf_stream( $stream_name );
$start_value = _epoch_to_date( $stream->start_time() );
$end_value = _epoch_to_date( $stream->end_time() );
$nrecords = $stream->nrecords();
$stream->close();
} else {
$start_value = '';
$end_value = '';
$nrecords = defined $stream_name ? 0 : '';
}
$self->getobj( 'stream_start_value' )->text( $start_value );
$self->getobj( 'stream_end_value' )->text( $end_value );
$self->getobj( 'nrecords_value' )->text( $nrecords );
return;
}
sub store {
return $_[0]->{'store'};
}
# callbacks
sub _sel_change_cb {
my $list_widget = $_[0];
$list_widget->{'-selected'} = $list_widget->{'-ypos'};
$list_widget->run_event( '-onchange' );
return;
}
sub _change_cb {
my $list_widget = $_[0];
croak "'-selected' can never be undefined"
unless defined $list_widget->{'-selected'};
my $self = $list_widget->parent();
$self->_update_labels();
return;
}
# helper functions
sub _test_date {
my $text = $_[0];
return 0
unless ( $text =~ m/^(\d{2}|\d{4})-(\d{2})-(\d{2})$/ );
my $month = $2;
my $day = $3;
return 0
if ( $month < 1 || $month > 12 );
return 0
if ( $day < 1 || $day > 31 );
return 1;
}
sub _date_to_epoch {
my $date = $_[0];
check_param( $date, 'date', \&_test_date,
"wrong format for 'date' (yyyy-mm-dd)" );
$date =~ m/^(\d{2}|\d{4})-(\d{2})-(\d{2})$/;
my $year = $1;
my $month = $2 - 1;
my $day = $3;
return timelocal( 0, 0, 0, $day, $month, $year );
}
sub _epoch_to_date {
my $epoch = $_[0];
return __( '-- no date --' )
unless $epoch;
check_param( $epoch, 'epoch', qr/^\d+$/,
"'epoch' should be a positive integer" );
return strftime( '%Y-%m-%d %H:%S', localtime $epoch );
}
1;
syntax highlighted by Code2HTML, v. 0.9.1