use strict;
use warnings;
use Irssi;
use Irssi::TextUI; # for sbar_items_redraw
use Data::Dumper;
our $VERSION = "0.1";
our %IRSSI =
(
authors => "shabble",
contact => 'shabble+irssi@metavore.org, shabble@#irssi/Freenode',
name => "prompt_info",
description => "Helper script for dynamically adding text "
. "into the input-bar prompt.",
license => "Public Domain",
changed => "24/7/2010"
);
sub DEBUG () { 1 }
#sub DEBUG () { 0 }
my $prompt_data = undef;
my $prompt_item = undef;
my $vis_enabled = 0;
my ($term_w, $term_h) = (0, 0);
my ($region_start, $region_end) = (0, 0);
# overlay := { $num1 => line1, $num2 => line2 }
# line := [ region, region, region ]
# region := { start => x, end => y, ...? }
my $overlay;
my $prompt_format;
my $prompt_format_str = '';
sub _add_overlay_region {
my ($line, $start, $end, $text, $len) = @_;
my $region = { start => $start,
end => $end,
text => $text,
len => $len };
my $o_line = $overlay->{$line};
unless (defined $o_line) {
$o_line = [];
$overlay->{$line} = $o_line;
}
foreach my $cur_region (@$o_line) {
if (_region_overlaps($cur_region, $region)) {
# do something.
print "Region overlaps";
last;
}
}
push @$o_line, $region;
}
sub _remove_overlay_region {
my ($line, $start, $end) = @_;
my $o_line = $overlay->{$line};
return unless $o_line;
my $i = 0;
foreach my $region (@$o_line) {
if ($region->{start} == $start && $region->{end} == $end) {
last;
}
$i++;
}
splice @$o_line, $i, 1, (); # remove it.
}
sub _redraw_overlay {
foreach my $line_num (sort keys %$overlay) {
my $line = $overlay->{$line_num};
foreach my $region (@$line) {
Irssi::gui_printtext($region->{start}, $line_num,
$region->{text});
}
}
}
init();
sub update_terminal_size {
my @stty_lines = qx/stty -a/;
my $line = $stty_lines[0];
@stty_lines = (); # don't need the rest.
if ($line =~ m/\s*(\d+)\s*rows\s*;\s*(\d+)\s*columns\s*;/) {
$term_h = $1;
$term_w = $2;
} else {
print "Failed to detect terminal size";
}
}
sub subcmd_handler {
my ($data, $server, $item) = @_;
$data =~ s/\s+$//g; # strip trailing whitespace.
Irssi::command_runsub('prompt', $data, $server, $item);
}
sub init {
Irssi::statusbar_item_register ('uberprompt', 0, 'uberprompt_draw');
Irssi::settings_add_str('uberprompt', 'uberprompt_format', '[$*] ');
Irssi::command_bind("prompt", \&subcmd_handler);
Irssi::command_bind('prompt on', \&replace_prompt_items);
Irssi::command_bind('prompt off', \&restore_prompt_items);
Irssi::command_bind('prompt set',
sub { $prompt_data = shift; uberprompt_refresh(); });
Irssi::command_bind('prompt clear',
sub { undef $prompt_data; uberprompt_refresh(); });
# misc faff
Irssi::command_bind('visual', \&cmd_toggle_visual);
Irssi::command("^BIND meta-l /visual");
Irssi::command_bind('menu', \&draw_menu);
# redraw interception
Irssi::signal_add_last('command redraw', \&augment_redraw);
Irssi::signal_add_first('gui key pressed', \&ctrl_l_intercept);
# for updating the overlay.
Irssi::signal_add_last ('gui key pressed', \&key_pressed);
# things to refresh the overlay for.
Irssi::signal_add('window changed', \&uberprompt_refresh);
Irssi::signal_add('window name changed', \&uberprompt_refresh);
Irssi::signal_add('window changed automatic', \&uberprompt_refresh);
Irssi::signal_add('window item changed', \&uberprompt_refresh);
Irssi::signal_add('terminal resized', \&update_terminal_size);
Irssi::signal_add('setup changed', \&reload_settings);
# so we know where the bottom line is
update_terminal_size();
# intialise the prompt format.
reload_settings();
# install our statusbars.
replace_prompt_items();
}
sub UNLOAD {
# remove uberprompt and return the original ones.
restore_prompt_items();
}
sub reload_settings {
my $new = Irssi::settings_get_str('uberprompt_format');
if ($prompt_format_str ne $new) {
print "Updated prompt format";
$prompt_format_str = $new;
Irssi::abstracts_register(['uberprompt', $prompt_format_str]);
}
}
sub uberprompt_draw {
my ($sb_item, $get_size_only) = @_;
my $default_prompt = '';
my $window = Irssi::active_win;
# hack to produce the same defaults as prompt/prompt_empty sbars.
if (scalar( () = $window->items )) {
$default_prompt = '{uberprompt $[.15]itemname}';
} else {
$default_prompt = '{uberprompt $winname}';
}
my $p_copy = $prompt_data;
if (defined $prompt_data) {
# replace the special marker '$p' with the original prompt.
$p_copy =~ s/\$p/$default_prompt/;
} else {
$p_copy = $default_prompt;
}
print "Redrawing with: $p_copy, size-only: $get_size_only";
$prompt_item = $sb_item;
$sb_item->default_handler($get_size_only, $p_copy, '', 0);
}
sub augment_redraw {
print "Redraw called";
uberprompt_refresh();
Irssi::timeout_add_once(10, \&refresh_inputline, 0);
}
sub uberprompt_refresh {
Irssi::statusbar_items_redraw('uberprompt');
}
sub cmd_toggle_visual {
$vis_enabled = not $vis_enabled;
if ($vis_enabled) {
$region_start = _pos();
$region_end = 0; # reset end marker.
print "visual mode started at $region_start";
} else {
$region_end = _pos();
print "Visual mode ended at $region_end";
if ($region_end > $region_start) {
my $input = Irssi::parse_special('$L', 0, 0);
my $str = substr($input, $region_start, $region_end - $region_start);
print "Region selected: $str";
} else {
print "Invalid region selection: [ $region_start - $region_end ]";
$region_start = $region_end = 0;
}
}
}
sub ctrl_l_intercept {
my $key = shift;
if ($key == 12) {
print "C-l pressed";
Irssi::command("redraw");
Irssi::signal_stop();
}
if ($key == 10) {
$region_end = $region_start = 0;
}
}
sub key_pressed {
my $key = shift;
return unless $vis_enabled;
refresh_inputline();
}
sub refresh_inputline {
my $end_pos = $region_end;
$end_pos ||= _pos(); # if not set, take current position as end.
my $len = $end_pos - $region_start;
return unless $len; # no point drawing an empty overlay
my $input = Irssi::parse_special('$L');
my $offset = $prompt_item->{size} + $region_start;
my $text = substr($input, $region_start, $len);
print "printing '$text' at $offset [$region_start, $end_pos] ($len)";
$text = '%k%2' . $text . '%n';
_draw_overlay($offset, $text, $len);
}
sub _draw_overlay {
my ($offset, $text, $len) = @_;
Irssi::gui_printtext($offset, $term_h, $text);
}
sub _clear_overlay {
Irssi::active_win->view->redraw();
}
sub _draw_overlay_menu {
my $w = 10;
my @lines = (
'%7+' . ('-' x $w) . '+%n',
sprintf('%%7|%%n%*s%%7|%%n', $w, 'bacon'),
sprintf('|%*s|', $w, 'bacon'),
sprintf('|%*s|', $w, 'bacon'),
sprintf('|%*s|', $w, 'bacon'),
sprintf('|%*s|', $w, 'bacon'),
sprintf('|%*s|', $w, 'bacon'),
'%7+' . ('-' x $w) . '+%n',
);
my $i = 10; # start vert offset.
for my $line (@lines) {
Irssi::gui_printtext(int ($term_w / 2), $i++, $line);
}
}
sub replace_prompt_items {
# remove existing ones.
print "Removing original prompt" if DEBUG;
_sbar_command('prompt', 'remove', 'prompt');
_sbar_command('prompt', 'remove', 'prompt_empty');
# add the new one.
_sbar_command('prompt', 'add', 'uberprompt',
qw/-alignment left -before input -priority 0/);
}
sub restore_prompt_items {
_sbar_command('prompt', 'remove', 'uberprompt');
print "Restoring original prompt" if DEBUG;
_sbar_command('prompt', 'add', 'prompt',
qw/-alignment left -before input -priority 0/);
_sbar_command('prompt', 'add', 'prompt_empty',
qw/-alignment left -after prompt -priority 0/);
}
sub _sbar_command {
my ($bar, $cmd, $item, @args) = @_;
my $args_str = join ' ', @args;
$args_str .= ' ' if length $args_str;
my $command = sprintf 'STATUSBAR %s %s %s%s',
$bar, $cmd, $args_str, defined($item)?$item:'';
print "Running command: $command" if DEBUG;
Irssi::command($command);
}
sub _pos {
return Irssi::gui_input_get_pos();
}