From 7363458a88c9dbcf4a81fa2c54ce4b93bc91cdfb Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Mon, 15 Aug 2016 16:11:09 +0200 Subject: import anames script 1.5 --- scripts/anames.pl | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 scripts/anames.pl (limited to 'scripts') diff --git a/scripts/anames.pl b/scripts/anames.pl new file mode 100644 index 0000000..b73203c --- /dev/null +++ b/scripts/anames.pl @@ -0,0 +1,233 @@ +# anames.pl +# Irssi script that adds an /anames command, a clone of /names, with away nicks +# grayed out. +# +# Thanks to Dirm and Chris62vw for the Perl help and coekie for writing the +# evil code to sort the nicklist by the alphabet and rank in nicklist.pl +# +# 1.5 - Fixed halfop display bug (patch by epinephrine), 20100712 +# +# 1.4 - Merged changes from VMiklos and readded /who redirection to prevent +# spamming the status window. - ms, 20090122 +# +# 1.3 - by VMiklos +# Doing /dowho is very annoying and /alias foo /dowho;/anames won't +# work either since anames will work from the old infos. So I've +# modified /anames to just do a /dowho and the nicklist will be printed +# when we'll get the answer from the server. +# +# 1.2 - It seems that redirected events will not pass through the internal +# mechanisms that update user information (like away states). So, it +# /dowho and the periodic execution of the command has been disabled. +# /anames will still work, but new away information will need to be +# obtained by executing a /who on a channel. +# If you can make redirection (execute a /who without the information +# spilling to the status window) work, let me know so I can fix the +# script. +# +# 1.0.1 - Fixed row-determining and max-nick-length code, changed command_add +# calls to refs instead of names. +# +# 1.0 - Added timer for periodic /who of all channels +# +# 0.9 - Initial test release + +use strict; +use Irssi; +use POSIX; +#use Data::Dumper; + +use vars qw($VERSION %IRSSI); + +$VERSION = '1.5'; +%IRSSI = ( + authors => 'Matt "f0rked" Sparks, Miklos Vajna', + contact => 'ms+irssi@quadpoint.org', + name => 'anames', + description => 'a /names display with away nicks colored', + license => 'GPLv2', + url => 'http://quadpoint.org', + changed => '2010-07-12', +); + +# How often to do a /who of all channels (in seconds) +#my $who_timer = 300; + +my $tmp_server; +my $tmp_chan; + + +sub cmd_anames +{ + my($args, $server, $item) = @_; + my $channel = Irssi::active_win->{active}; + $tmp_server = $server; + $tmp_chan = $channel->{"name"}; + + if ($args ne "") { + $server = $args; + $server =~ s/-([^ ]*) .*/\1/; + $tmp_server = Irssi::server_find_tag($server); + $tmp_chan = $args; + $tmp_chan =~ s/-[^ ]* (.*)/\1/; + } + + # set up redirection + $tmp_server->redirect_event("who", 1, $tmp_chan, 0, undef, + { + "event 352" => "redir who_reply", + "event 315" => "redir who_reply_end", + }); + + $tmp_server->command("who $tmp_chan"); +} + + +sub print_anames +{ + my $server = $tmp_server; + my $chan = $tmp_chan; + my $channel = Irssi::Server::channel_find($server, $chan); + my $nick; + + if (!$channel) { + # no nicklist + Irssi::print("Not joined to any channel", MSGLEVEL_CLIENTERROR); + } else { + # Loop through each nick and display + my @nicks; + my($ops, $halfops, $voices, $normal, $away) = (0, 0, 0, 0, 0); + + # sorting from nicklist.pl + foreach my $nick (sort {(($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':'4').lc($a->{'nick'})) + cmp (($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':'4').lc($b->{'nick'}))} $channel->nicks()) { + my $realnick = $nick->{'nick'}; + my $gone = $nick->{'gone'}; + + my $prefix; + if ($nick->{'op'}) { + $prefix = "@"; + $ops++; + } elsif ($nick->{'halfop'}) { + $prefix = "%%"; + $halfops++; + } elsif ($nick->{'voice'}) { + $prefix = "+"; + $voices++; + } else { + $prefix = " "; + $normal++; + } + + $prefix = "%W$prefix%n"; + if ($gone) { + $realnick = "%K$realnick%n"; + $away++; + } + + push @nicks, "$prefix" . $realnick; + } + + my $total = @nicks; + $channel->print("%K[%n%gUsers%n %G" . $chan . "%n%K]%n", + MSGLEVEL_CLIENTCRAP); + columnize_nicks($channel,@nicks); + $channel->print("%W$chan%n: Total of %W$total%n nicks %K[%W$ops%n ops, " . + "%W$halfops%n halfops, %W$voices%n voices, %W$normal%n " . + "normal, %W$away%n away%K]%n", + MSGLEVEL_CLIENTNOTICE); + } +} + + +# create a /names style column, increasing alphabetically going down the +# columns. +sub columnize_nicks +{ + my($channel, @nicks) = @_; + my $total = @nicks; + + # determine max columns + # FIXME: this could be more intelligent (i.e., read window size) + my $cols = Irssi::settings_get_int("names_max_columns"); + $cols = 6 if $cols == 0; + + # determine number of rows + my $rows = round(ceil($total / $cols)); + + # array of rows + my @r; + for (my $i = 0; $i < $cols; $i++) { + # peek at next $rows items, determine max length + my $max_length = find_max_length(@nicks[0 .. $rows - 1]); + + # fill rows + for (my $j = 0; $j < $rows; $j++) { + my $n = shift @nicks; # single nick + if ($n ne "") { + $r[$j] .= "%K[%n$n" . fill_spaces($n,$max_length) . "%K]%n "; + } + } + } + + for (my $m = 0; $m < $rows; $m++) { + chomp $r[$m]; + $channel->print($r[$m], MSGLEVEL_CLIENTCRAP); + } +} + + +sub fill_spaces +{ + my($text, $max_length) = @_; + $text =~ s/%[a-zA-Z]//g; + return " " x ($max_length - length($text)); +} + + +sub find_max_length +{ + my $max_length = 0; + for (my $i = 0; $i < @_; $i++) { + my $nick = $_[$i]; + $nick =~ s/%[a-zA-Z]//g; + if (length($nick) > $max_length) { + $max_length = length($nick); + } + } + return $max_length; +} + + +sub round +{ + my($number) = @_; + return int($number + .5); +} + + +sub who_reply +{ + my($server, $data) = @_; + my(undef, $c, $i, $h, $n, $s) = split / /, $data; + if ($tmp_chan ne $c) { + $tmp_chan = $c; + #print "Got who info for $c"; + } +} + + +sub who_reply_end +{ + print_anames(); + $tmp_chan = ""; +} + + +Irssi::Irc::Server::redirect_register("who", 0, 0, + {"event 352" => 1}, + {"event 315" => 1}, + undef); +Irssi::signal_add("redir who_reply", \&who_reply); +Irssi::signal_add("redir who_reply_end", \&who_reply_end); +Irssi::command_bind("anames", \&cmd_anames); -- cgit v1.2.3 From 51cc823059fe1c394e0505ac300a2f44f4af93d2 Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Mon, 15 Aug 2016 16:14:22 +0200 Subject: update anames to 1.6 --- scripts/anames.pl | 66 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/anames.pl b/scripts/anames.pl index b73203c..686323b 100644 --- a/scripts/anames.pl +++ b/scripts/anames.pl @@ -5,6 +5,9 @@ # Thanks to Dirm and Chris62vw for the Perl help and coekie for writing the # evil code to sort the nicklist by the alphabet and rank in nicklist.pl # +# 1.6 - optional support for unicode nicknames, realnames script, +# formattable summary line (Nei) +# # 1.5 - Fixed halfop display bug (patch by epinephrine), 20100712 # # 1.4 - Merged changes from VMiklos and readded /who redirection to prevent @@ -39,7 +42,7 @@ use POSIX; use vars qw($VERSION %IRSSI); -$VERSION = '1.5'; +$VERSION = '1.6'; # d34833f077fa302 %IRSSI = ( authors => 'Matt "f0rked" Sparks, Miklos Vajna', contact => 'ms+irssi@quadpoint.org', @@ -56,6 +59,9 @@ $VERSION = '1.5'; my $tmp_server; my $tmp_chan; +Irssi::theme_register([ + 'endofanames', '{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal, {hilight $6} away}', +]); sub cmd_anames { @@ -82,6 +88,32 @@ sub cmd_anames $tmp_server->command("who $tmp_chan"); } +local $@; +eval { require Text::CharWidth; }; +unless ($@) { + *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; +} else { + *screen_length = sub { length($_[0]); } +} + +sub object_printformat_module { + my ($object, $level, $module, $format, @args) = @_; + { + # deeeeeeep black magic. + local *CORE::GLOBAL::caller = sub { $module }; + $object->printformat($level, $format, @args); + } +} + +sub core_printformat_module { + my ($level, $module, $format, @args) = @_; + { + # deeeeeeep black magic. + local *CORE::GLOBAL::caller = sub { $module }; + Irssi::printformat($level, $format, @args); + } +} + sub print_anames { @@ -92,16 +124,25 @@ sub print_anames if (!$channel) { # no nicklist - Irssi::print("Not joined to any channel", MSGLEVEL_CLIENTERROR); + core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_joined'); } else { # Loop through each nick and display my @nicks; my($ops, $halfops, $voices, $normal, $away) = (0, 0, 0, 0, 0); # sorting from nicklist.pl - foreach my $nick (sort {(($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':'4').lc($a->{'nick'})) - cmp (($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':'4').lc($b->{'nick'}))} $channel->nicks()) { - my $realnick = $nick->{'nick'}; + my $prefer_real; + if (exists $Irssi::Script::{'realnames::'}) { + my $code = "Irssi::Script::realnames"->can('use_realnames'); + $prefer_real = $code && $code->($channel); + } + my $_real = sub { + my $nick = shift; + $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} + }; + foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').lc($_real->($a)) + cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').lc($_real->($b))} $channel->nicks()) { + my $realnick = $_real->($nick); my $gone = $nick->{'gone'}; my $prefix; @@ -129,13 +170,10 @@ sub print_anames } my $total = @nicks; - $channel->print("%K[%n%gUsers%n %G" . $chan . "%n%K]%n", - MSGLEVEL_CLIENTCRAP); + object_printformat_module($channel, MSGLEVEL_CLIENTCRAP, 'fe-common/core', 'names', $chan); columnize_nicks($channel,@nicks); - $channel->print("%W$chan%n: Total of %W$total%n nicks %K[%W$ops%n ops, " . - "%W$halfops%n halfops, %W$voices%n voices, %W$normal%n " . - "normal, %W$away%n away%K]%n", - MSGLEVEL_CLIENTNOTICE); + $channel->printformat(MSGLEVEL_CLIENTNOTICE, 'endofanames', $chan, $total, $ops, + $halfops, $voices, $normal, $away); } } @@ -181,7 +219,7 @@ sub fill_spaces { my($text, $max_length) = @_; $text =~ s/%[a-zA-Z]//g; - return " " x ($max_length - length($text)); + return " " x ($max_length - screen_length($text)); } @@ -191,8 +229,8 @@ sub find_max_length for (my $i = 0; $i < @_; $i++) { my $nick = $_[$i]; $nick =~ s/%[a-zA-Z]//g; - if (length($nick) > $max_length) { - $max_length = length($nick); + if (screen_length($nick) > $max_length) { + $max_length = screen_length($nick); } } return $max_length; -- cgit v1.2.3 From 542987a4b18ab278a2e658783d3ec0190f8b4b9b Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Mon, 15 Aug 2016 18:01:45 +0200 Subject: some anames improvements --- scripts/anames.pl | 193 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 111 insertions(+), 82 deletions(-) (limited to 'scripts') diff --git a/scripts/anames.pl b/scripts/anames.pl index 686323b..e365a2a 100644 --- a/scripts/anames.pl +++ b/scripts/anames.pl @@ -6,7 +6,7 @@ # evil code to sort the nicklist by the alphabet and rank in nicklist.pl # # 1.6 - optional support for unicode nicknames, realnames script, -# formattable summary line (Nei) +# formattable summary line (default away colour changed!) (Nei) # # 1.5 - Fixed halfop display bug (patch by epinephrine), 20100712 # @@ -36,18 +36,18 @@ # 0.9 - Initial test release use strict; -use Irssi; +use Irssi 20140918; use POSIX; -#use Data::Dumper; +use List::Util 'max'; use vars qw($VERSION %IRSSI); -$VERSION = '1.6'; # d34833f077fa302 +$VERSION = '1.6'; # f200e871df81c77 %IRSSI = ( authors => 'Matt "f0rked" Sparks, Miklos Vajna', contact => 'ms+irssi@quadpoint.org', name => 'anames', - description => 'a /names display with away nicks colored', + description => 'a /names display with away nicks coloured', license => 'GPLv2', url => 'http://quadpoint.org', changed => '2010-07-12', @@ -59,34 +59,12 @@ $VERSION = '1.6'; # d34833f077fa302 my $tmp_server; my $tmp_chan; + Irssi::theme_register([ - 'endofanames', '{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal, {hilight $6} away}', + 'endofanames' => '{channel $0}: Total of {hilight $1} nicks {comment {hilight $2} ops, {hilight $3} halfops, {hilight $4} voices, {hilight $5} normal, {hilight $6} away}', + 'names_awaynick' => '{channick $0}', ]); -sub cmd_anames -{ - my($args, $server, $item) = @_; - my $channel = Irssi::active_win->{active}; - $tmp_server = $server; - $tmp_chan = $channel->{"name"}; - - if ($args ne "") { - $server = $args; - $server =~ s/-([^ ]*) .*/\1/; - $tmp_server = Irssi::server_find_tag($server); - $tmp_chan = $args; - $tmp_chan =~ s/-[^ ]* (.*)/\1/; - } - - # set up redirection - $tmp_server->redirect_event("who", 1, $tmp_chan, 0, undef, - { - "event 352" => "redir who_reply", - "event 315" => "redir who_reply_end", - }); - - $tmp_server->command("who $tmp_chan"); -} local $@; eval { require Text::CharWidth; }; @@ -96,30 +74,63 @@ unless ($@) { *screen_length = sub { length($_[0]); } } + sub object_printformat_module { my ($object, $level, $module, $format, @args) = @_; { - # deeeeeeep black magic. local *CORE::GLOBAL::caller = sub { $module }; $object->printformat($level, $format, @args); } } + sub core_printformat_module { my ($level, $module, $format, @args) = @_; { - # deeeeeeep black magic. local *CORE::GLOBAL::caller = sub { $module }; Irssi::printformat($level, $format, @args); } } +sub cmd_anames +{ + my($args, $server, $item) = @_; + my $channel = $item; + $tmp_server = $server->{"tag"}; + $tmp_chan = $channel->{"name"}; + + if ($args ne "") { + my @args = split ' ', $args; + if ($args[0] =~ /-(.*)/) { + $tmp_server = $1; + $server = Irssi::server_find_tag($tmp_server); + shift @args; + } + $tmp_chan = $args[0]; + } + + unless ($server) { + core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_connected'); + return; + } + + # set up redirection + $server->redirect_event("who", 1, $tmp_chan, 0, undef, + { + "event 352" => "silent event who", + "event 315" => "redir who_reply_end", + }); + + $server->command("who $tmp_chan"); +} + + sub print_anames { - my $server = $tmp_server; + my $server = Irssi::server_find_tag($tmp_server); my $chan = $tmp_chan; - my $channel = Irssi::Server::channel_find($server, $chan); + my $channel = $server ? $server->channel_find($chan) : undef; my $nick; if (!$channel) { @@ -130,50 +141,80 @@ sub print_anames my @nicks; my($ops, $halfops, $voices, $normal, $away) = (0, 0, 0, 0, 0); - # sorting from nicklist.pl my $prefer_real; if (exists $Irssi::Script::{'realnames::'}) { - my $code = "Irssi::Script::realnames"->can('use_realnames'); - $prefer_real = $code && $code->($channel); + my $code = "Irssi::Script::realnames"->can('use_realnames'); + $prefer_real = $code && $code->($channel); } my $_real = sub { - my $nick = shift; - $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} + my $nick = shift; + $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} }; + # sorting from nicklist.pl foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').lc($_real->($a)) cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').lc($_real->($b))} $channel->nicks()) { my $realnick = $_real->($nick); my $gone = $nick->{'gone'}; my $prefix; - if ($nick->{'op'}) { + if ($nick->{'other'}) { + $prefix = chr $nick->{'other'}; + } elsif ($nick->{'op'}) { $prefix = "@"; + } elsif ($nick->{'halfop'}) { + $prefix = "%"; + } elsif ($nick->{'voice'}) { + $prefix = "+"; + } else { + $prefix = " "; + } + + my $format; + if ($nick->{'op'}) { $ops++; + $format = 'names_nick_op'; } elsif ($nick->{'halfop'}) { - $prefix = "%%"; $halfops++; + $format = 'names_nick_halfop'; } elsif ($nick->{'voice'}) { - $prefix = "+"; $voices++; + $format = 'names_nick_voice'; } else { - $prefix = " "; $normal++; + $format = 'names_nick'; } - $prefix = "%W$prefix%n"; if ($gone) { - $realnick = "%K$realnick%n"; + $realnick = $channel->window->format_get_text(__PACKAGE__, $server, $chan, 'names_awaynick', $realnick); $away++; } + my $text = $channel->window->format_get_text('fe-common/core', $server, $chan, $format, $prefix, $realnick); + my $bleak = Irssi::strip_codes($text); - push @nicks, "$prefix" . $realnick; + push @nicks, [ $prefix, $realnick, $format, screen_length($bleak) ]; } my $total = @nicks; object_printformat_module($channel, MSGLEVEL_CLIENTCRAP, 'fe-common/core', 'names', $chan); columnize_nicks($channel,@nicks); $channel->printformat(MSGLEVEL_CLIENTNOTICE, 'endofanames', $chan, $total, $ops, - $halfops, $voices, $normal, $away); + $halfops, $voices, $normal, $away); + } +} + + +{ my %strip_table = ( + # fe-common::core::formats.c:format_expand_styles + # delete format_backs format_fores bold_fores other stuff + (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), + # escape + (map { $_ => $_ } (split //, '{}%')), + ); + sub ir_strip_codes { # strip %codes + my $o = shift; + $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : + $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; + $o } } @@ -188,7 +229,17 @@ sub columnize_nicks # determine max columns # FIXME: this could be more intelligent (i.e., read window size) my $cols = Irssi::settings_get_int("names_max_columns"); - $cols = 6 if $cols == 0; + if ($cols == 0) { + my $width = $channel->window->{width}; + my $ts_format = Irssi::settings_get_str('timestamp_format'); + my $render_str = Irssi::current_theme->format_expand( + Irssi::current_theme->get_format('fe-common/core', 'timestamp')); + (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; + $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; + $render_str = ir_strip_codes($render_str); + $width -= screen_length($render_str); + $cols = max(1, int( $width / max(1, map { 1+ $_->[-1] } @nicks) )); + } # determine number of rows my $rows = round(ceil($total / $cols)); @@ -197,19 +248,21 @@ sub columnize_nicks my @r; for (my $i = 0; $i < $cols; $i++) { # peek at next $rows items, determine max length - my $max_length = find_max_length(@nicks[0 .. $rows - 1]); + my $max_length = max map { $_->[-1] } @nicks[0 .. $rows - 1]; # fill rows for (my $j = 0; $j < $rows; $j++) { my $n = shift @nicks; # single nick - if ($n ne "") { - $r[$j] .= "%K[%n$n" . fill_spaces($n,$max_length) . "%K]%n "; + if ($n->[-1]) { + $r[$j] .= $channel->window->format_get_text('fe-common/core', $channel->{server}, $channel->{visible_name}, + $n->[2], $n->[0], $n->[1] . fill_spaces($n->[-1],$max_length) ); } } } for (my $m = 0; $m < $rows; $m++) { chomp $r[$m]; + $r[$m] =~ s/%/%%/g; $channel->print($r[$m], MSGLEVEL_CLIENTCRAP); } } @@ -217,23 +270,8 @@ sub columnize_nicks sub fill_spaces { - my($text, $max_length) = @_; - $text =~ s/%[a-zA-Z]//g; - return " " x ($max_length - screen_length($text)); -} - - -sub find_max_length -{ - my $max_length = 0; - for (my $i = 0; $i < @_; $i++) { - my $nick = $_[$i]; - $nick =~ s/%[a-zA-Z]//g; - if (screen_length($nick) > $max_length) { - $max_length = screen_length($nick); - } - } - return $max_length; + my($length, $max_length) = @_; + return " " x max (0, $max_length - $length); } @@ -244,20 +282,10 @@ sub round } -sub who_reply -{ - my($server, $data) = @_; - my(undef, $c, $i, $h, $n, $s) = split / /, $data; - if ($tmp_chan ne $c) { - $tmp_chan = $c; - #print "Got who info for $c"; - } -} - - sub who_reply_end { print_anames(); + Irssi::signal_emit('chanquery who end', @_); $tmp_chan = ""; } @@ -266,6 +294,7 @@ Irssi::Irc::Server::redirect_register("who", 0, 0, {"event 352" => 1}, {"event 315" => 1}, undef); -Irssi::signal_add("redir who_reply", \&who_reply); -Irssi::signal_add("redir who_reply_end", \&who_reply_end); -Irssi::command_bind("anames", \&cmd_anames); +Irssi::signal_register({'chanquery who end' => [qw[iobject string]]}); +Irssi::signal_add("redir who_reply", 'who_reply'); +Irssi::signal_add("redir who_reply_end", 'who_reply_end'); +Irssi::command_bind("anames", 'cmd_anames'); -- cgit v1.2.3 From 95fb0b8e4e44f3f7aee5e48496a6a21e887619e8 Mon Sep 17 00:00:00 2001 From: Ailin Nemui Date: Tue, 16 Aug 2016 15:48:00 +0200 Subject: add anames help, settings docs in comments, and get_max_column_count --- scripts/anames.pl | 232 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 185 insertions(+), 47 deletions(-) (limited to 'scripts') diff --git a/scripts/anames.pl b/scripts/anames.pl index e365a2a..385ffe6 100644 --- a/scripts/anames.pl +++ b/scripts/anames.pl @@ -1,7 +1,25 @@ # anames.pl -# Irssi script that adds an /anames command, a clone of /names, with away nicks -# grayed out. + +# Commands +# ======== +# /anames +# * a clone of /names, with away nicks greyed out. + +# Options +# ======= +# /format endofanames +# * equivalent of /format endofnames, the final summary line of /anames command # +# /format names_awaynick +# * colour for the nicks that are away, example: %w$0 +# +# /set anames_force_sync +# * whether to use -sync by default (request fresh /who list) +# if you do not turn this on you will have to manually refresh the +# away list using /who #channel, /anames -sync or you can turn it +# off if you are running the autowho script to automatically update +# the who list periodically + # Thanks to Dirm and Chris62vw for the Perl help and coekie for writing the # evil code to sort the nicklist by the alphabet and rank in nicklist.pl # @@ -36,13 +54,13 @@ # 0.9 - Initial test release use strict; +use warnings; use Irssi 20140918; -use POSIX; -use List::Util 'max'; +use List::Util qw(min max); use vars qw($VERSION %IRSSI); -$VERSION = '1.6'; # f200e871df81c77 +$VERSION = '1.6'; # 6eb152135b1bc6d %IRSSI = ( authors => 'Matt "f0rked" Sparks, Miklos Vajna', contact => 'ms+irssi@quadpoint.org', @@ -50,14 +68,12 @@ $VERSION = '1.6'; # f200e871df81c77 description => 'a /names display with away nicks coloured', license => 'GPLv2', url => 'http://quadpoint.org', - changed => '2010-07-12', ); -# How often to do a /who of all channels (in seconds) -#my $who_timer = 300; my $tmp_server; my $tmp_chan; +my $tmp_count; Irssi::theme_register([ @@ -66,30 +82,60 @@ Irssi::theme_register([ ]); -local $@; -eval { require Text::CharWidth; }; -unless ($@) { +sub cmd_help { + my ($args) = @_; + if ($args =~ /^anames *$/i) { + print CLIENTCRAP <] [] + +%9Parameters:%9 + + -sync: Synchronise the away state of the channel. + -cached: Do not synchronise the away state, use cached info. + -count: Displays the amount of users away in the channel. + + If no arguments are given, the users in the active channel will be + displayed. + +%9Description:%9 + + Display the users who are in channel and grey out those who are away. + +%9See also:%9 NAMES, WHO, WHOIS +HELP + + } +} + + +{ + local $@; + eval { require Text::CharWidth; }; + unless ($@) { *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; -} else { + } else { *screen_length = sub { length($_[0]); } + } } sub object_printformat_module { - my ($object, $level, $module, $format, @args) = @_; - { - local *CORE::GLOBAL::caller = sub { $module }; - $object->printformat($level, $format, @args); - } + my ($object, $level, $module, $format, @args) = @_; + { + local *CORE::GLOBAL::caller = sub { $module }; + $object->printformat($level, $format, @args); + } } sub core_printformat_module { - my ($level, $module, $format, @args) = @_; - { - local *CORE::GLOBAL::caller = sub { $module }; - Irssi::printformat($level, $format, @args); - } + my ($level, $module, $format, @args) = @_; + { + local *CORE::GLOBAL::caller = sub { $module }; + Irssi::printformat($level, $format, @args); + } } @@ -99,14 +145,34 @@ sub cmd_anames my $channel = $item; $tmp_server = $server->{"tag"}; $tmp_chan = $channel->{"name"}; + $tmp_count = undef; - if ($args ne "") { - my @args = split ' ', $args; + my ($force_sync, $force_cache); + my @args = split ' ', $args; + while (@args && $args[0] =~ /^-/) { + if (lc $args[0] eq '-sync') { + $force_sync = 1; shift @args; + } + elsif (lc $args[0] eq '-cached') { + $force_cache = 1; shift @args; + } + elsif (lc $args[0] eq '-count') { + $tmp_count = 1; shift @args; + } + else { + last; + } + } + if (@args) { if ($args[0] =~ /-(.*)/) { $tmp_server = $1; $server = Irssi::server_find_tag($tmp_server); shift @args; } + unless (@args) { + core_printformat_module(MSGLEVEL_CLIENTERROR, 'fe-common/core', 'not_enough_params'); + return; + } $tmp_chan = $args[0]; } @@ -116,13 +182,21 @@ sub cmd_anames } # set up redirection - $server->redirect_event("who", 1, $tmp_chan, 0, undef, + my $sync = Irssi::settings_get_bool('anames_force_sync'); + my $irc = $server->isa('Irssi::Irc::Server'); + if ($irc && ($force_sync || ($sync && !$force_cache))) { + $server->redirect_event("who", 1, $tmp_chan, 0, "", { "event 352" => "silent event who", "event 315" => "redir who_reply_end", }); - $server->command("who $tmp_chan"); + $server->command("who $tmp_chan"); + } elsif ($force_sync) { + print CLIENTERROR "anames -sync is not supported for the chat protocol of the target server"; + } else { + print_anames(); + } } @@ -195,13 +269,69 @@ sub print_anames } my $total = @nicks; - object_printformat_module($channel, MSGLEVEL_CLIENTCRAP, 'fe-common/core', 'names', $chan); - columnize_nicks($channel,@nicks); + unless ($tmp_count) { + object_printformat_module($channel, MSGLEVEL_CLIENTCRAP, 'fe-common/core', 'names', $chan); + columnize_nicks($channel, @nicks); + } $channel->printformat(MSGLEVEL_CLIENTNOTICE, 'endofanames', $chan, $total, $ops, $halfops, $voices, $normal, $away); } } +# src/core/misc.c +sub get_max_column_count { + my $max_width = pop(@_) - 1; + my @item_info = @_; + + my $items_count = @item_info; + if ($items_count == 0) { + return; + } + + my $min_len = max 1, min map { $_->[-1] } @item_info; + my $max_columns = max 1, int($max_width/$min_len); + + my (@columns, @columns_width, @columns_rows); + + for my $n (1 .. $max_columns - 1) { + $columns_rows[$n] = $items_count <= $n+1 ? 1 : + ($items_count+$n)/($n+1); + } + + # for each possible column count, save the column widths and + # find the biggest column count that fits to screen. + my $item_pos = 0; + my $max_len = max 1, map { $_->[-1] } @item_info; + for my $tmp (@item_info) { + my $len = $tmp->[-1]; + + for my $n (1 .. $max_columns - 1) { + no warnings 'uninitialized'; + if ($columns_width[$n] > $max_width) { + next; # too wide + } + + my $col = $item_pos/$columns_rows[$n]; + if ($columns[$n][$col] < $len) { + $columns_width[$n] += $len - $columns[$n][$col]; + $columns[$n][$col] = $len; + } + } + + $item_pos++; + } + + for my $n (reverse 1 .. $max_columns - 1) { + no warnings 'uninitialized'; + if ($columns_width[$n] <= $max_width && + $columns[$n][$n] > 0) { + return $n + 1; + } + } + + return 1; +} + { my %strip_table = ( # fe-common::core::formats.c:format_expand_styles @@ -211,10 +341,10 @@ sub print_anames (map { $_ => $_ } (split //, '{}%')), ); sub ir_strip_codes { # strip %codes - my $o = shift; - $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : - $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; - $o + my $o = shift; + $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : + $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; + $o } } @@ -227,35 +357,40 @@ sub columnize_nicks my $total = @nicks; # determine max columns - # FIXME: this could be more intelligent (i.e., read window size) my $cols = Irssi::settings_get_int("names_max_columns"); - if ($cols == 0) { - my $width = $channel->window->{width}; - my $ts_format = Irssi::settings_get_str('timestamp_format'); - my $render_str = Irssi::current_theme->format_expand( - Irssi::current_theme->get_format('fe-common/core', 'timestamp')); - (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; - $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; - $render_str = ir_strip_codes($render_str); - $width -= screen_length($render_str); - $cols = max(1, int( $width / max(1, map { 1+ $_->[-1] } @nicks) )); + my $width = $channel->window->{width}; + { + my $ts_format = Irssi::settings_get_str('timestamp_format'); + my $render_str = Irssi::current_theme->format_expand( + Irssi::current_theme->get_format('fe-common/core', 'timestamp')); + (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; + $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; + $render_str = ir_strip_codes($render_str); + $width -= screen_length($render_str); + } + $width = max 10, $width; + my $max_cols = get_max_column_count(@nicks, $width - 1); + return unless $max_cols; + if ($cols < 1) { + $cols = $max_cols; } + $cols = min $max_cols, $cols; # determine number of rows - my $rows = round(ceil($total / $cols)); + my $rows = int($total / $cols) + !!($total % $cols); # array of rows my @r; for (my $i = 0; $i < $cols; $i++) { # peek at next $rows items, determine max length - my $max_length = max map { $_->[-1] } @nicks[0 .. $rows - 1]; + my $max_length = max map { $_->[-1] } grep { defined } @nicks[0 .. $rows - 1]; # fill rows for (my $j = 0; $j < $rows; $j++) { my $n = shift @nicks; # single nick if ($n->[-1]) { $r[$j] .= $channel->window->format_get_text('fe-common/core', $channel->{server}, $channel->{visible_name}, - $n->[2], $n->[0], $n->[1] . fill_spaces($n->[-1],$max_length) ); + $n->[2], $n->[0], $n->[1] . fill_spaces($n->[-1], $max_length) ); } } } @@ -285,7 +420,7 @@ sub round sub who_reply_end { print_anames(); - Irssi::signal_emit('chanquery who end', @_); +# Irssi::signal_emit('chanquery who end', @_); $tmp_chan = ""; } @@ -297,4 +432,7 @@ Irssi::Irc::Server::redirect_register("who", 0, 0, Irssi::signal_register({'chanquery who end' => [qw[iobject string]]}); Irssi::signal_add("redir who_reply", 'who_reply'); Irssi::signal_add("redir who_reply_end", 'who_reply_end'); +Irssi::settings_add_bool("anames", "anames_force_sync", 0); Irssi::command_bind("anames", 'cmd_anames'); +Irssi::command_set_options("anames", "sync cached count"); +Irssi::command_bind_last('help' => 'cmd_help'); -- cgit v1.2.3