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;