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(); }