Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
757 lines (674 sloc) 22.1 KB
use Irssi 20020101.0001 ();
use strict;
use warnings;
use Irssi::TextUI;
use vars qw($VERSION %IRSSI);
$VERSION = "0.6.0";
%IRSSI = (
authors => 'BC-bd',
contact => 'bd@bc-bd.org',
name => 'chanact',
description => 'Adds new powerful and customizable [Act: ...] item (chanelnames,modes,alias). Lets you give alias characters to windows so that you can select those with meta-<char>',
license => 'GNU GPLv2 or later',
url => 'http://bc-bd.org/blog/irssi/'
);
# Please send patches / pull requests to the email listed unter contact above
# and not to the irssi/scripts.irssi.org repository on github.
# Adds new powerful and customizable [Act: ...] item (chanelnames,modes,alias).
# Lets you give alias characters to windows so that you can select those with
# meta-<char>.
#
# for irssi 0.8.2 by bd@bc-bd.org
#
# inspired by chanlist.pl by 'cumol@hammerhart.de'
#
#########
# Contributors
#########
#
# veli@piipiip.net original /window_alias code
# qrczak@knm.org.pl chanact_abbreviate_names
# qerub@home.se Extra chanact_show_mode and chanact_chop_status
# madduck@madduck.net Better channel aliasing (case-sensitive, cross-network)
# chanact_filter_windowlist basis
# jast@heapsort.de Updated documentation
# ivo@o2w.nl win->{hilight} patch
# Bazerka base patch for sorting by level change
# updated documentation
# mrtnpaolo@gmail.com rename commands
# tslocum@gmail.com Case-insensitive aliases
#
#########
# USAGE
###
#
# copy the script to ~/.irssi/scripts/
#
# In irssi:
#
# /script load chanact
# /statusbar window add -after act chanact
#
# If you want the item to appear on another position read the help
# for /statusbar.
# To remove the [Act: 1,2,3] item type:
#
# /statusbar window remove act
#
# To see all chanact options type:
#
# / set chanact_
#
# After these steps you have your new statusbar item and you can start giving
# aliases to your windows. Go to the window you want to give the alias to
# and say:
#
# /chanact_window_alias <alias char>
#
# You can remove the aliases with from an aliased window:
#
# /chanact_window_unalias
#
# To see a list of your windows use:
#
# /window list
#
# To make your bindings permanent you will need to save the config and layout
# before quiting irssi:
#
# /save
# /layout save
#
#########
# OPTIONS
#########
#
# /set chanact_chop_status <ON|OFF>
# * ON : shorten (Status) to S
# * OFF : don't do it
#
# /set chanact_sort <refnum|activity|level+refnum|level+activity>
# sort by ...
# refnum : refnum
# activity : last active window
# level+refnum : data_level of window, then refnum
# level+activity : data_level then activity
#
# /set chanact_display <string>
# * string : Format String for one Channel. The following $'s are expanded:
# $C : Channel
# $N : Number of the Window
# $M : Mode in that channel
# $H : Start highlightning
# $S : Stop highlightning
# * example:
#
# /set chanact_display $H$N:$M.$S$C
#
# will give you on #irssi.de if you have voice
#
# [3:+.#irssi.de]
#
# with '3:+.' highlighted and the channel name printed in regular color
#
# /set chanact_case_sensitive <ON|OFF>
# * ON : Aliases are case-sensitive
# * OFF : Aliases are case-insensitive
#
# Existing aliases must be reapplied after changing this option
#
# Switching from OFF to ON _after_ aliases have been defined, and
# then redefining or changing an existing alias will leave some
# bindings behind, e.g.
#
# /set chanact_case_sensitive OFF
# /chanact_window_alias x
#
# -> window reachable with meta-x/meta-X
#
# /set chanact_case_sensitive ON
# /chanact_window_alias y
#
# -> window reachable with meta-y/meta-X
#
# /set chanact_display_alias <string>
# as 'chanact_display' but is used if the window has an alias and
# 'chanact_show_alias' is set to on.
#
# /set chanact_show_names <ON|OFF>
# * ON : show the channelnames after the number/alias
# * OFF : don't show the names
#
# /set chanact_abbreviate_names <int>
# * 0 : don't abbreviate
# * <int> : strip channel name prefix character and leave only
# that many characters of the proper name
#
# /set chanact_show_alias <ON|OFF>
# * ON : show the aliase instead of the refnum
# * OFF : shot the refnum
#
# /set chanact_header <str>
# * <str> : Characters to be displayed at the start of the item.
# Defaults to: "Act: "
#
# /set chanact_separator <str>
# * <str> : Charater to use between the channel entries
#
# /set chanact_autorenumber <ON|OFF>
# * ON : Move the window automatically to first available slot
# starting from "chanact_renumber_start" when assigning
# an alias to window. Also moves the window back to a
# first available slot from refnum 1 when the window
# loses it's alias.
# * OFF : Don't move the windows automatically
#
# /set chanact_renumber_start <int>
# * <int> : Move the window to first available slot after this
# num when "chanact_autorenumber" is ON.
#
# /set chanact_remove_hash <ON|OFF>
# * ON : Remove &#+!= from channel names
# * OFF : Don't touch channel names
#
# /set chanact_remove_prefix <string>
# * <string> : Regular expression used to remove from the
# beginning of the channel name.
# * example :
# To shorten a lot of debian channels:
#
# /set chanact_remove_prefix deb(ian.(devel-)?)?
#
# /set chanact_filter <int>
# * 0 : show all channels
# * 1 : hide channels without activity
# * 2 : hide channels with only join/part/etc messages
# * 3 : hide channels with text messages
# * 4 : hide all channels (now why would you want to do that)
#
# /set chanact_filter_windowlist <string>
# * <string> : space-separated list of windows for which to use
# chanact_filter_windowlist_level instead of
# chanact_filter.
#
# Alternatively, an entry can be postfixed with
# a comma (',') and the level to use for that
# window.
#
# The special string @QUERIES matches all queries.
#
# /set chanact_filter_windowlist_level <int>
# Use this level to filter all windows listed in chanact_filter_windowlist.
# You can use these two settings to apply different filter levels to different
# windows. Defaults to 0.
#
#########
# HINTS
#########
#
# If you have trouble with wrong colored entries your 'default.theme' might
# be too old. Try on a shell:
#
# $ mv ~/.irssi/default.theme /tmp/
#
# And in irssi:
# /reload
# /save
#
###
#################
my %show = (
0 => "{%n ", # NOTHING
1 => "{sb_act_text ", # TEXT
2 => "{sb_act_msg ", # MSG
3 => "{sb_act_hilight ", # HILIGHT
);
# comparison operators for our sort methods
my %sort = (
'refnum' => '$a->{refnum} <=> $b->{refnum};',
'activity' => '$b->{last_line} <=> $a->{last_line};',
'level+refnum' => '$b->{data_level} <=> $a->{data_level} ||
$a->{refnum} <=> $b->{refnum};',
'level+activity'=> '$b->{data_level} <=> $a->{data_level} ||
$b->{last_line} <=> $a->{last_line};',
);
my ($actString,$needRemake);
sub expand {
my ($string, %format) = @_;
my ($exp, $repl);
$string =~ s/\$$exp/$repl/g while (($exp, $repl) = each(%format));
return $string;
}
# method will get called every time the statusbar item will be displayed
# but we dont need to recreate the item every time so we first
# check if something has changed and only then we recreate the string
# this might just save some cycles
sub chanact {
my ($item, $get_size_only) = @_;
if ($needRemake) {
remake();
}
$item->default_handler($get_size_only, $actString, "", 1);
}
# build a hash to easily access special levels based on
# chanact_filter_windowlist
sub calculate_windowlist() {
my @matchlist = split ' ', Irssi::settings_get_str('chanact_filter_windowlist');
my $default = Irssi::settings_get_int('chanact_filter_windowlist_level');
my %windowlist;
foreach my $m (@matchlist) {
my ($name, $level) = split(/,/, $m);
$windowlist{$name} = $level ? $level : $default;
}
return %windowlist;
}
# calculate level per window
sub calculate_levels(@) {
my (@windows) = @_;
my %matches = calculate_windowlist();
my $default = Irssi::settings_get_int('chanact_filter');
my %levels;
foreach my $win (@windows) {
!ref($win) && next;
my $name = $win->get_active_name;
# skip nameless windows
next unless $name;
if ($name && exists($matches{$name})) {
$levels{$name} = $matches{$name};
} else {
$levels{$name} = $default;
}
}
if (exists($matches{'@QUERIES'})) {
$levels{'@QUERIES'} = $matches{'@QUERIES'};
} else {
$levels{'@QUERIES'} = $default;
}
return %levels;
}
# this is the real creation method
sub remake() {
my ($afternumber,$finish,$hilight,$mode,$number,$display,@windows);
my $separator = Irssi::settings_get_str('chanact_separator');
my $abbrev = Irssi::settings_get_int('chanact_abbreviate_names');
my $remove_prefix = Irssi::settings_get_str('chanact_remove_prefix');
my $remove_hash = Irssi::settings_get_bool('chanact_remove_hash');
my $method = $sort{Irssi::settings_get_str('chanact_sort')};
@windows = sort { eval $method } Irssi::windows();
my %levels = calculate_levels(@windows);
$actString = "";
foreach my $win (@windows) {
# since irssi is single threaded this shouldn't happen
!ref($win) && next;
my $active = $win->{active};
# define $type to emtpy string and overwrite if we do have an
# active item. we need this to display windows without active
# items e.g. '(status)'
my $type = "";
$type = $active->{type} if $active;
my $name = $win->get_active_name;
# skip windows without a name
next unless $name;
my $filter_level =
$type eq 'QUERY' ? $levels{'@QUERIES'} : $levels{$name};
# now, skip windows with data of level lower than the
# filter level
next if ($win->{data_level} < $filter_level);
# alright, the activity is important, let's show the window
# after a bit of additional processing.
# (status) is an awfull long name, so make it short to 'S'
# some people don't like it, so make it configurable
if (Irssi::settings_get_bool('chanact_chop_status')
&& $name eq "(status)") {
$name = "S";
}
# check if we should show the mode
$mode = "";
if ($type eq "CHANNEL") {
my $server = $win->{active_server};
!ref($server) && next;
my $channel = $server->channel_find($name);
!ref($channel) && next;
my $nick = $channel->nick_find($server->{nick});
!ref($nick) && next;
if ($nick->{op}) {
$mode = "@";
} elsif ($nick->{voice}) {
$mode = "+";
} elsif ($nick->{halfop}) {
$mode = "%";
}
}
# in case we have a specific hilightcolor use it
if ($win->{hilight_color}) {
$hilight = "{sb_act_hilight_color $win->{hilight_color} ";
} else {
$hilight = $show{$win->{data_level}};
}
if ($remove_prefix) {
$name =~ s/^([&#+!=]?)$remove_prefix/$1/;
}
if ($abbrev) {
if ($name =~ /^[&#+!=]/) {
$name = substr($name, 1, $abbrev + 1);
} else {
$name = substr($name, 0, $abbrev);
}
}
if ($remove_hash) {
$name =~ s/^[&#+!=]//;
}
if (Irssi::settings_get_bool('chanact_show_alias') == 1 &&
$win->{name} =~ /^([a-zA-Z+]):(.+)$/) {
$number = "$1";
$display = Irssi::settings_get_str('chanact_display_alias');
} else {
$number = $win->{refnum};
$display = Irssi::settings_get_str('chanact_display');
}
# fixup { and } in nicks, those are used by irssi themes
$name =~ s/([{}])/%$1/g;
$actString .= expand($display,"C",$name,"N",$number,"M",$mode,"H",$hilight,"S","}{sb_background}").$separator;
}
# assemble the final string
if ($actString ne "") {
# Remove the last separator
$actString =~ s/$separator$//;
$actString = "{sb ".Irssi::settings_get_str('chanact_header').$actString."}";
}
# no remake needed any longer
$needRemake = 0;
}
# method called because of some events. here we dont remake the item but just
# remember that we have to remake it the next time we are called
sub chanactHasChanged()
{
# if needRemake is already set, no need to trigger a redraw as we will
# be redrawing the item anyway.
return if $needRemake;
$needRemake = 1;
Irssi::statusbar_items_redraw('chanact');
}
sub setup_changed {
my $method = Irssi::settings_get_str('chanact_sort');
unless (exists($sort{$method})) {
Irssi::print("chanact: invalid sort method, setting to 'refnum'."
." valid methods are: ".join(", ", sort(keys(%sort))));
my $method = Irssi::settings_set_str('chanact_sort', 'refnum');
}
chanactHasChanged();
}
# Remove key binding for current window
sub unbind {
my ($name, $server) = @_;
# chanact'ified windows have a name like this: X:servertag/name. if we
# can't find anything like this we return and do not unbind nor renumber
# anything
my ($key, $tag) = split(/:/, $name);
return unless $tag;
($tag, $name) = split('/', $tag);
return unless (length($key) == 1);
if (Irssi::settings_get_bool('chanact_case_sensitive')) {
$server->command("/bind -delete meta-$key");
} else {
$server->command("/bind -delete meta-" . lc($key));
$server->command("/bind -delete meta-" . uc($key));
}
}
# Remove alias
sub cmd_window_unalias {
my ($data, $server, $witem) = @_;
if ($data ne '') {
Irssi::print("chanact: /chanact_window_unalias does not take any ".
"parameters, Run it in the window you want to unalias");
return;
}
my $win = Irssi::active_win();
my $name = Irssi::active_win()->{name};
unbind($name, $server);
# set the windowname back to it's old one. We don't bother checking
# for a vaild name here, as we want to remove the current one and if
# worse comes to wors set an empty one.
$win->set_name($name);
# if autorenumbering is off, we are done.
return unless (Irssi::settings_get_bool('chanact_autorenumber'));
# we are renumbering, so move the window to the lowest available
# refnum.
my $refnum = 1;
while (Irssi::window_find_refnum($refnum)) {
$refnum++;
}
$win->set_refnum($refnum);
Irssi::print("chanact: moved wintow to refnum $refnum");
}
# Make an alias
sub cmd_window_alias {
my ($data, $server, $witem) = @_;
my $rn_start = Irssi::settings_get_int('chanact_renumber_start');
unless ($data =~ /^[a-zA-Z+]$/) {
Irssi::print("Usage: /chanact_window_alias <char>");
return;
}
# in case of an itemless window $witem is undef, thus future operations
# on it fail. to prevent this we pull in the current window.
#
# Also we need to initialize $winname, else we would get a broken name:
#
# 'name' => 'S:IRCnet/S:IRCnet/',
#
my $window;
my $winname = "";
if (defined($witem)) {
$window = $witem->window();
$winname = $witem->{name};
} else {
$window = Irssi::active_win();
$winname = $window->{name};
}
unbind($window->{name}, $server);
my $winnum = $window->{refnum};
if (Irssi::settings_get_bool('chanact_autorenumber') == 1 &&
$window->{refnum} < $rn_start) {
my $old_refnum = $window->{refnum};
$winnum = $rn_start;
# Find the first available slot and move the window
while (Irssi::window_find_refnum($winnum)) { $winnum++; }
$window->set_refnum($winnum);
Irssi::print("Moved the window from $old_refnum to $winnum");
}
my $winserver = $window->{active_server}->{tag};
my $winhandle = "$winserver/$winname";
# cmd_window_unalias relies on a certain format here
my $name = "$data:$winhandle";
$window->set_name($name);
if (Irssi::settings_get_bool('chanact_case_sensitive')) {
$server->command("/bind meta-$data change_window $name");
} else {
$server->command("/bind meta-" . lc($data) . " change_window $name");
$server->command("/bind meta-" . uc($data) . " change_window $name");
}
Irssi::print("Window $winhandle is now accessible with meta-$data");
}
$needRemake = 1;
# Window alias command
Irssi::command_bind('chanact_window_alias','cmd_window_alias');
Irssi::command_bind('chanact_window_unalias','cmd_window_unalias');
# our config item
Irssi::settings_add_str('chanact', 'chanact_display', '$H$N:$M$C$S');
Irssi::settings_add_str('chanact', 'chanact_display_alias', '$H$N$M$S');
Irssi::settings_add_int('chanact', 'chanact_abbreviate_names', 0);
Irssi::settings_add_bool('chanact', 'chanact_case_sensitive', 1);
Irssi::settings_add_bool('chanact', 'chanact_show_alias', 1);
Irssi::settings_add_str('chanact', 'chanact_separator', " ");
Irssi::settings_add_bool('chanact', 'chanact_autorenumber', 0);
Irssi::settings_add_bool('chanact', 'chanact_remove_hash', 0);
Irssi::settings_add_str('chanact', 'chanact_remove_prefix', "");
Irssi::settings_add_int('chanact', 'chanact_renumber_start', 50);
Irssi::settings_add_str('chanact', 'chanact_header', "Act: ");
Irssi::settings_add_bool('chanact', 'chanact_chop_status', 1);
Irssi::settings_add_str('chanact', 'chanact_sort', 'refnum');
Irssi::settings_add_int('chanact', 'chanact_filter', 0);
Irssi::settings_add_str('chanact', 'chanact_filter_windowlist', "");
Irssi::settings_add_int('chanact', 'chanact_filter_windowlist_level', 0);
# register the statusbar item
Irssi::statusbar_item_register('chanact', '$0', 'chanact');
# according to cras we shall not call this
# Irssi::statusbars_recreate_items();
# register all that nifty callbacks on special events
Irssi::signal_add_last('setup changed', 'setup_changed');
Irssi::signal_add_last('window changed', 'chanactHasChanged');
Irssi::signal_add_last('window item changed', 'chanactHasChanged');
Irssi::signal_add_last('window hilight', 'chanactHasChanged');
Irssi::signal_add_last('window item hilight', 'chanactHasChanged');
Irssi::signal_add("window created", "chanactHasChanged");
Irssi::signal_add("window destroyed", "chanactHasChanged");
Irssi::signal_add("window name changed", "chanactHasChanged");
Irssi::signal_add("window activity", "chanactHasChanged");
Irssi::signal_add("print text", "chanactHasChanged");
Irssi::signal_add('nick mode changed', 'chanactHasChanged');
###############
###
#
# Changelog
#
# 0.6.0
# - fixed URL
# - now with 'use warnings'
# - fix cmd_window_unalias call from cmd_window_alias
# - fix Use of uninitialized value $name in hash element warnings
# - return from cmd_window_unalias if the window has no valid
# chanact'ified name
# - rename /window_(un)alias to /chanact_window_(un)alias
# - fix refnum renumber race
# - added setting to allow case-insensitive window aliases
#
# 0.5.14
# - fix itemless window handling, thx Bazerka
# - fix /window_alias for itemless windows
# - fix /window_unalias. Also longer takes an argument
# - added sorting by level, based on patch by Bazerka
# + retired chanact_sort_by_activity, integrated in chanact_sort
#
# 0.5.13
# - trivial cleanup in cmd_window_alias()
# - updated documentation regarding /layout save, thx Bazerka
# - removed cmd_rebuild_aliases(), no longer working since we use channel
# names to select windows and not refnums
# - removed refnum_changed(), see cmd_rebuild_aliases() above
#
# 0.5.12
# - Use comma instead of colon as windowlist separator, patch by martin f.
# krafft, reported by James Vega
#
# 0.5.11
# - added chanact_filter_windowlist based on a patch by madduck@madduck.net
# - fixed display error for nicks/channels with { or } in them
# - fixed chanact_header, was hidden behind chanact_filter
# - fixed documentation
# + removed chanact_show_mode, long gone
#
# 0.5.10
# - fixed irssi crash when using Irssi::print from within remake()
# - added option to filter out some data levels, based on a patch by
# Juergen Jung <juergen@Winterkaelte.de>, see
# https://bc-bd.org/trac/irssi/ticket/15
# + retired chanact_show_all in favour of chanact_filter
#
# 0.5.9
# - changes by stefan voelkel
# + sort channels by activity, see
# https://bc-bd.org/trac/irssi/ticket/5, based on a patch by jan
# krueger
# + fixed chrash on /exec -interactive, see
# https://bc-bd.org/trac/irssi/ticket/7
#
# - changes by Jan 'jast' Krueger <jast@heapsort.de>, 2004-06-22
# + updated documentation in script's comments
#
# - changes by Ivo Timmermans <ivo@o2w.nl>
# + honor actcolor /hilight setting if present
#
# 0.5.8
# - made aliases case-sensitive and include network in channel names by madduck
#
# 0.5.7
# - integrated remove patch by Christoph Berg <myon@debian.org>
#
# 0.5.6
# - fixed a bug (#1) reported by Wouter Coekaert
#
# 0.5.5
# - some speedups from David Leadbeater <dgl@dgl.cx>
#
#
# 0.5.4
# - added help for chanact_display_alias
#
# 0.5.3
# - added '+' to the available chars of aliase's
# - added chanact_display_alias to allow different display modes if the window
# has an alias
#
# 0.5.2
# - removed unused chanact_show_name settings (thx to Qerub)
# - fixed $mode display
# - guarded reference operations to (hopefully) fix errors on server disconnect
#
# 0.5.1
# - small typo fixed
#
# 0.5.0
# - changed chanact_show_mode to chanact_display. reversed changes from
# Qerub through that, but kept funcionality.
# - removed chanact_color_all since it is no longer needed
#
# 0.4.3
# - changes by Qerub
# + added chanact_show_mode to show the mode just before the channel name
# + added chanact_chop_status to be able to control the (status) chopping
# [bd] minor implementation changes
# - moved Changelog to the end of the file since it is getting pretty big
#
# 0.4.2
# - changed back to old version numbering sheme
# - added '=' to Qrczak's chanact_abbreviate_names stuff :)
# - added chanact_header
#
# 0.41q
# - changes by Qrczak
# + added setting 'chanact_abbreviate_names'
# + windows are sorted by refnum; I didn't understand the old
# logic and it broke sorting for numbers above 9
#
# 0.41
# - minor updates
# + fixed channel sort [veli]
# + removed few typos and added some documentation [veli]
#
# 0.4
# - merge with window_alias.pl
# + added /window_alias from window_alias.pl by veli@piipiip.net
# + added setting 'chanact_show_alias'
# + added setting 'chanact_show_names'
# + changed setting 'chanact_show_mode' to int
# + added setting 'chanact_separator' [veli]
# + added setting 'chanact_autorenumber' [veli]
# + added setting 'chanact_renumber_start' [veli]
# + added /window_unalias [veli]
# + moved setting to their own group 'chanact' [veli]
#
# 0.3
# - merge with chanlist.pl
# + added setting 'chanact_show_mode'
# + added setting 'chanact_show_all'
#
# 0.2
# - added 'Act' to the item
# - added setting 'chanact_color_all'
# - finally found format for statusbar hilight
#
# 0.1
# - Initial Release
#
###
################