package RT::Client::Console::Session;
use strict;
use warnings;
use parent qw(RT::Client::Console);
use POE;
use List::Util qw(max first);
use Curses::Widgets::Label;
use Curses::Forms;
use relative -to => "RT::Client::Console::Session",
-aliased => qw(Ticket);
use Params::Validate qw(:all);
# global vars
my %sessions;
my @modal_sessions;
my $modal_index = 0;
sub get_sessions { return %sessions }
sub get_modal_sessions { return @modal_sessions }
# a small wrapper around POE::Session
sub create {
my ($class, $name, %args) = @_;
my $is_modal = delete $args{_is_modal};
$args{inline_states}{_start} = sub {
my ($kernel, $heap) = @_[ KERNEL, HEAP ];
$kernel->alias_set($name);
# $kernel->call($name, 'window_resize', undef, undef);
$kernel->call($name, 'init');
$kernel->call($name, '__window_resize', !$is_modal);
};
$args{inline_states}{_unalias} = sub {
my ($kernel, $heap) = @_[ KERNEL, HEAP ];
my @aliases = $kernel->alias_list();
foreach my $alias (@aliases) {
$kernel->alias_remove($alias);
}
};
# automatic window change handling
$args{inline_states}{__window_resize} = sub {
my ($kernel, $heap, $is_init_mode) = @_[ KERNEL, HEAP, ARG0 ];
my ($old_screen_h, $old_screen_w) = @{$heap}{qw(screen_h screen_w)};
my ($screen_w, $screen_h);
$class->get_curses_handler()->getmaxyx($screen_h, $screen_w);
$heap->{screen_h} = $screen_h;
$heap->{screen_w} = $screen_w;
print STDERR " -- $name -- __window resize " . $heap->{screen_w} . " | " . $heap->{screen_h} . "\n";
$is_init_mode or $kernel->yield('draw');
# give a chance to the session to do something specific
$kernel->call($name, 'window_resize', $old_screen_h, $old_screen_w);
};
if ($is_modal) {
my $keys = delete $args{keys};
$args{inline_states}{key_handler} = sub {
my ( $kernel, $heap, $keystroke ) = @_[ KERNEL, HEAP, ARG0 ];
if (!defined $keys) {
$kernel->yield('private_key_handler', $keystroke);
$kernel->yield('draw');
return;
}
exists $keys->{$keystroke} or return;
if ($keys->{$keystroke}{code}->()) {
$class->remove_modal($name);
} else {
$kernel->yield('draw');
}
};
POE::Session->create(%args);
push @modal_sessions, $name;
} else {
$sessions{$name} = {
poe_object => POE::Session->create(%args),
displayed => 1,
};
}
return $name;
}
# create a modal session
sub create_modal {
my ($class, %args) = @_;
$args{_is_modal} = 1;
my $name = 'modal_' . ++$modal_index;
return create($class, $name, %args);
}
sub create_choice_modal {
my ($class, %args) = @_;
my $title = "[ $args{title} ]";
my $text = $args{text} . "\n";
exists $args{interactive} or $args{interactive} = 1;
if ($args{interactive}) {
$args{keys}{c} ||= { text => "close this dialog",
code => sub { return 1 },
};
while (my ($k, $v) = each %{$args{keys}} ) {
$text .= "$k : " . $v->{text} . "\n";
}
$args{keys}{'<^[>'} ||= { text => "close this dialog",
code => sub { return 1 },
};
}
my $height = scalar( () = $text =~ /(\n)/g) + 1;
my $width = max (map { length } (split(/\n/, $text), $title));
return create_modal(
$class,
keys => $args{keys},
inline_states => {
draw => sub {
my ( $kernel, $heap ) = @_[ KERNEL, HEAP ];
my $curses_handler = $class->get_curses_handler();
my $label = Curses::Widgets::Label->new({
CAPTION => $title,
CAPTIONCOL => 'yellow',
BORDER => 1,
LINES => $height,
COLUMNS => $width,
Y => $heap->{screen_h}/2-($height+2)/2,
X => $heap->{screen_w}/2-($width+2)/2,
VALUE => $text,
FOREGROUND => 'white',
BACKGROUND => 'blue',
BORDERCOL => 'white',
});
$label->draw($curses_handler);
}
},
);
}
sub create_wait_modal {
my ($class, $text) = @_;
return create_choice_modal($class,
title => 'Please wait',
text => $text,
interactive => 0,
);
}
sub execute_wait_modal {
my ($class, $text, $code) = @_;
my $modal_session_name =
create_wait_modal($class, $text);
$poe_kernel->call($modal_session_name, 'draw');
my @ret;
eval { @ret = wantarray ? $code->() : scalar($code->()) };
my $save_error = $@;
remove_modal($class, $modal_session_name);
$save_error and die $save_error;
return @ret;
}
sub remove_modal {
my ($class, $modal_session_name) = @_;
# stop modal mode
pop @modal_sessions;
remove($class, $modal_session_name);
$poe_kernel->post('key_handler', 'compute_keys');
# if no ticket is displayed, we clear the background
Ticket->get_current_ticket() or $class->cls();
return;
}
# start the POE main loop
sub run {
my ($class) = @_;
$poe_kernel->run();
}
# display/hide a session
sub set_display {
my ($class, $session_name, $display_state) = @_;
$sessions{$session_name}{displayed} = $display_state ? 1 : 0;
}
=head2 remove
Removes a session
input : a session name
=cut
sub remove {
my ($class, $session_name) = @_;
delete $sessions{$session_name};
$poe_kernel->call($session_name, '_quit');
$poe_kernel->call($session_name, '_unalias');
}
sub remove_all {
my ($class) = @_;
my %sessions = $class->get_sessions();
my @sessions_names = keys(%sessions);
foreach (@sessions_names) {
$class->remove($_);
}
}
sub execute_textmemo_modal {
my $class = shift;
my %params = validate( @_, { foreground => { type => SCALAR,
default => 'white',
},
background => { type => SCALAR,
default => 'blue',
},
border_color => { type => SCALAR,
default => 'yellow',
},
text => { type => SCALAR, default => '' },
title => { type => SCALAR },
}
);
my $title = "[ $params{title} ]";
my $text = $params{text};
my ($fg, $bg, $cfg) = ('white', 'blue', 'yellow');
my ($form, @lines, $max);
my ($cols, $lines, $bx, $by);
# Build array of buttons to display
my @buttons = qw(OK Cancel);
# Calculate the necessary dimensions of the message box, based
# on both the button(s) and the length of the message.
$max = max(15 * @buttons + 2 * $#buttons + 1, 30);
use Curses;
@lines = textwrap($text, $COLS - 4);
foreach (@lines) { $max = length($_) if length($_) > $max };
# Calculate cols and lines
$cols = $max + 5;
$lines = max(scalar(@lines), 10) + 3 + 5;
# Exit if the geometry exceeds the display
unless ($cols + 2 < $COLS && $lines + 2 < $LINES) {
warn "dialog: Calculated geometry exceeds display geometry!";
return 0;
}
# Calculate upper-left corner of the buttons
$bx = 10 * @buttons + 2 * $#buttons + 1;
$bx = int(($cols - $bx) / 2);
$by = $lines - 3;
local *btnexit = sub {
my $f = shift;
my $key = shift;
return unless $key eq "\n";
$f->setField(EXIT => 1);
};
$form = Curses::Forms->new({
AUTOCENTER => 1,
DERIVED => 0,
COLUMNS => $cols,
LINES => $lines,
CAPTION => $title,
CAPTIONCOL => $cfg,
BORDER => 1,
FOREGROUND => $fg,
BACKGROUND => $bg,
FOCUSED => 'Memo',
TABORDER => ['Memo', 'Buttons'],
WIDGETS => {
Buttons => {
TYPE => 'ButtonSet',
LABELS => [@buttons],
Y => $by,
X => $bx,
FOREGROUND => $fg,
BACKGROUND => $bg,
BORDER => 1,
OnExit => *btnexit,
},
Memo => {
TYPE => 'TextMemo',
Y => 0,
X => 0,
COLUMNS => $max ,
LINES => max(scalar @lines, 10) + 2,
VALUE => $text,
CAPTION => 'Enter new comment',
CAPTIONCOL => $cfg,
FOREGROUND => $fg,
BACKGROUND => $bg,
},
},
});
$form->execute;
return ( $form->getWidget('Buttons')->getField('VALUE'),
$form->getWidget('Memo')->getField('VALUE')
);
}
1;