aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--auto-server/auto_server.pl208
-rw-r--r--feature-tests/easy_exec.pl35
-rw-r--r--feature-tests/template.pl8
-rw-r--r--history-search/rl_history_search.pl13
-rw-r--r--ido-mode/ido_switcher.pl838
-rw-r--r--prompt_info/uberprompt.pl13
-rw-r--r--sb-position/sb_position.pl112
7 files changed, 1218 insertions, 9 deletions
diff --git a/auto-server/auto_server.pl b/auto-server/auto_server.pl
new file mode 100644
index 0000000..7054664
--- /dev/null
+++ b/auto-server/auto_server.pl
@@ -0,0 +1,208 @@
+# This script was initially written by shabble, this fork (and molestation) is
+# built upon his original work.
+#
+# USAGE:
+#
+# the primary command used is /join+ #channelname
+#
+# Mappings for channels to servers is accomplished with the
+# joinplus_server_maps setting.
+#
+# Within this setting, space separated pairs denote channel, server pairs.
+# Spaces also separate individual pairs, for example:
+#
+# /set joinplus_server_maps #foo Freenode #bar irc.somewhere.tld #meep DALNet
+#
+# Then use /join+ #foo, and if you are not already connected to freenode, it
+# will connect you, and then join that channel.
+
+# TODO:
+# Autocompletion for channel names
+# address conflict resolution
+# fix that disgusting race condition
+
+use strict;
+use warnings;
+
+
+use Irssi;
+use Irssi::Irc;
+use Irssi::TextUI;
+
+use Data::Dumper;
+
+
+my $DEBUG_ENABLED = 0;
+sub DEBUG () { $DEBUG_ENABLED }
+
+sub _debug_print {
+ return unless DEBUG;
+ return unless scalar (grep { defined && length } @_) == @_;
+ my $win = Irssi::active_win;
+ my $str = join('', @_);
+ $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP);
+}
+
+our $VERSION = '0.1';
+
+our %IRSSI = (
+ authors => 'shabble, richo',
+ contact => 'richo@psych0tik.net',
+ name => 'auto-join-ng',
+ description => 'connects to a specified server in order to connect' .
+ ' to a channel there, without having first to' .
+ ' connect to the server',
+ license => 'Public Domain',
+ );
+
+my $channel_map;
+my @hack_channels;
+my $pending_joins;
+
+sub auto_server_init {
+ Irssi::command_bind('join+', \&join_plus);
+ Irssi::settings_add_str('join_plus', 'joinplus_server_maps', '');
+ Irssi::signal_add_last('setup changed', \&setup_changed);
+ Irssi::settings_add_bool('join_plus', 'join_plus_debug', 0);
+
+ setup_changed();
+ $pending_joins = {};
+
+}
+
+sub setup_changed {
+ $DEBUG_ENABLED = Irssi::settings_get_bool('join_plus_debug');
+ parse_channel_map();
+}
+
+# This is a tremendous kludge.
+# If anyone knows a better way to get this listing, I'd like to hear it.
+# This has so many race condition bugs I just don't even know where to start.
+sub retrieve_channels {
+ @hack_channels = ();
+ Irssi::signal_add_first('print text', 'haxy_print_hook');
+ Irssi::command("CHANNEL LIST");
+ Irssi::signal_remove('print text', 'haxy_print_hook');
+ return join(" ", @hack_channels);
+}
+
+
+# The idea for how to do this courtesy of http://wouter.coekaerts.be/site/irssi/aliases
+sub haxy_print_hook {
+ Irssi::signal_remove('print text', 'haxy_print_hook');
+ Irssi::signal_stop();
+ my $data = $_[1];
+ # Hose control characters
+ $data =~ s/\x04.//g;
+ if ($data =~ m/^#/) {
+ my @items = split /\s+/, $data;
+ push(@hack_channels, $items[0]);
+ push(@hack_channels, $items[1]);
+ }
+ Irssi::signal_add_first('print text', 'haxy_print_hook');
+}
+
+sub parse_channel_map {
+ #my $data = Irssi::settings_get_str('joinplus_server_maps');
+ my $data = retrieve_channels();
+ my @items = split /\s+/, $data;
+ if (@items % 2 == 0) {
+ $channel_map = { @items }; # risky?
+ } else {
+ Irssi::active_win->print("Could not process channel => server mappings");
+ $channel_map = {};
+ }
+ _debug_print Dumper($channel_map);
+}
+
+sub join_plus {
+ my ($args, $cmd_server, $witem) = @_;
+ #print Dumper($cmd, "moo", $win);
+
+ # parse out channel name from args:
+ my $channel;
+ if ($args =~ m/^(#?[#a-zA-Z0-9]+)/) {
+ $channel = $1;
+ _debug_print ("Channel is: $channel");
+ }
+
+ unless ($channel) {
+ Irssi::active_win()->print("Channel $args not recognised");
+ return;
+ }
+
+ # lookup server
+ my $server_id = $channel_map->{$channel};
+ _debug_print($server_id);
+
+ unless ($server_id) {
+ Irssi::active_win()->print("Channel $channel does not have an"
+ . " appropriate server mapping");
+ return;
+ }
+ # TODO: search values() and give a 'did you mean' for closest channel
+
+ # check if we're connected to that server
+ my $server = Irssi::server_find_tag($server_id);
+
+ if (not defined $server) {
+ $server = Irssi::server_find_chatnet($server_id);
+ }
+
+ if (not defined $server) {
+ # still no server, walk the server list looking for address matches.
+ my @servers = Irssi::servers();
+ foreach my $srv (@servers) {
+ if (($srv->{address} eq $server_id) or
+ ($srv->{real_address} eq $server_id)) {
+ $server = $srv;
+ last;
+ }
+ }
+ }
+
+ if (defined $server) {
+
+ _debug_print ("Already connected to server: " . $server->{tag} );
+
+ # check if we're already on the required channel
+ my $on_channel = $server->channel_find($channel);
+
+ if (defined $channel && ref($channel) eq 'Irssi::Irc::Channel') {
+ Irssi::active_win()->print("You are already connected to "
+ . " $channel on " . $server->{tag});
+ return;
+ } else {
+ _debug_print ("joining channel: $channel");
+ $server->command("JOIN $channel");
+ }
+ } else {
+ # not connected to server.
+ _debug_print ("connecting to server: $server_id");
+
+ Irssi::command("CONNECT $server_id");
+ _debug_print ("awaiting connection for join");
+
+ $pending_joins->{$server_id} = $channel;
+ Irssi::signal_add_last("event 376", 'do_channel_join');
+ }
+}
+
+sub do_channel_join {
+ my ($serv) = @_;
+ #_debug_print("server is " . Dumper($serv));
+ _debug_print(sprintf("server is %s (%s)", $serv->{address}, $serv->{tag}));
+
+ my $channel = $pending_joins->{$serv->{address}};
+ $channel = $pending_joins->{$serv->{tag}} unless $channel;
+
+ _debug_print ("attempting to join $channel");
+
+ Irssi::server_find_tag($serv->{tag})->command("JOIN $channel");
+
+ delete $pending_joins->{$serv->{address}};
+ delete $pending_joins->{$serv->{tag}};
+
+}
+
+auto_server_init();
diff --git a/feature-tests/easy_exec.pl b/feature-tests/easy_exec.pl
new file mode 100644
index 0000000..dbcd101
--- /dev/null
+++ b/feature-tests/easy_exec.pl
@@ -0,0 +1,35 @@
+use strict;
+use warnings;
+
+# export everything.
+use Irssi (@Irssi::EXPORT_OK);
+use Irssi::Irc;
+use Irssi::TextUI;
+
+use Data::Dumper;
+
+our $VERSION = '0.1';
+our %IRSSI = (
+ authors => 'shabble',
+ contact => 'shabble+irssi@metavore.org',
+ name => 'easy_exec',
+ description => 'drop-in replacement for /script exec which imports'
+ . ' all of the Irssi:: namespace for easier testing',
+ license => 'Public Domain',
+ );
+
+Irssi::signal_add_first 'command script exec', \&better_exec;
+
+sub better_exec {
+ my ($args, $serv, $witem) = @_;
+ eval $args;
+ Irssi::signal_stop();
+}
+
+sub Dump {
+ print Dumper(\@_);
+}
+
+sub test() {
+ print "This is a test";
+}
diff --git a/feature-tests/template.pl b/feature-tests/template.pl
index 2c75044..f3ae68a 100644
--- a/feature-tests/template.pl
+++ b/feature-tests/template.pl
@@ -3,12 +3,16 @@ use warnings;
use Irssi;
+use Irssi::Irc;
+use Irssi::TextUI;
+
+use Data::Dumper;
our $VERSION = '0.1';
our %IRSSI = (
- authors => '',
- contact => '',
+ authors => 'shabble',
+ contact => 'shabble+irssi@metavore.org',
name => '',
description => '',
license => 'Public Domain',
diff --git a/history-search/rl_history_search.pl b/history-search/rl_history_search.pl
index fcdde8b..ca45fe5 100644
--- a/history-search/rl_history_search.pl
+++ b/history-search/rl_history_search.pl
@@ -90,22 +90,29 @@ sub script_is_loaded {
return $retval;
}
-unless (script_is_loaded('uberprompt')) {
+if (not script_is_loaded('uberprompt')) {
+
print "This script requires 'uberprompt.pl' in order to work. "
. "Attempting to load it now...";
- Irssi::signal_add('script error', \&load_uberprompt_failed);
+
+ Irssi::signal_add('script error', 'load_uberprompt_failed');
Irssi::command("script load uberprompt.pl");
+
unless(script_is_loaded('uberprompt')) {
load_uberprompt_failed("File does not exist");
}
history_init();
+} else {
+ history_init();
}
sub load_uberprompt_failed {
- Irssi::signal_remove('script error', \&load_prompt_failed);
+ Irssi::signal_remove('script error', 'load_prompt_failed');
+
print "Script could not be loaded. Script cannot continue. "
. "Check you have uberprompt.pl installed in your path and "
. "try again.";
+
die "Script Load Failed: " . join(" ", @_);
}
diff --git a/ido-mode/ido_switcher.pl b/ido-mode/ido_switcher.pl
new file mode 100644
index 0000000..5d6be5a
--- /dev/null
+++ b/ido-mode/ido_switcher.pl
@@ -0,0 +1,838 @@
+# Search and select windows similar to ido-mode for emacs
+#
+# INSTALL:
+#
+# This script requires that you have first installed and loaded 'uberprompt.pl'
+# Uberprompt can be downloaded from:
+#
+# http://github.com/shabble/irssi-scripts/raw/master/prompt_info/uberprompt.pl
+#
+# and follow the instructions at the top of that file for installation.
+#
+# SETUP:
+#
+# * Setup: /bind ^G /ido_switch_start
+#
+# * Then type ctrl-G and type what you're searching for
+#
+# USAGE:
+#
+# C-g (or whatever you've set the above bind to), enters window switching mode.
+#
+# NB: When entering window switching mode, the contents of your input line will
+# be saved and cleared, to avoid visual clutter whilst using the switching
+# interface. It will be restored once you exit the mode using either C-g, Esc,
+# or RET.
+
+# The following key-bindings are available only once the mode has been
+# activated:
+#
+# * C-g - cancel out of the mode without changing windows.
+# * Esc - cancel out, as above.
+# * C-s - rotate the list of window candidates forward by 1
+# * C-r - rotate the list of window candidates backward by 1
+# * C-e - Toggle 'Active windows only' filter
+# * C-f - Switch between 'Flex' and 'Exact' matching.
+# * C-d - Select a network or server to filter candidates by
+# * C-u - Clear the current search string
+# * C-q - Cycle between showing only queries, channels, or all.
+# * C-SPC - Filter candidates by current search string, and then reset
+# the search string
+# * RET - Select the current head of the candidate list (the green one)
+# * SPC - Select the current head of the list, without exiting the
+# switching mode. The head is then moved one place to the right,
+# allowing one to cycle through channels by repeatedly pressing space.
+# * TAB - [currently in development] displays all possible completions
+# at the bottom of the current window.
+# * All other keys (a-z, A-Z, etc) - Add that character to the current search
+# string.
+#
+# USAGE NOTES:
+#
+# * Using C-e (show actives), followed by repeatedly pressing space will cycle
+# through all your currently active windows.
+#
+# * If you enter a search string fragment, and realise that more than one candidate
+# is still presented, rather than delete the whole string and modify it, you can
+# use C-SPC to 'lock' the current matching candidates, but allow you to search
+# through those matches alone.
+#
+# Based in part on window_switcher.pl script Copyright 2007 Wouter Coekaerts
+# <coekie@irssi.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+use strict;
+use Irssi;
+use Irssi::TextUI;
+use Data::Dumper;
+
+use vars qw($VERSION %IRSSI);
+$VERSION = '2.0';
+%IRSSI =
+ (
+ authors => 'Tom Feist, Wouter Coekaerts',
+ contact => 'shabble+irssi@metavore.org, shabble@#irssi/freenode',
+ name => 'ido_switcher',
+ description => 'Select window[-items] using an ido-mode like search interface',
+ license => 'GPLv2 or later',
+ url => 'http://github.com/shabble/irssi-scripts/tree/master/ido-mode/',
+ changed => '24/7/2010'
+ );
+
+
+# TODO:
+# DONE C-g - cancel
+# DONE C-spc - narrow
+# DONE flex matching (on by default, but optional)
+# TODO server/network narrowing
+# DONE colourised output (via uberprompt)
+# DONE C-r / C-s rotate matches
+# DONE toggle queries/channels
+# DONE remove inputline content, restore it afterwards.
+# TODO tab - display all possibilities in window (clean up afterwards)
+# how exactly will this work?
+# DONE sort by recent activity/recently used windows (separate commands?)
+# TODO need to be able to switch ordering of active ones (numerical, or most recently
+# active, priority to PMs/hilights, etc?)
+# DONE should space auto-move forward to next window for easy stepping through
+# sequential/active windows?
+
+my $input_copy = '';
+my $input_pos_copy = 0;
+
+my $ido_switch_active = 0; # for intercepting keystrokes
+
+my @window_cache = ();
+my @search_matches = ();
+
+my $match_index = 0;
+my $search_str = '';
+my $active_only = 0;
+
+my $mode_type = 'ALL';
+my @mode_cache;
+
+my $need_clear = 0;
+
+my $sort_ordering = "start-asc";
+
+# /set configurable settings
+my $ido_show_count;
+my $ido_use_flex;
+
+my $DEBUG_ENABLED = 0;
+sub DEBUG () { $DEBUG_ENABLED }
+
+
+sub MODE_WIN () { 0 } # windows
+sub MODE_NET () { 1 } # chatnets
+#sub MODE_C () { 2 } # channels
+#sub MODE_S () { 3 } # select server
+#sub MODE_W () { 4 } # select window
+
+my $MODE = MODE_WIN;
+
+# check we have uberprompt loaded.
+
+sub _print {
+ my $win = Irssi::active_win;
+ my $str = join('', @_);
+ $need_clear = 1;
+ $win->print($str, Irssi::MSGLEVEL_NEVER);
+}
+
+sub _debug_print {
+ return unless DEBUG;
+ my $win = Irssi::active_win;
+ my $str = join('', @_);
+ $win->print($str, Irssi::MSGLEVEL_CLIENTCRAP);
+}
+
+sub _print_clear {
+ return unless $need_clear;
+ my $win = Irssi::active_win();
+ $win->command('/scrollback levelclear -level NEVER');
+}
+
+sub print_all_matches {
+ my $msg = join(", ", map { $_->{name} } @search_matches);
+ my $message_header = "Windows:";
+ my $win = Irssi::active_win();
+ my $win_width = $win->{width} || 80;
+
+ # TODO: needs to prefix ambig things with chatnet, or maybe order in groups
+ # by chatnet with newlines.
+
+ # Also, colourise the channel list.
+
+ my $col_width;
+
+ for (@search_matches) {
+ my $len = length($_->{name});
+ $col_width = $len if $len > $col_width;
+ }
+
+ my $cols = int($win_width / $col_width);
+
+ my @lines;
+ my $i = 0;
+ my @line;
+
+ for my $item (@search_matches) {
+ my $name = $item->{name};
+ push @line, sprintf('%.*s', $col_width, $name);
+ if ($i == $cols) {
+ push @lines, join ' ', @line;
+ @line = ();
+ $i = 0;
+ }
+ }
+ # flush rest out.
+ push @lines, join ' ', @line;
+
+ _print($message_header);
+ _print($_) for (@lines);
+ #_print("Longtest name: $longest_name");
+}
+
+ sub script_is_loaded {
+ my $name = shift;
+ _debug_print "Checking if $name is loaded";
+ no strict 'refs';
+ my $retval = defined %{ "Irssi::Script::${name}::" };
+ use strict 'refs';
+
+ return $retval;
+ }
+
+unless (script_is_loaded('uberprompt')) {
+
+ _print "This script requires '\%_uberprompt.pl\%_' in order to work. "
+ . "Attempting to load it now...";
+
+ Irssi::signal_add('script error', 'load_uberprompt_failed');
+ Irssi::command("script load uberprompt.pl");
+
+ unless(script_is_loaded('uberprompt')) {
+ load_uberprompt_failed("File does not exist");
+ }
+ ido_switch_init();
+}
+
+sub load_uberprompt_failed {
+ Irssi::signal_remove('script error', 'load_uberprompt_failed');
+ _print "Script could not be loaded. Script cannot continue. "
+ . "Check you have uberprompt.pl installed in your path and "
+ . "try again.";
+ die "Script Load Failed: " . join(" ", @_);
+}
+
+sub ido_switch_init {
+ Irssi::settings_add_bool('ido_switch', 'ido_switch_debug', 0);
+ Irssi::settings_add_bool('ido_switch', 'ido_use_flex', 1);
+ Irssi::settings_add_int ('ido_switch', 'ido_show_count', 5);
+
+ Irssi::command_bind('ido_switch_start', \&ido_switch_start);
+
+ Irssi::signal_add ('setup changed' => \&setup_changed);
+ Irssi::signal_add_first('gui key pressed' => \&handle_keypress);
+
+ setup_changed();
+}
+
+sub setup_changed {
+ $DEBUG_ENABLED = Irssi::settings_get_bool('ido_switch_debug');
+ $ido_show_count = Irssi::settings_get_int ('ido_show_count');
+ $ido_use_flex = Irssi::settings_get_bool('ido_use_flex');
+}
+
+
+sub ido_switch_start {
+ # store copy of input line to restore later.
+ $input_copy = Irssi::parse_special('$L');
+ $input_pos_copy = Irssi::gui_input_get_pos();
+
+ Irssi::gui_input_set('');
+
+ # set startup flags
+ $ido_switch_active = 1;
+ $search_str = '';
+ $match_index = 0;
+ $mode_type = 'ALL';
+
+ # refresh in case we toggled it last time.
+ $ido_use_flex = Irssi::settings_get_bool('ido_use_flex');
+ $active_only = 0;
+
+ _debug_print "Win cache: " . join(", ", map { $_->{name} } @window_cache);
+
+ _update_cache();
+
+ update_matches();
+ update_window_select_prompt();
+}
+
+sub _update_cache {
+ @window_cache = get_all_windows();
+}
+
+sub _build_win_obj {
+ my ($win, $win_item) = @_;
+
+ my @base = (
+ b_pos => -1,
+ e_pos => -1,
+ hilight_field => 'name',
+ active => $win->{data_level} > 0,
+ num => $win->{refnum},
+ server => $win->{active_server},
+
+ );
+
+ if (defined($win_item)) {
+ return (
+ @base,
+ name => $win_item->{visible_name},
+ type => $win_item->{type},
+ itemname => $win_item->{name},
+ active => $win_item->{data_level} > 0,
+
+ )
+ } else {
+ return (
+ @base,
+ name => $win->{name},
+ type => 'WIN',
+ );
+ }
+}
+
+sub get_all_windows {
+ my @ret;
+
+ foreach my $win (Irssi::windows()) {
+ my @items = $win->items();
+
+ if ($win->{name} ne '') {
+ _debug_print "Adding window: " . $win->{name};
+ push @ret, { _build_win_obj($win, undef) };
+ }
+ if (scalar @items) {
+ foreach my $item (@items) {
+ _debug_print "Adding windowitem: " . $item->{visible_name};
+ push @ret, { _build_win_obj($win, $item) };
+ }
+ } else {
+ _debug_print "Error occurred reading info from window: $win";
+ #_debug_print Dumper($win);
+ }
+ }
+ @ret = _sort_windows(\@ret);
+
+ return @ret;
+
+}
+
+ sub _sort_windows {
+ my $list_ref = shift;
+ my @ret = @$list_ref;
+
+ @ret = sort { $a->{num} <=> $b->{num} } @ret;
+
+ return @ret;
+ }
+
+ sub ido_switch_select {
+ my ($selected) = @_;
+
+ _debug_print "Selecting window: " . $selected->{name};
+
+ Irssi::command("WINDOW GOTO " . $selected->{name});
+
+ if ($selected->{type} ne 'WIN') {
+ _debug_print "Selecting window item: " . $selected->{itemname};
+ Irssi::command("WINDOW ITEM GOTO " . $selected->{itemname});
+ }
+
+ update_matches();
+ }
+
+ sub ido_switch_exit {
+ $ido_switch_active = 0;
+
+ _print_clear();
+
+ Irssi::gui_input_set($input_copy);
+ Irssi::gui_input_set_pos($input_pos_copy);
+ Irssi::signal_emit('change prompt', '', 'UP_INNER');
+ }
+
+ sub _order_matches {
+ return @_[$match_index .. $#_,
+ 0 .. $match_index - 1]
+ }
+
+ sub update_window_select_prompt {
+
+ # take the top $ido_show_count entries and display them.
+ my $match_count = scalar @search_matches;
+ my $show_count = $ido_show_count;
+ my $match_string = '[No match';
+
+ $show_count = $match_count if $match_count < $show_count;
+
+ if ($show_count > 0) { # otherwise, default message above.
+ _debug_print "Showing: $show_count matches";
+
+ my @ordered_matches = _order_matches(@search_matches);
+
+ my @display = @ordered_matches[0..$show_count - 1];
+
+ # determine which items are non-unique, if any.
+
+ my %uniq;
+
+ foreach my $res (@display) {
+ my $name = $res->{name};
+
+ if (exists $uniq{$name}) {
+ push @{$uniq{$name}}, $res;
+ } else {
+ $uniq{$name} = [];
+ push @{$uniq{$name}}, $res;
+ }
+ }
+
+ # and set a flag to ensure they have their network tag applied
+ # to them when drawn.
+ foreach my $name (keys %uniq) {
+ my @values = @{$uniq{$name}};
+ if (@values > 1) {
+ $_->{display_net} = 1 for @values;
+ }
+ }
+
+ # show the first entry in green
+
+ my $first = shift @display;
+ my $formatted_first = _format_display_entry($first, '%g');
+ unshift @display, $formatted_first;
+
+ # and array-slice-map the rest to be red.
+ # or yellow, if they have unviewed activity
+
+ @display[1..$#display]
+ = map
+ {
+ _format_display_entry($_, $_->{active}?'%y':'%r')
+
+ } @display[1..$#display];
+
+ # join em all up
+ $match_string = join ', ', @display;
+ }
+
+ my @indicators;
+
+ # indicator if flex mode is being used (C-f to toggle)
+ push @indicators, $ido_use_flex ? 'Flex' : 'Exact';
+ push @indicators, 'Active' if $active_only;
+ push @indicators, ucfirst(lc($mode_type));
+
+ my $flex = sprintf(' %%k[%%n%s%%k]%%n ', join ',', @indicators);
+
+ my $search = '';
+ $search = (sprintf '`%s\': ', $search_str) if length $search_str;
+
+ Irssi::signal_emit('change prompt', $flex . $search . $match_string,
+ 'UP_INNER');
+ }
+
+
+
+ sub _format_display_entry {
+ my ($obj, $colour) = @_;
+
+ my $field = $obj->{hilight_field};
+ my $hilighted = { name => $obj->{name}, num => $obj->{num} };
+ my $show_tag = $obj->{display_net} || 0;
+
+ if ($obj->{b_pos} >= 0 && $obj->{e_pos} > $obj->{b_pos}) {
+ substr($hilighted->{$field}, $obj->{e_pos}, 0) = '%_';
+ substr($hilighted->{$field}, $obj->{b_pos}, 0) = '%_';
+ _debug_print "Showing $field as: " . $hilighted->{$field}
+ }
+
+ return sprintf('%s%s:%s%s%%n',
+ $colour,
+ $hilighted->{num},
+ $show_tag ? _format_display_tag($obj) : '',
+ $hilighted->{name});
+ }
+
+ sub _format_display_tag {
+ my $obj = shift;
+ if (defined $obj->{server}) {
+ my $server = $obj->{server};
+ my $tag = $server->{tag};
+ return $tag . '/' if length $tag;
+ }
+ return '';
+ }
+
+ sub _check_active {
+ my ($obj) = @_;
+ return 1 unless $active_only;
+ return $obj->{active};
+ }
+
+ sub update_matches {
+
+ _update_cache() unless $search_str;
+
+ if ($mode_type ne 'ALL') {
+ @mode_cache = @window_cache;
+ @window_cache = grep { print "Type: " . $_->{type}; $_->{type} eq $mode_type } @window_cache;
+ } else {
+ @window_cache = @mode_cache if @mode_cache;
+ }
+
+ if ($search_str =~ m/^\d+$/) {
+
+ @search_matches =
+ grep {
+ _check_active($_) and regex_match($_, 'num')
+ } @window_cache;
+
+ } elsif ($ido_use_flex) {
+
+ @search_matches =
+ grep {
+ _check_active($_) and flex_match($_) >= 0
+ } @window_cache;
+
+ } else {
+
+ @search_matches =
+ grep {
+ _check_active($_) and regex_match($_, 'name')
+ } @window_cache;
+ }
+
+ }
+
+ sub regex_match {
+ my ($obj, $field) = @_;
+ if ($obj->{$field} =~ m/^(.*?)\Q$search_str\E(.*?)$/i) {
+ $obj->{hilight_field} = $field;
+ $obj->{b_pos} = length $1;
+ $obj->{e_pos} = $obj->{b_pos} + length($search_str);
+ return 1;
+ }
+ return 0;
+ }
+
+ sub flex_match {
+ my ($obj) = @_;
+
+ my $pattern = $search_str;
+ my $source = $obj->{name};
+
+ _debug_print "Flex match: $pattern / $source";
+
+ # default to matching everything if we don't have a pattern to compare
+ # against.
+
+ return 0 unless $pattern;
+
+ my @chars = split '', lc($pattern);
+ my $ret = -1;
+ my $first = 0;
+
+ my $lc_source = lc($source);
+
+ $obj->{hilight_field} = 'name';
+
+ foreach my $char (@chars) {
+ my $pos = index($lc_source, $char, $ret);
+ if ($pos > -1) {
+
+ # store the beginning of the match
+ $obj->{b_pos} = $pos unless $first;
+ $first = 1;
+
+ _debug_print("matched: $char at $pos in $source");
+ $ret = $pos + 1;
+
+ } else {
+
+ $obj->{b_pos} = $obj->{e_pos} = -1;
+ _debug_print "Flex returning: -1";
+
+ return -1;
+ }
+ }
+
+ _debug_print "Flex returning: $ret";
+
+ #store the end of the match.
+ $obj->{e_pos} = $ret;
+
+ return $ret;
+ }
+
+ sub prev_match {
+
+ $match_index++;
+ if ($match_index > $#search_matches) {
+ $match_index = 0;
+ }
+
+ _debug_print "index now: $match_index";
+ }
+
+ sub next_match {
+
+ $match_index--;
+ if ($match_index < 0) {
+ $match_index = $#search_matches;
+ }
+ _debug_print "index now: $match_index";
+ }
+
+ sub get_window_match {
+ return $search_matches[$match_index];
+ }
+
+ sub handle_keypress {
+ my ($key) = @_;
+
+ return unless $ido_switch_active;
+
+ if ($key == 0) { # C-SPC?
+ _debug_print "\%_Ctrl-space\%_";
+
+ $search_str = '';
+ @window_cache = @search_matches;
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+ }
+
+ if ($key == 3) { # C-c
+ _print_clear();
+ Irssi::signal_stop();
+ return;
+ }
+ if ($key == 4) { # C-d
+ update_network_select_prompt();
+ Irssi::signal_stop();
+ return;
+ }
+
+ if ($key == 5) { # C-e
+ $active_only = not $active_only;
+ Irssi::signal_stop();
+ update_matches();
+ update_window_select_prompt();
+ return;
+ }
+
+ if ($key == 6) { # C-f
+
+ $ido_use_flex = not $ido_use_flex;
+ _update_cache();
+
+ update_matches();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+ }
+ if ($key == 9) { # TAB
+ _debug_print "Tab complete";
+ print_all_matches();
+ Irssi::signal_stop();
+ }
+
+ if ($key == 10) { # enter
+ _debug_print "selecting history and quitting";
+ my $selected_win = get_window_match();
+ ido_switch_select($selected_win);
+
+ ido_switch_exit();
+ Irssi::signal_stop();
+ return;
+ }
+
+ if ($key == 18) { # Ctrl-R
+ _debug_print "skipping to prev match";
+ #update_matches();
+ next_match();
+
+ update_window_select_prompt();
+ Irssi::signal_stop(); # prevent the bind from being re-triggered.
+ return;
+ }
+
+ if ($key == 17) { # Ctrl-q
+ if ($mode_type eq 'CHANNEL') {
+ $mode_type = 'QUERY';
+ } elsif ($mode_type eq 'QUERY') {
+ $mode_type = 'ALL';
+ } else { # ALL
+ $mode_type = 'CHANNEL';
+ }
+ update_matches();
+ update_window_select_prompt();
+ Irssi::signal_stop();
+ }
+
+ if ($key == 19) { # Ctrl-s
+ _debug_print "skipping to next match";
+ prev_match();
+
+ #update_matches();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+ }
+
+ if ($key == 7) { # Ctrl-g
+ _debug_print "aborting search";
+ ido_switch_exit();
+ Irssi::signal_stop();
+ return;
+ }
+
+ if ($key == 21) { # Ctrl-u
+ $search_str = '';
+ update_matches();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+
+ }
+
+ if ($key == 127) { # DEL
+
+ if (length $search_str) {
+ $search_str = substr($search_str, 0, -1);
+ _debug_print "Deleting char, now: $search_str";
+ }
+
+ update_matches();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+ }
+
+ # TODO: handle esc- sequences and arrow-keys?
+
+ if ($key == 27) { # Esc
+ ido_switch_exit();
+ return;
+ }
+
+ if ($key == 32) { # space
+ my $selected_win = get_window_match();
+ ido_switch_select($selected_win);
+
+ prev_match();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+
+ return;
+ }
+
+ if ($key > 32) { # printable
+ $search_str .= chr($key);
+
+ update_matches();
+ update_window_select_prompt();
+
+ Irssi::signal_stop();
+ return;
+ }
+
+ # ignore all other keys.
+ Irssi::signal_stop();
+ }
+
+ ido_switch_init();
+
+ sub update_network_select_prompt {
+
+ my @servers = map
+ {
+ {
+ name => $_->{tag},
+ type => 'SERVER',
+ active => 0,
+ e_pos => -1,
+ b_pos => -1,
+ hilight_field => 'name',
+ }
+ } Irssi::servers();
+
+ my $match_count = scalar @servers;
+ my $show_count = $ido_show_count;
+ my $match_string = '(no matches) ';
+
+ $show_count = $match_count if $match_count < $show_count;
+
+ if ($show_count > 0) {
+ _debug_print "Showing: $show_count matches";
+
+ my @ordered_matches = _order_matches(@servers);
+ my @display = @ordered_matches[0..$show_count - 1];
+
+ # show the first entry in green
+
+ unshift(@display, _format_display_entry(shift(@display), '%g'));
+
+ # and array-slice-map the rest to be red (or yellow for active)
+ @display[1..$#display]
+ = map
+ {
+ _format_display_entry($_, $_->{active}?'%y':'%r')
+
+ } @display[1..$#display];
+
+ # join em all up
+ $match_string = join ', ', @display;
+ }
+
+ my @indicators;
+
+ # indicator if flex mode is being used (C-f to toggle)
+ push @indicators, $ido_use_flex ? 'Flex' : 'Exact';
+ push @indicators, 'Active' if $active_only;
+
+ my $flex = sprintf(' %%k[%%n%s%%k]%%n ', join ',', @indicators);
+
+ my $search = '';
+ $search = (sprintf '`%s\': ', $search_str) if length $search_str;
+
+ Irssi::signal_emit('change prompt', $flex . $search . $match_string,
+ 'UP_INNER');
+
+ }
diff --git a/prompt_info/uberprompt.pl b/prompt_info/uberprompt.pl
index 41803d8..c1c4a55 100644
--- a/prompt_info/uberprompt.pl
+++ b/prompt_info/uberprompt.pl
@@ -117,6 +117,8 @@ use Irssi;
use Irssi::TextUI; # for sbar_items_redraw
use Data::Dumper;
+{ package Irssi::Nick }
+
our $VERSION = "0.2";
our %IRSSI =
(
@@ -235,10 +237,6 @@ sub init {
Irssi::signal_register({'prompt length request' => []});
Irssi::signal_add('prompt length request', \&length_request_handler);
-
- if (DEBUG) {
- Irssi::signal_add 'prompt changed', \&debug_prompt_changed;
- }
}
sub refresh_if_me {
@@ -277,6 +275,13 @@ sub reload_settings {
$DEBUG_ENABLED = Irssi::settings_get_bool('uberprompt_debug');
+ if (DEBUG) {
+ Irssi::signal_add 'prompt changed', 'debug_prompt_changed';
+ } else {
+ Irssi::signal_remove 'prompt changed', 'debug_prompt_changed';
+ }
+
+
my $new = Irssi::settings_get_str('uberprompt_format');
if ($prompt_format ne $new) {
diff --git a/sb-position/sb_position.pl b/sb-position/sb_position.pl
new file mode 100644
index 0000000..3c60205
--- /dev/null
+++ b/sb-position/sb_position.pl
@@ -0,0 +1,112 @@
+
+# Display current position in scrollback in a statusbar item named 'position'.
+
+# Copyright (C) 2010 Simon Ruderich & Tom Feist
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+use strict;
+use warnings;
+
+use Irssi;
+use Irssi::TextUI;
+use POSIX qw(ceil);
+
+{ package Irssi::Nick }
+
+our $VERSION = '0.1';
+our %IRSSI = (
+ authors => 'Simon Ruderich, Tom Feist',
+ contact => 'simon@ruderich.org, shabble+irssi@metavore.org',
+ name => 'sb_position',
+ description => 'Displays current position in scrollback.',
+ license => 'GPLv3 or later',
+ changed => '2010-12-02'
+);
+
+my ($buf, $size, $pos, $height, $empty);
+my ($pages, $cur_page, $percent);
+
+
+init();
+
+sub init {
+
+ # (re-)register it so we can access the WIN_REC object directly.
+ Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]});
+ # primary update signal.
+ Irssi::signal_add('gui page scrolled', \&update_position);
+ Irssi::statusbar_item_register('position', 0, 'position_statusbar');
+
+ Irssi::signal_add("window changed", \&update_position);
+ Irssi::signal_add_last("command clear", \&update_cmd_shim);
+ Irssi::signal_add_last("command scrollback", \&update_cmd_shim);
+ # Irssi::signal_add_last("gui print text finished", sig_statusbar_more_updated);
+
+ update_position(Irssi::active_win());
+}
+
+sub update_cmd_shim {
+ my ($cmd, $server, $witem) = @_;
+
+ my $win = $witem ? $witem->window : Irssi::active_win;
+
+ update_position($win);
+}
+
+sub update_position {
+
+ my $win = shift;
+ return unless $win;
+
+ my $view = $win->view;
+
+ $pos = $view->{ypos};
+ $buf = $view->{buffer};
+ $height = $view->{height};
+ $size = $buf->{lines_count};
+
+ $empty = $view->{empty_linecount};
+ $empty = 0 unless $empty;
+
+
+ $pages = ceil($size / $height);
+ $pages = 1 unless $pages;
+
+ my $buf_pos_cache = $size - $pos + ($height - $empty) - 1;
+
+ if ($pos == -1 or $size < $height) {
+ $cur_page = $pages;
+ $percent = 100;
+ } elsif ($pos > ($size - $height)) {
+ $cur_page = 1;
+ $percent = 0;
+ } else {
+ $cur_page = ceil($buf_pos_cache / $height);
+ $percent = ceil($buf_pos_cache / $size * 100);
+ }
+
+ Irssi::statusbar_items_redraw('position');
+}
+
+sub position_statusbar {
+ my ($statusbar_item, $get_size_only) = @_;
+
+ # Alternate view.
+ #my $sb = "p=$pos, s=$size, h=$height, pp:$cur_page/$pages $percent%%";
+ my $sb = "Page: $cur_page/$pages $percent%%";
+
+ $statusbar_item->default_handler($get_size_only, "{sb $sb}", 0, 1);
+}