فهرست منبع

add irssi scripts for nick colors, and nick and window lists

Weiyi Lou 13 سال پیش
والد
کامیت
deebd959da
4فایلهای تغییر یافته به همراه1440 افزوده شده و 0 حذف شده
  1. 4 0
      RUNME
  2. 156 0
      irssi/scripts/autorun/nickcolor.pl
  3. 611 0
      irssi/scripts/autorun/nicklist.pl
  4. 669 0
      irssi/scripts/autorun/wlstat.pl

+ 4 - 0
RUNME

@@ -78,4 +78,8 @@ ln -s dotfiles/screen/screenrc ~/.screenrc
 mv ~/.nethackrc "$BACKUP_DIR"
 ln -s dotfiles/nethack/nethackrc ~/.nethackrc
 
+# Irssi
+mv ~/.irssi "$BACKUP_DIR"
+ln -s dotfiles/irssi ~/.irssi
+
 echo "Setup complete!"

+ 156 - 0
irssi/scripts/autorun/nickcolor.pl

@@ -0,0 +1,156 @@
+use strict;
+use Irssi 20020101.0250 ();
+use vars qw($VERSION %IRSSI); 
+$VERSION = "1";
+%IRSSI = (
+    authors     => "Timo Sirainen, Ian Peters",
+    contact	=> "tss\@iki.fi", 
+    name        => "Nick Color",
+    description => "assign a different color for each nick",
+    license	=> "Public Domain",
+    url		=> "http://irssi.org/",
+    changed	=> "2002-03-04T22:47+0100"
+);
+
+# hm.. i should make it possible to use the existing one..
+Irssi::theme_register([
+  'pubmsg_hilight', '{pubmsghinick $0 $3 $1}$2'
+]);
+
+my %saved_colors;
+my %session_colors = {};
+my @colors = qw/2 3 4 5 6 7 9 10 11 12 13/;
+
+sub load_colors {
+  open COLORS, "$ENV{HOME}/.irssi/saved_colors";
+
+  while (<COLORS>) {
+    # I don't know why this is necessary only inside of irssi
+    my @lines = split "\n";
+    foreach my $line (@lines) {
+      my($nick, $color) = split ":", $line;
+      $saved_colors{$nick} = $color;
+    }
+  }
+
+  close COLORS;
+}
+
+sub save_colors {
+  open COLORS, ">$ENV{HOME}/.irssi/saved_colors";
+
+  foreach my $nick (keys %saved_colors) {
+    print COLORS "$nick:$saved_colors{$nick}\n";
+  }
+
+  close COLORS;
+}
+
+# If someone we've colored (either through the saved colors, or the hash
+# function) changes their nick, we'd like to keep the same color associated
+# with them (but only in the session_colors, ie a temporary mapping).
+
+sub sig_nick {
+  my ($server, $newnick, $nick, $address) = @_;
+  my $color;
+
+  $newnick = substr ($newnick, 1) if ($newnick =~ /^:/);
+
+  if ($color = $saved_colors{$nick}) {
+    $session_colors{$newnick} = $color;
+  } elsif ($color = $session_colors{$nick}) {
+    $session_colors{$newnick} = $color;
+  }
+}
+
+# This gave reasonable distribution values when run across
+# /usr/share/dict/words
+
+sub simple_hash {
+  my ($string) = @_;
+  chomp $string;
+  my @chars = split //, $string;
+  my $counter;
+
+  foreach my $char (@chars) {
+    $counter += ord $char;
+  }
+
+  $counter = $colors[$counter % 11];
+
+  return $counter;
+}
+
+# FIXME: breaks /HILIGHT etc.
+sub sig_public {
+  my ($server, $msg, $nick, $address, $target) = @_;
+  my $chanrec = $server->channel_find($target);
+  return if not $chanrec;
+  my $nickrec = $chanrec->nick_find($nick);
+  return if not $nickrec;
+  my $nickmode = $nickrec->{op} ? "@" : $nickrec->{voice} ? "+" : "";
+
+  # Has the user assigned this nick a color?
+  my $color = $saved_colors{$nick};
+
+  # Have -we- already assigned this nick a color?
+  if (!$color) {
+    $color = $session_colors{$nick};
+  }
+
+  # Let's assign this nick a color
+  if (!$color) {
+    $color = simple_hash $nick;
+    $session_colors{$nick} = $color;
+  }
+
+  $color = "0".$color if ($color < 10);
+  $server->command('/^format pubmsg {pubmsgnick $2 {pubnick '.chr(3).$color.'$0}}$1');
+}
+
+sub cmd_color {
+  my ($data, $server, $witem) = @_;
+  my ($op, $nick, $color) = split " ", $data;
+
+  $op = lc $op;
+
+  if (!$op) {
+    Irssi::print ("No operation given");
+  } elsif ($op eq "save") {
+    save_colors;
+  } elsif ($op eq "set") {
+    if (!$nick) {
+      Irssi::print ("Nick not given");
+    } elsif (!$color) {
+      Irssi::print ("Color not given");
+    } elsif ($color < 2 || $color > 14) {
+      Irssi::print ("Color must be between 2 and 14 inclusive");
+    } else {
+      $saved_colors{$nick} = $color;
+    }
+  } elsif ($op eq "clear") {
+    if (!$nick) {
+      Irssi::print ("Nick not given");
+    } else {
+      delete ($saved_colors{$nick});
+    }
+  } elsif ($op eq "list") {
+    Irssi::print ("\nSaved Colors:");
+    foreach my $nick (keys %saved_colors) {
+      Irssi::print (chr (3) . "$saved_colors{$nick}$nick" .
+		    chr (3) . "1 ($saved_colors{$nick})");
+    }
+  } elsif ($op eq "preview") {
+    Irssi::print ("\nAvailable colors:");
+    foreach my $i (2..14) {
+      Irssi::print (chr (3) . "$i" . "Color #$i");
+    }
+  }
+}
+
+load_colors;
+
+Irssi::command_bind('color', 'cmd_color');
+
+Irssi::signal_add('message public', 'sig_public');
+Irssi::signal_add('event nick', 'sig_nick');

+ 611 - 0
irssi/scripts/autorun/nicklist.pl

@@ -0,0 +1,611 @@
+# for documentation: see http://wouter.coekaerts.be/site/irssi/nicklist
+
+use Irssi;
+use strict;
+use IO::Handle; # for (auto)flush
+use Fcntl; # for sysopen
+use vars qw($VERSION %IRSSI);
+$VERSION = '0.4.6';
+%IRSSI = (
+	authors     => 'Wouter Coekaerts',
+	contact     => 'coekie@irssi.org',
+	name        => 'nicklist',
+	description => 'draws a nicklist to another terminal, or at the right of your irssi in the same terminal',
+	license     => 'GPLv2',
+	url         => 'http://wouter.coekaerts.be/irssi',
+	changed     => '29/06/2004'
+);
+
+sub cmd_help {
+	print ( <<EOF
+Commands:
+NICKLIST HELP
+NICKLIST SCROLL <nr of lines>
+NICKLIST SCREEN
+NICKLIST FIFO
+NICKLIST OFF
+NICKLIST UPDATE
+
+For help see: http://wouter.coekaerts.be/site/irssi/nicklist
+
+in short:
+
+1. FIFO MODE
+- in irssi: /NICKLIST FIFO (only the first time, to create the fifo)
+- in a shell, in a window where you want the nicklist: cat ~/.irssi/nicklistfifo
+- back in irssi:
+    /SET nicklist_heigth <height of nicklist>
+    /SET nicklist_width <width of nicklist>
+    /NICKLIST FIFO
+
+2. SCREEN MODE
+- start irssi inside screen ("screen irssi")
+- /NICKLIST SCREEN
+EOF
+    );
+}
+
+my $prev_lines = 0;                  # number of lines in previous written nicklist
+my $scroll_pos = 0;                  # scrolling position
+my $cursor_line;                     # line the cursor is currently on
+my ($OFF, $SCREEN, $FIFO) = (0,1,2); # modes
+my $mode = $OFF;                     # current mode
+my $need_redraw = 0;                 # nicklist needs redrawing
+my $screen_resizing = 0;             # terminal is being resized
+my $active_channel;                  # (REC)
+
+my @nicklist=();                     # array of hashes, containing the internal nicklist of the active channel
+	# nick => realnick
+	# mode =>
+	my ($MODE_OP, $MODE_HALFOP, $MODE_VOICE, $MODE_NORMAL) = (0,1,2,3);
+	# status =>
+	my ($STATUS_NORMAL, $STATUS_JOINING, $STATUS_PARTING, $STATUS_QUITING, $STATUS_KICKED, $STATUS_SPLIT) = (0,1,2,3,4,5);
+	# text => text to be printed
+	# cmp => text used to compare (sort) nicks
+
+
+# 'cached' settings
+my ($screen_prefix, $irssi_width, @prefix_mode, @prefix_status, $height, $nicklist_width);
+
+sub read_settings {
+	($screen_prefix = Irssi::settings_get_str('nicklist_screen_prefix')) =~ s/\\e/\033/g;
+
+	($prefix_mode[$MODE_OP] = Irssi::settings_get_str('nicklist_prefix_mode_op')) =~ s/\\e/\033/g;
+	($prefix_mode[$MODE_HALFOP] = Irssi::settings_get_str('nicklist_prefix_mode_halfop')) =~ s/\\e/\033/g;
+	($prefix_mode[$MODE_VOICE] = Irssi::settings_get_str('nicklist_prefix_mode_voice')) =~ s/\\e/\033/g;
+	($prefix_mode[$MODE_NORMAL] = Irssi::settings_get_str('nicklist_prefix_mode_normal')) =~ s/\\e/\033/g;
+	
+	if ($mode != $SCREEN) {
+		$height = Irssi::settings_get_int('nicklist_height');
+	}
+	my $new_nicklist_width = Irssi::settings_get_int('nicklist_width');
+	if ($new_nicklist_width != $nicklist_width && $mode == $SCREEN) {
+		sig_terminal_resized();
+	}
+	$nicklist_width = $new_nicklist_width;
+}
+
+sub update {
+	read_settings();
+	make_nicklist();
+}
+
+##################
+##### OUTPUT #####
+##################
+
+### off ###
+
+sub cmd_off {
+	if ($mode == $SCREEN) {
+		screen_stop();
+	} elsif ($mode == $FIFO) {
+		fifo_stop();
+	}
+}
+
+### fifo ###
+
+sub cmd_fifo_start {
+	read_settings();
+	my $path = Irssi::settings_get_str('nicklist_fifo_path');
+	unless (-p $path) { # not a pipe
+	    if (-e _) { # but a something else
+	        die "$0: $path exists and is not a pipe, please remove it\n";
+	    } else {
+	        require POSIX;
+	        POSIX::mkfifo($path, 0666) or die "can\'t mkfifo $path: $!";
+		Irssi::print("Fifo created. Start reading it (\"cat $path\") and try again.");
+		return;
+	    }
+	}
+	if (!sysopen(FIFO, $path, O_WRONLY | O_NONBLOCK)) { # or die "can't write $path: $!";
+		Irssi::print("Couldn\'t write to the fifo ($!). Please start reading the fifo (\"cat $path\") and try again.");
+		return;
+	}
+	FIFO->autoflush(1);
+	print FIFO "\033[2J\033[1;1H"; # erase screen & jump to 0,0
+	$cursor_line = 0;
+	if ($mode == $SCREEN) {
+		screen_stop();
+	}
+	$mode = $FIFO;
+	make_nicklist();
+}
+
+sub fifo_stop {
+	close FIFO;
+	$mode = $OFF;
+	Irssi::print("Fifo closed.");
+}
+
+### screen ###
+
+sub cmd_screen_start {
+	if (!defined($ENV{'STY'})) {
+		Irssi::print 'screen not detected, screen mode only works inside screen';
+		return;
+	}
+	read_settings();
+	if ($mode == $SCREEN) {return;}
+	if ($mode == $FIFO) {
+		fifo_stop();
+	}
+	$mode = $SCREEN;
+	Irssi::signal_add_last('gui print text finished', \&sig_gui_print_text_finished);
+	Irssi::signal_add_last('gui page scrolled', \&sig_page_scrolled);
+	Irssi::signal_add('terminal resized', \&sig_terminal_resized);
+	screen_size();
+	make_nicklist();
+}
+
+sub screen_stop {
+	$mode = $OFF;
+	Irssi::signal_remove('gui print text finished', \&sig_gui_print_text_finished);
+	Irssi::signal_remove('gui page scrolled', \&sig_page_scrolled);
+	Irssi::signal_remove('terminal resized', \&sig_terminal_resized);
+	system 'screen -x '.$ENV{'STY'}.' -X fit';
+}
+
+sub screen_size {
+	if ($mode != $SCREEN) {
+		return;
+	}
+	$screen_resizing = 1;
+	# fit screen
+	system 'screen -x '.$ENV{'STY'}.' -X fit';
+	# get size (from perldoc -q size)
+	my ($winsize, $row, $col, $xpixel, $ypixel);
+	eval 'use Term::ReadKey; ($col, $row, $xpixel, $ypixel) = GetTerminalSize';
+	#	require Term::ReadKey 'GetTerminalSize';
+	#	($col, $row, $xpixel, $ypixel) = Term::ReadKey::GetTerminalSize;
+	#};
+	if ($@) { # no Term::ReadKey, try the ugly way
+		eval {
+			require 'sys/ioctl.ph';
+			# without this reloading doesn't work. workaround for some unknown bug
+			do 'asm/ioctls.ph';
+		};
+		
+		# ugly way not working, let's try something uglier, the dg-hack(tm) (constant for linux only?)
+		if($@) { no strict 'refs'; *TIOCGWINSZ = sub { return 0x5413 } }
+		
+		unless (defined &TIOCGWINSZ) {
+			die "Term::ReadKey not found, and ioctl 'workaround' failed. Install the Term::ReadKey perl module to use screen mode.\n";
+		}
+		open(TTY, "+</dev/tty") or die "No tty: $!";
+		unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) {
+			die "Term::ReadKey not found, and ioctl 'workaround' failed ($!). Install the Term::ReadKey perl module to use screen mode.\n";
+		}
+		close(TTY);
+		($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
+	}
+	
+	# set screen width
+	$irssi_width = $col-$nicklist_width-1;
+	$height = $row-1;
+	
+	# on some recent systems, "screen -X fit; screen -X width -w 50" doesn't work, needs a sleep in between the 2 commands
+	# so we wait a second before setting the width
+	Irssi::timeout_add_once(1000, sub {
+		my ($new_irssi_width) = @_;
+		system 'screen -x '.$ENV{'STY'}.' -X width -w ' . $new_irssi_width;
+		# and then we wait another second for the resizing, and then redraw.
+		Irssi::timeout_add_once(1000,sub {$screen_resizing = 0; redraw()}, []);
+	}, $irssi_width);
+}
+
+sub sig_terminal_resized {
+	if ($screen_resizing) {
+		return;
+	}
+	$screen_resizing = 1;
+	Irssi::timeout_add_once(1000,\&screen_size,[]);
+}
+
+
+### both ###
+
+sub nicklist_write_start {
+	if ($mode == $SCREEN) {
+		print STDERR "\033P\033[s\033\\"; # save cursor
+	}
+}
+
+sub nicklist_write_end {
+	if ($mode == $SCREEN) {
+		print STDERR "\033P\033[u\033\\"; # restore cursor
+	}
+}
+
+sub nicklist_write_line {
+	my ($line, $data) = @_;
+	if ($mode == $SCREEN) {
+		print STDERR "\033P\033[" . ($line+1) . ';'. ($irssi_width+1) .'H'. $screen_prefix . $data . "\033\\";
+	} elsif ($mode == $FIFO) {
+		$data = "\033[m$data"; # reset color
+		if ($line == $cursor_line+1) {
+			$data = "\n$data"; # next line
+		} elsif ($line == $cursor_line) {
+			$data = "\033[1G".$data; # back to beginning of line
+		} else {
+			$data = "\033[".($line+1).";0H".$data; # jump
+		}
+		$cursor_line=$line;
+		print(FIFO $data) or fifo_stop();
+	}
+}
+
+# recalc the text of the nicklist item
+sub calc_text {
+	my ($nick) = @_;
+	my $tmp = $nicklist_width-3;
+	(my $text = $nick->{'nick'}) =~ s/^(.{$tmp})..+$/$1\033[34m~\033[m/;
+	$nick->{'text'} = $prefix_mode[$nick->{'mode'}] . $text . (' ' x ($nicklist_width-length($nick->{'nick'})-1));
+	$nick->{'cmp'} = $nick->{'mode'}.lc($nick->{'nick'});
+}
+
+# redraw the given nick (nr) if it is visible
+sub redraw_nick_nr {
+	my ($nr) = @_;
+	my $line = $nr - $scroll_pos;
+	if ($line >= 0 && $line < $height) {
+		nicklist_write_line($line, $nicklist[$nr]->{'text'});
+	}
+}
+
+# nick was inserted, redraw area if necessary
+sub draw_insert_nick_nr {
+	my ($nr) = @_;
+	my $line = $nr - $scroll_pos;
+	if ($line < 0) { # nick is inserted above visible area
+		$scroll_pos++; # 'scroll' down :)
+	} elsif ($line < $height) { # line is visible
+		if ($mode == $SCREEN) {
+			need_redraw();
+		} elsif ($mode == $FIFO) {
+			my $data = "\033[m\033[L". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick
+			if ($line == $cursor_line) {
+				$data = "\033[1G".$data; # back to beginning of line
+			} else {
+				$data = "\033[".($line+1).";1H".$data; # jump
+			}
+			$cursor_line=$line;
+			print(FIFO $data) or fifo_stop();
+			if ($prev_lines < $height) {
+				$prev_lines++; # the nicklist has one line more
+			}
+		}
+	}
+}
+
+sub draw_remove_nick_nr {
+	my ($nr) = @_;
+	my $line = $nr - $scroll_pos;
+	if ($line < 0) { # nick removed above visible area
+		$scroll_pos--; # 'scroll' up :)
+	} elsif ($line < $height) { # line is visible
+		if ($mode == $SCREEN) {
+			need_redraw();
+		} elsif ($mode == $FIFO) {
+			#my $data = "\033[m\033[L[i$line]". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick
+			my $data = "\033[M"; # delete line
+			if ($line != $cursor_line) {
+				$data = "\033[".($line+1)."d".$data; # jump
+			}
+			$cursor_line=$line;
+			print(FIFO $data) or fifo_stop();
+			if (@nicklist-$scroll_pos >= $height) {
+				redraw_nick_nr($scroll_pos+$height-1);
+			}
+		}
+	}
+}
+
+# redraw the whole nicklist
+sub redraw {
+	$need_redraw = 0;
+	#make_nicklist();
+	nicklist_write_start();
+	my $line = 0;
+	### draw nicklist ###
+	for (my $i=$scroll_pos;$line < $height && $i < @nicklist; $i++) {
+		nicklist_write_line($line++, $nicklist[$i]->{'text'});
+	}
+
+	### clean up other lines ###
+	my $real_lines = $line;
+	while($line < $prev_lines) {
+		nicklist_write_line($line++,' ' x $nicklist_width);
+	}
+	$prev_lines = $real_lines;
+	nicklist_write_end();
+}
+
+# redraw (with little delay to avoid redrawing to much)
+sub need_redraw {
+	if(!$need_redraw) {
+		$need_redraw = 1;
+		Irssi::timeout_add_once(10,\&redraw,[]);
+	}
+}
+
+sub sig_page_scrolled {
+	$prev_lines = $height; # we'll need to redraw everything if he scrolled up
+	need_redraw;
+}
+
+# redraw (with delay) if the window is visible (only in screen mode)
+sub sig_gui_print_text_finished {
+	if ($need_redraw) { # there's already a redraw 'queued'
+		return;
+	}
+	my $window = @_[0];
+	if ($window->{'refnum'} == Irssi::active_win->{'refnum'} || Irssi::settings_get_str('nicklist_screen_split_windows') eq '*') {
+		need_redraw;
+		return;
+	}
+	foreach my $win (split(/[ ,]/, Irssi::settings_get_str('nicklist_screen_split_windows'))) {
+		if ($window->{'refnum'} == $win || $window->{'name'} eq $win) {
+			need_redraw;
+			return;
+		}
+	}
+}
+
+####################
+##### NICKLIST #####
+####################
+
+# returns the position of the given nick(as string) in the (internal) nicklist
+sub find_nick {
+	my ($nick) = @_;
+	for (my $i=0;$i < @nicklist; $i++) {
+		if ($nicklist[$i]->{'nick'} eq $nick) {
+			return $i;
+		}
+	}
+	return -1;
+}
+
+# find position where nick should be inserted into the list
+sub find_insert_pos {
+	my ($cmp)= @_;
+	for (my $i=0;$i < @nicklist; $i++) {
+		if ($nicklist[$i]->{'cmp'} gt $cmp) {
+			return $i;
+		}
+	}
+	return scalar(@nicklist); #last
+}
+
+# make the (internal) nicklist (@nicklist)
+sub make_nicklist {
+	@nicklist = ();
+	$scroll_pos = 0;
+
+	### get & check channel ###
+	my $channel = Irssi::active_win->{active};
+
+	if (!$channel || (ref($channel) ne 'Irssi::Irc::Channel' && ref($channel) ne 'Irssi::Silc::Channel') || $channel->{'type'} ne 'CHANNEL' || ($channel->{chat_type} ne 'SILC' && !$channel->{'names_got'}) ) {
+		$active_channel = undef;
+		# no nicklist
+	} else {
+		$active_channel = $channel;
+		### make nicklist ###
+		my $thisnick;
+		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()) {
+			$thisnick = {'nick' => $nick->{'nick'}, 'mode' => ($nick->{'op'}?$MODE_OP:$nick->{'halfop'}?$MODE_HALFOP:$nick->{'voice'}?$MODE_VOICE:$MODE_NORMAL)};
+			calc_text($thisnick);
+			push @nicklist, $thisnick;
+		}
+	}
+	need_redraw();
+}
+
+# insert nick(as hash) into nicklist
+# pre: cmp has to be calculated
+sub insert_nick {
+	my ($nick) = @_;
+	my $nr = find_insert_pos($nick->{'cmp'});
+	splice @nicklist, $nr, 0, $nick;
+	draw_insert_nick_nr($nr);
+}
+
+# remove nick(as nr) from nicklist
+sub remove_nick {
+	my ($nr) = @_;
+	splice @nicklist, $nr, 1;
+	draw_remove_nick_nr($nr);
+}
+
+###################
+##### ACTIONS #####
+###################
+
+# scroll the nicklist, arg = number of lines to scroll, positive = down, negative = up
+sub cmd_scroll {
+	if (!$active_channel) { # not a channel active
+		return;
+	}
+	my @nicks=Irssi::active_win->{active}->nicks;
+	my $nick_count = scalar(@nicks)+0;
+	my $channel = Irssi::active_win->{active};
+	if (!$channel || $channel->{type} ne 'CHANNEL' || !$channel->{names_got} || $nick_count <= Irssi::settings_get_int('nicklist_height')) {
+		return;
+	}
+	$scroll_pos += @_[0];
+
+	if ($scroll_pos > $nick_count - $height) {
+		$scroll_pos = $nick_count - $height;
+	}
+	if ($scroll_pos <= 0) {
+		$scroll_pos = 0;
+	}
+	need_redraw();
+}
+
+sub is_active_channel {
+	my ($server,$channel) = @_; # (channel as string)
+	return ($server && $server->{'tag'} eq $active_channel->{'server'}->{'tag'} && $server->channel_find($channel) && $active_channel && $server->channel_find($channel)->{'name'} eq $active_channel->{'name'});
+}
+
+sub sig_channel_wholist { # this is actualy a little late, when the names are received would be better
+	my ($channel) = @_;
+	if (Irssi::active_win->{'active'} && Irssi::active_win->{'active'}->{'name'} eq $channel->{'name'}) { # the channel joined is active
+		make_nicklist
+	}
+}
+
+sub sig_join {
+	my ($server,$channel,$nick,$address) = @_;
+	if (!is_active_channel($server,$channel)) {
+		return;
+	}
+	my $newnick = {'nick' => $nick, 'mode' => $MODE_NORMAL};
+	calc_text($newnick);
+	insert_nick($newnick);
+}
+
+sub sig_kick {
+	my ($server, $channel, $nick, $kicker, $address, $reason) = @_;
+	if (!is_active_channel($server,$channel)) {
+		return;
+	}
+	my $nr = find_nick($nick);
+	if ($nr == -1) {
+		Irssi::print("nicklist warning: $nick was kicked from $channel, but not found in nicklist");
+	} else {
+		remove_nick($nr);
+	}
+}
+
+sub sig_part {
+	my ($server,$channel,$nick,$address, $reason) = @_;
+	if (!is_active_channel($server,$channel)) {
+		return;
+	}
+	my $nr = find_nick($nick);
+	if ($nr == -1) {
+		Irssi::print("nicklist warning: $nick has parted $channel, but was not found in nicklist");
+	} else {
+		remove_nick($nr);
+	}
+
+}
+
+sub sig_quit {
+	my ($server,$nick,$address, $reason) = @_;
+	if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) {
+		return;
+	}
+	my $nr = find_nick($nick);
+	if ($nr != -1) {
+		remove_nick($nr);
+	}
+}
+
+sub sig_nick {
+	my ($server, $newnick, $oldnick, $address) = @_;
+	if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) {
+		return;
+	}
+	my $nr = find_nick($oldnick);
+	if ($nr != -1) { # if nick was found (nickchange is in current channel)
+		my $nick = $nicklist[$nr];
+		remove_nick($nr);
+		$nick->{'nick'} = $newnick;
+		calc_text($nick);
+		insert_nick($nick);
+	}
+}
+
+sub sig_mode {
+	my ($channel, $nick, $setby, $mode, $type) = @_; # (nick and channel as rec)
+	if ($channel->{'server'}->{'tag'} ne $active_channel->{'server'}->{'tag'} || $channel->{'name'} ne $active_channel->{'name'}) {
+		return;
+	}
+	my $nr = find_nick($nick->{'nick'});
+	if ($nr == -1) {
+		Irssi::print("nicklist warning: $nick->{'nick'} had mode set on $channel->{'name'}, but was not found in nicklist");
+	} else {
+		my $nicklist_item = $nicklist[$nr];
+		remove_nick($nr);
+		$nicklist_item->{'mode'} = ($nick->{'op'}?$MODE_OP:$nick->{'halfop'}?$MODE_HALFOP:$nick->{'voice'}?$MODE_VOICE:$MODE_NORMAL);
+		calc_text($nicklist_item);
+		insert_nick($nicklist_item);
+	}
+}
+
+##### command binds #####
+Irssi::command_bind 'nicklist' => sub {
+    my ( $data, $server, $item ) = @_;
+    $data =~ s/\s+$//g;
+    Irssi::command_runsub ('nicklist', $data, $server, $item ) ;
+};
+Irssi::signal_add_first 'default command nicklist' => sub {
+	# gets triggered if called with unknown subcommand
+	cmd_help();
+};
+Irssi::command_bind('nicklist update',\&update);
+Irssi::command_bind('nicklist help',\&cmd_help);
+Irssi::command_bind('nicklist scroll',\&cmd_scroll);
+Irssi::command_bind('nicklist fifo',\&cmd_fifo_start);
+Irssi::command_bind('nicklist screen',\&cmd_screen_start);
+Irssi::command_bind('nicklist screensize',\&screen_size);
+Irssi::command_bind('nicklist off',\&cmd_off);
+
+##### signals #####
+Irssi::signal_add_last('window item changed', \&make_nicklist);
+Irssi::signal_add_last('window changed', \&make_nicklist);
+Irssi::signal_add_last('channel wholist', \&sig_channel_wholist);
+Irssi::signal_add_first('message join', \&sig_join); # first, to be before ignores
+Irssi::signal_add_first('message part', \&sig_part);
+Irssi::signal_add_first('message kick', \&sig_kick);
+Irssi::signal_add_first('message quit', \&sig_quit);
+Irssi::signal_add_first('message nick', \&sig_nick);
+Irssi::signal_add_first('message own_nick', \&sig_nick);
+Irssi::signal_add_first('nick mode changed', \&sig_mode);
+
+Irssi::signal_add('setup changed', \&read_settings);
+
+##### settings #####
+Irssi::settings_add_str('nicklist', 'nicklist_screen_prefix', '\e[m ');
+Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_op', '\e[32m@\e[39m');
+Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_halfop', '\e[34m%\e[39m');
+Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_voice', '\e[33m+\e[39m');
+Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_normal', ' ');
+
+Irssi::settings_add_int('nicklist', 'nicklist_width',11);
+Irssi::settings_add_int('nicklist', 'nicklist_height',24);
+Irssi::settings_add_str('nicklist', 'nicklist_fifo_path', Irssi::get_irssi_dir . '/nicklistfifo');
+Irssi::settings_add_str('nicklist', 'nicklist_screen_split_windows', '');
+Irssi::settings_add_str('nicklist', 'nicklist_automode', '');
+
+read_settings();
+if (uc(Irssi::settings_get_str('nicklist_automode')) eq 'SCREEN') {
+	cmd_screen_start();
+} elsif (uc(Irssi::settings_get_str('nicklist_automode')) eq 'FIFO') {
+	cmd_fifo_start();
+}

+ 669 - 0
irssi/scripts/autorun/wlstat.pl

@@ -0,0 +1,669 @@
+use strict; # use warnings;
+
+# FIXME COULD SOMEONE PLEASE TELL ME HOW TO SHUT UP
+#
+# ...
+# Variable "*" will not stay shared at (eval *) line *.
+# Variable "*" will not stay shared at (eval *) line *.
+# ...
+# Can't locate package Irssi::Nick for @Irssi::Irc::Nick::ISA at (eval *) line *.
+# ...
+#
+# THANKS
+
+use Irssi (); # which is the minimum required version of Irssi ?
+use Irssi::TextUI;
+
+use vars qw($VERSION %IRSSI);
+
+$VERSION = '0.5';
+%IRSSI = (
+    authors     => 'BC-bd, Veli, Timo \'cras\' Sirainen, Wouter Coekaerts, Nei',
+    contact     => 'bd@bc-bd.org, veli@piipiip.net, tss@iki.fi, wouter@coekaerts.be, Nei@QuakeNet',
+    name        => 'wlstat',
+    description => 'Adds a window list in the status area. Based on chanact.pl by above authors.',
+    license     => 'GNU GPLv2 or later',
+);
+
+# adapted by Nei
+
+###############
+# original comment
+# ###########
+# # 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   /window_alias code
+# # qrczak@knm.org.pl  chanact_abbreviate_names
+# # qerub@home.se      Extra chanact_show_mode and chanact_chop_status
+# #
+#
+# FURTHER THANKS TO
+# ############
+# # buu, fxn, Somni, Khisanth, integral, tybalt89   for much support in any aspect perl
+# # and the channel in general ( #perl @ freenode ) and especially the ir_* functions
+# #
+# # Valentin 'senneth' Batz ( vb@g-23.org ) for the pointer to grep.pl, continuous support
+# #                                         and help in digging up ir_strip_codes
+# #
+# # OnetrixNET technology networks for the debian environment
+# #
+# # Monkey-Pirate.com / Spaceman Spiff for the webspace
+# #
+#
+
+######
+# M A I N    P R O B L E M
+#####
+#
+# It is impossible to place the wlstat on a statusbar together with other items, because I
+# do not know how to calculate the size that it is going to get granted, and therefore I
+# cannot do the linebreaks properly.
+# This is what is missing to make a nice script out of wlstat.
+# If you have any ideas, please contact me ASAP :).
+#
+######
+
+######
+# UTF-8 PROBLEM
+#####
+#
+# Please help me find a solution to this:
+# this be your statusbar, it is using up the maximum term size
+# [[1=1]#abc [2=2]#defghi]
+# 
+# now consider this example: "ascii" characters are marked with ., utf-8 characters with *
+# [[1=1]#... [2=2]#...***]
+#
+# you should think that this is how it would be displayed? WRONG!
+# [[1=1]#... [2=2]#...***   ]
+#
+# this is what Irssi does.. I believe my length calculating code to be correct, however, I'd
+# love to be proven wrong (or receive any other fix, too, of course!)
+#
+######
+
+#########
+# USAGE
+###
+#
+# copy the script to ~/.irssi/scripts/
+#
+# In irssi:
+#
+#		/script load wlstat
+#
+#
+# Hint: to get rid of the old [Act:] display
+#     /statusbar window remove act
+#
+# to get it back:
+#     /statusbar window add -after lag -priority 10 act
+#
+##########
+# OPTIONS
+########
+#
+# /set wlstat_display_nokey <string>
+# /set wlstat_display_key <string>
+#		* string : Format String for one window. The following $'s are expanded:
+#		    $C : Name
+#		    $N : Number of the Window
+#		    $Q : meta-Keymap
+#		    $H : Start highlighting
+#		    $S : Stop highlighting
+#     IMPORTANT: don't forget to use $S if you used $H before!
+#
+# /set wlstat_separator <string>
+#     * string : Charater to use between the channel entries
+#     you'll need to escape " " space and "$" like this:
+#     "/set wlstat_separator \ "
+#     "/set wlstat_separator \$"
+#     and {}% like this:
+#     "/set wlstat_separator %{"
+#     "/set wlstat_separator %}"
+#     "/set wlstat_separator %%"
+#     (reason being, that the separator is used inside a {format })
+#
+# /set wlstat_hide_data <num>
+#     * num : hide the window if its data_level is below num
+#     set it to 0 to basically disable this feature,
+#               1 if you don't want windows without activity to be shown
+#               2 to show only those windows with channel text or hilight
+#               3 to show only windows with hilight
+#
+# /set wlstat_maxlines <num>
+#     * num : number of lines to use for the window list (0 to disable)
+#
+# /set wlstat_sort <-data_level|-last_line|refnum>
+#     * you can change the window sort order with this variable
+#         -data_level : sort windows with hilight first
+#         -last_line  : sort windows in order of activity
+#         refnum      : sort windows by window number
+#
+# /set wlstat_placement <top|bottom>
+# /set wlstat_position <num>
+#     * these settings correspond to /statusbar because wlstat will create
+#       statusbars for you
+#     (see /help statusbar to learn more)
+#
+# /set wlstat_all_disable <ON|OFF>
+#     * if you set wlstat_all_disable to ON, wlstat will also remove the
+#       last statusbar it created if it is empty.
+#       As you might guess, this only makes sense with wlstat_hide_data > 0 ;)
+#
+###
+# WISHES
+####
+#
+# if you fiddle with my mess, provide me with your fixes so I can benefit as well
+#
+# Nei =^.^= ( QuakeNet accountname: ailin )
+#
+
+my $actString = [];   # statusbar texts
+my $currentLines = 0;
+my $resetNeeded;      # layout/screen has changed, redo everything
+my $needRemake;       # "normal" changes
+#my $callcount = 0;
+my $globTime = undef; # timer to limit remake() calls
+
+my %statusbars;       # currently active statusbars
+
+# maybe I should just tie the array ?
+sub add_statusbar {
+	for (@_) {
+		# add subs
+		for my $l ($_) { eval {
+			no strict 'refs'; # :P
+			*{"wlstat$l"} = sub { wlstat($l, @_) };
+		}; }
+		Irssi::command("statusbar wl$_ reset");
+		Irssi::command("statusbar wl$_ enable");
+		if (lc Irssi::settings_get_str('wlstat_placement') eq 'top') {
+			Irssi::command("statusbar wl$_ placement top");
+		}
+		if ((my $x = int Irssi::settings_get_int('wlstat_position')) != 0) {
+			Irssi::command("statusbar wl$_ position $x");
+		}
+		Irssi::command("statusbar wl$_ add -priority 100 -alignment left barstar");
+		Irssi::command("statusbar wl$_ add wlstat$_");
+		Irssi::command("statusbar wl$_ add -priority 100 -alignment right barend");
+		Irssi::command("statusbar wl$_ disable");
+		Irssi::statusbar_item_register("wlstat$_", '$0', "wlstat$_");
+		$statusbars{$_} = {};
+	}
+}
+
+sub remove_statusbar {
+	for (@_) {
+		Irssi::command("statusbar wl$_ reset");
+		Irssi::statusbar_item_unregister("wlstat$_"); # XXX does this actually work ?
+		# DO NOT REMOVE the sub before you have unregistered it :))
+		for my $l ($_) { eval {
+			no strict 'refs';
+			undef &{"wlstat$l"};
+		}; }
+		delete $statusbars{$_};
+	}
+}
+
+sub syncLines {
+	my $temp = $currentLines;
+	$currentLines = @$actString;
+	#Irssi::print("current lines: $temp new lines: $currentLines");
+	my $currMaxLines = Irssi::settings_get_int('wlstat_maxlines');
+	if ($currMaxLines > 0 and @$actString > $currMaxLines) {
+		$currentLines = $currMaxLines;
+	}
+	return if ($temp == $currentLines);
+	if ($currentLines > $temp) {
+		for ($temp .. ($currentLines - 1)) {
+			add_statusbar($_);
+			Irssi::command("statusbar wl$_ enable");
+		}
+	}
+	else {
+		for ($_ = ($temp - 1); $_ >= $currentLines; $_--) {
+			Irssi::command("statusbar wl$_ disable");
+			remove_statusbar($_);
+		}
+	}
+}
+
+my %keymap;
+
+sub get_keymap {
+	my ($textDest, undef, $cont_stripped) = @_;
+	if ($textDest->{'level'} == 524288 and $textDest->{'target'} eq '' and !defined($textDest->{'server'})) {
+		if ($cont_stripped =~ m/meta-(.)\s+change_window (\d+)/) { $keymap{$2} = "$1"; }
+		Irssi::signal_stop();
+	}
+}
+
+sub update_keymap {
+	%keymap = ();
+	Irssi::signal_remove('command bind' => 'watch_keymap');
+	Irssi::signal_add_first('print text' => 'get_keymap');
+	Irssi::command('bind'); # stolen from grep
+	Irssi::signal_remove('print text' => 'get_keymap');
+	Irssi::signal_add('command bind' => 'watch_keymap');
+	Irssi::timeout_add_once(100, 'eventChanged', undef);
+}
+
+# watch keymap changes
+sub watch_keymap {
+	Irssi::timeout_add_once(1000, 'update_keymap', undef);
+}
+
+update_keymap();
+
+sub expand {
+	my ($string, %format) = @_;
+	my ($exp, $repl);
+	$string =~ s/\$$exp/$repl/g while (($exp, $repl) = each(%format));
+	return $string;
+}
+
+# FIXME implement $get_size_only check, and user $item->{min|max-size} ??
+sub wlstat {
+	my ($line, $item, $get_size_only) = @_;
+
+	if ($needRemake) {
+		$needRemake = undef;
+		remake();
+	}
+
+	my $text = $actString->[$line];  # DO NOT set the actual $actString->[$line] to '' here or
+	$text = '' unless defined $text; # you'll screw up the statusbar counter ($currentLines)
+	$item->default_handler($get_size_only, $text, '', 1);
+}
+
+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_8:|FnN>#[')),
+	#      escape
+	(map { $_ => $_ } (split //, '{}%')),
+);
+sub ir_strip_codes { # strip %codes
+	my $o = shift;
+	$o =~ s/(%(.))/exists $strip_table{$2} ? $strip_table{$2} : $1/gex;
+	$o
+}
+
+sub ir_parse_special {
+	my $o; my $i = shift;
+	my $win = Irssi::active_win();
+	my $server = Irssi::active_server();
+	if (ref $win and ref $win->{'active'}) {
+		$o = $win->{'active'}->parse_special($i);
+	}
+	elsif (ref $win and ref $win->{'active_server'}) {
+		$o = $win->{'active_server'}->parse_special($i);
+	}
+	elsif (ref $server) {
+		$o =  $server->parse_special($i);
+	}
+	else {
+		$o = Irssi::parse_special($i);
+	}
+	$o
+}
+
+sub sb_expand { # expand {format }s (and apply parse_special for $vars)
+	ir_parse_special(
+		Irssi::current_theme->format_expand(
+			shift,
+			(
+				Irssi::EXPAND_FLAG_IGNORE_REPLACES
+					|
+				Irssi::EXPAND_FLAG_IGNORE_EMPTY
+			)
+		)
+	)
+}
+sub sb_strip {
+	ir_strip_codes(
+		sb_expand(shift)
+	); # does this get us the actual length of that s*ty bar :P ?
+}
+sub sb_length {
+	# unicode cludge, d*mn broken Irssi
+	# screw it, this will fail from broken joining anyway (and cause warnings)
+	if (lc Irssi::settings_get_str('term_charset') eq 'utf-8') {
+		my $temp = sb_strip(shift);
+		# try to switch on utf8
+		eval {
+			no warnings;
+			require Encode;
+			#$temp = Encode::decode_utf8($temp); # thanks for the hint, but I have my reasons for _utf8_on
+			Encode::_utf8_on($temp);
+		};
+		length($temp)
+	}
+	else {
+		length(sb_strip(shift))
+	}
+}
+
+# !!! G*DD*MN Irssi is adding an additional layer of backslashitis per { } layer
+# !!! AND I still don't know what I need to escape.
+# !!! and NOONE else seems to know or care either.
+# !!! f*ck open source. I mean it.
+# XXX any Irssi::print debug statement leads to SEGFAULT - why ?
+
+# major parts of the idea by buu (#perl @ freenode)
+# thanks to fxn and Somni for debugging
+#	while ($_[0] =~ /(.)/g) {
+#		my $c = $1; # XXX sooo... goto kills $1
+#		if ($q eq '%') { goto ESC; }
+
+## <freenode:#perl:tybalt89> s/%(.)|(\{)|(\})|(\\|\$)/$1?$1:$2?($level++,$2):$3?($level>$min_level&&$level--,$3):'\\'x(2**$level-1).$4/ge;  # untested...
+sub ir_escape {
+	my $min_level = $_[1] || 0; my $level = $min_level;
+	my $o = shift;
+	$o =~ s/
+		(	%.	)	| # $1
+		(	\{	)	| # $2
+		(	\}	)	| # $3
+		(	\\	)	| # $4
+		(	\$(?=.)	)	| # $5
+		(	\$	) # $6
+	/
+		if ($1) { $1 } # %. escape
+		elsif ($2) { $level++; $2 } # { nesting start
+		elsif ($3) { if ($level > $min_level) { $level--; } $3 } # } nesting end
+		elsif ($4) { '\\'x(2**$level) } # \ needs \\escaping
+		elsif ($5) { '\\'x(2**$level-1) . '$' . '\\'x(2**$level-1) } # and $ needs even more because of "parse_special"
+		else { '\\'x(2**$level-1) . '$' } # $ needs \$ escaping
+	/gex;
+	$o
+}
+#sub ir_escape {
+#	my $min_level = $_[1] || 0; my $level = $min_level;
+#	my $o = shift;
+#	$o =~ s/
+#		(	%.	)	| # $1
+#		(	\{	)	| # $2
+#		(	\}	)	| # $3
+#		(	\\	|	\$	)	# $4
+#	/
+#		if ($1) { $1 } # %. escape
+#		elsif ($2) { $level++; $2 } # { nesting start
+#		elsif ($3) { if ($level > $min_level) { $level--; } $3 } # } nesting end
+#		else { '\\'x(2**($level-1)-1) . $4 } # \ or $ needs \\escaping
+#	/gex;
+#	$o
+#}
+
+sub ir_fe { # try to fix format stuff
+	my $x = shift;
+	# XXX why do I have to use two/four % here instead of one/two ?? answer: you screwed up in ir_escape
+	$x =~ s/([%{}])/%$1/g;
+	$x =~ s/(\\|\$)/\\$1/g;
+	#$x =~ s/(\$(?=.))|(\$)/$1?"\\\$\\":"\\\$"/ge; # I think this should be here (logic), but it doesn't work that way :P
+	#$x =~ s/\\/\\\\/g; # that's right, escape escapes
+	$x
+}
+
+sub remake () {
+	#$callcount++;
+	#my $xx = $callcount; Irssi::print("starting remake [ $xx ]");
+	my ($hilight, $number, $display);
+	my $separator = '{sb_act_sep ' . Irssi::settings_get_str('wlstat_separator') . '}';
+	my $custSort = Irssi::settings_get_str('wlstat_sort');
+	my $custSortDir = 1;
+	if ($custSort =~ /^[-!](.*)/) {
+		$custSortDir = -1;
+		$custSort = $1;
+	}
+
+	$actString = [];
+	my ($line, $width) = (0, [Irssi::windows]->[0]{'width'} - sb_length('{sb x}'));
+	foreach my $win (
+		sort {
+			(
+				( (int($a->{$custSort}) <=> int($b->{$custSort})) * $custSortDir )
+					||
+				($a->{'refnum'} <=> $b->{'refnum'})
+			)
+		} Irssi::windows
+	) {
+		$actString->[$line] = '' unless defined $actString->[$line] or Irssi::settings_get_bool('wlstat_all_disable');
+
+		# all stolen from chanact, what does this code do and why do we need it ?
+		!ref($win) && next;
+
+		my $name = $win->get_active_name;
+		my $active = $win->{'active'};
+		my $colour = $win->{'hilight_color'};
+		if (!defined $colour) { $colour = ''; }
+
+		if ($win->{'data_level'} < Irssi::settings_get_int('wlstat_hide_data')) { next; } # for Geert
+		if    ($win->{'data_level'} == 0) { $hilight = '{sb_act_none '; }
+		elsif ($win->{'data_level'} == 1) { $hilight = '{sb_act_text '; }
+		elsif ($win->{'data_level'} == 2) { $hilight = '{sb_act_msg '; }
+		elsif ($colour             ne '') { $hilight = "{sb_act_hilight_color $colour "; }
+		elsif ($win->{'data_level'} == 3) { $hilight = '{sb_act_hilight '; }
+		else                              { $hilight = '{sb_act_special '; }
+
+		$number = $win->{'refnum'};
+		$display = (defined $keymap{$number} and $keymap{$number} ne '')
+				?
+			(
+				Irssi::settings_get_str('wlstat_display_key')
+					||
+				Irssi::settings_get_str('wlstat_display_nokey')
+			)
+				:
+			Irssi::settings_get_str('wlstat_display_nokey')
+		;
+
+		my $add = expand($display,
+			C => ir_fe($name),
+			N => $number,
+			Q => ir_fe($keymap{$number}),
+			H => $hilight,
+			S => '}{sb_background}'
+		);
+		#$temp =~ s/\{\S+?(?:\s(.*?))?\}/$1/g;
+		#$temp =~ s/\\\\\\\\/\\/g; # XXX I'm actually guessing here, someone point me to docs please
+		$actString->[$line] = '' unless defined $actString->[$line];
+
+		# XXX how can I check whether the content still fits in the bar? this would allow
+		# XXX wlstatus to reside on a statusbar together with other items...
+		if (sb_length(ir_escape($actString->[$line] . $add)) >= $width) { # XXX doesn't correctly handle utf-8 multibyte ... help !!?
+			$actString->[$line] .= ' ' x ($width - sb_length(ir_escape($actString->[$line])));
+			$line++;
+		}
+		$actString->[$line] .= $add . $separator;
+		# XXX if I use these prints, output layout gets screwed up... why ?
+		#Irssi::print("line $line: ".$actString->[$line]);
+		#Irssi::print("temp $line: ".$temp);
+	}
+
+	# XXX the Irssi::print statements lead to the MOST WEIRD results
+	# e.g.: the loop gets executed TWICE for p > 0 ?!?
+	for (my $p = 0; $p < @$actString; $p++) { # wrap each line in {sb }, escape it properly, etc.
+		my $x = $actString->[$p];
+		$x =~ s/\Q$separator\E([ ]*)$/$1/;
+		#Irssi::print("[$p]".'current:'.join'.',split//,sb_strip(ir_escape($x,0)));
+		#Irssi::print("assumed length before:".sb_length(ir_escape($x,0)));
+		$x = "{sb $x}";
+		#Irssi::print("[$p]".'new:'.join'.',split//,sb_expand(ir_escape($x,0)));
+		#Irssi::print("[$p]".'new:'.join'.',split//,ir_escape($x,0));
+		#Irssi::print("assumed length after:".sb_length(ir_escape($x,0)));
+		$x = ir_escape($x);
+		#Irssi::print("[$p]".'REALnew:'.join'.',split//,sb_strip($x));
+		$actString->[$p] = $x;
+		# XXX any Irssi::print debug statement leads to SEGFAULT (sometimes) - why ?
+	}
+	#Irssi::print("remake [ $xx ] finished");
+}
+
+sub wlstatHasChanged () {
+	$globTime = undef;
+	my $temp = Irssi::settings_get_str('wlstat_placement').Irssi::settings_get_int('wlstat_position');
+	if ($temp ne $resetNeeded) { wlreset(); return; }
+	#Irssi::print("wlstat has changed, calls to remake so far: $callcount");
+	$needRemake = 1;
+
+	#remake();
+	if (
+		($needRemake and Irssi::settings_get_bool('wlstat_all_disable'))
+			or
+		(!Irssi::settings_get_bool('wlstat_all_disable') and $currentLines < 1)
+	) {
+		$needRemake = undef;
+		remake();
+	}
+	# XXX Irssi crashes if I try to do this without timer, why ? What's the minimum delay I need to use in the timer ?
+	Irssi::timeout_add_once(100, 'syncLines', undef);
+
+	for (keys %statusbars) {
+		Irssi::statusbar_items_redraw("wlstat$_");
+	}
+}
+
+sub eventChanged () { # Implement a change queue/blocker -.-)
+	if (defined $globTime) {
+		Irssi::timeout_remove($globTime);
+	} # delay the update further
+	$globTime = Irssi::timeout_add_once(10, 'wlstatHasChanged', undef);
+}
+
+#$needRemake = 1;
+sub resizeTerm () {
+	Irssi::timeout_add_once(100, 'eventChanged', undef);
+}
+
+Irssi::settings_add_str('wlstat', 'wlstat_display_nokey', '[$N]$H$C$S');
+Irssi::settings_add_str('wlstat', 'wlstat_display_key', '[$Q=$N]$H$C$S');
+Irssi::settings_add_str('wlstat', 'wlstat_separator', "\\ ");
+Irssi::settings_add_int('wlstat', 'wlstat_hide_data', 0);
+Irssi::settings_add_int('wlstat', 'wlstat_maxlines', 9);
+Irssi::settings_add_str('wlstat', 'wlstat_sort', 'refnum');
+Irssi::settings_add_str('wlstat', 'wlstat_placement', 'bottom');
+Irssi::settings_add_int('wlstat', 'wlstat_position', 0);
+Irssi::settings_add_bool('wlstat', 'wlstat_all_disable', 0);
+
+# remove old statusbars
+my %killBar;
+sub get_old_status {
+	my ($textDest, $cont, $cont_stripped) = @_;
+	if ($textDest->{'level'} == 524288 and $textDest->{'target'} eq '' and !defined($textDest->{'server'})) {
+		if ($cont_stripped =~ m/^wl(\d+)\s/) { $killBar{$1} = {}; }
+		Irssi::signal_stop();
+	}
+}
+sub killOldStatus {
+	%killBar = ();
+	Irssi::signal_add_first('print text' => 'get_old_status');
+	Irssi::command('statusbar');
+	Irssi::signal_remove('print text' => 'get_old_status');
+	remove_statusbar(keys %killBar);
+}
+#killOldStatus();
+
+sub wlreset {
+	$actString = [];
+	$currentLines = 0; # 1; # mhmmmm .. we actually enable one line down there so let's try this.
+	$resetNeeded = Irssi::settings_get_str('wlstat_placement').Irssi::settings_get_int('wlstat_position');
+	#update_keymap();
+	killOldStatus();
+	# Register statusbar
+	#add_statusbar(0);
+	#Irssi::command('statusbar wl0 enable');
+	resizeTerm();
+}
+
+wlreset();
+
+my $Unload;
+sub unload ($$$) {
+	$Unload = 1;
+	Irssi::timeout_add_once(10, sub { $Unload = undef; }, undef); # pretend we didn't do anything ASAP
+}
+Irssi::signal_add_first('gui exit' => sub { $Unload = undef; }); # last try to catch a sigsegv
+sub UNLOAD {
+	if ($Unload) { # this might well crash Irssi... try /eval /script unload someotherscript ; /quit (= SEGFAULT !)
+		$actString = ['']; # syncLines(); # XXX Irssi crashes when trying to disable all statusbars ?
+		killOldStatus();
+	}
+}
+
+sub addPrintTextHook { # update on print text
+	return if $_[0]->{'level'} == 262144 and $_[0]->{'target'} eq '' and !defined($_[0]->{'server'});
+	if (Irssi::settings_get_str('wlstat_sort') =~ /^[-!]?last_line$/) {
+		Irssi::timeout_add_once(100, 'eventChanged', undef);
+	}
+}
+
+#sub _x { my ($x, $y) = @_; ($x, sub { Irssi::print('-->signal '.$x); eval "$y();"; }) }
+#sub _x { @_ }
+Irssi::signal_add_first(
+	'command script unload' => 'unload'
+);
+Irssi::signal_add_last({
+	'setup changed' => 'eventChanged',
+	'print text' => 'addPrintTextHook',
+	'terminal resized' => 'resizeTerm',
+	'setup reread' => 'wlreset',
+	'window hilight' => 'eventChanged',
+});
+Irssi::signal_add({
+	'window created' => 'eventChanged',
+	'window destroyed' => 'eventChanged',
+	'window name changed' => 'eventChanged',
+	'window refnum changed' => 'eventChanged',
+	'window changed' => 'eventChanged',
+	'window changed automatic' => 'eventChanged',
+});
+
+#Irssi::signal_add('nick mode changed', 'chanactHasChanged'); # relicts
+
+###############
+###
+#
+# Changelog
+#
+# 0.5a
+# - add setting to also hide the last statusbar if empty (wlstat_all_disable)
+# - reverted to old utf8 code to also calculate broken utf8 length correctly
+# - simplified dealing with statusbars in wlreset
+# 
+# 0.4d
+# - fixed order of disabling statusbars
+# - several attempts at special chars, without any real success
+#   and much more weird new bugs caused by this
+# - setting to specify sort order
+# - reduced timeout values
+# - added wlstat_hide_data for Geert Hauwaerts ( geert@irssi.org ) :)
+# - make it so the dynamic sub is actually deleted
+# - fix a bug with removing of the last separator
+# - take into consideration parse_special
+# 
+# 0.3b
+# - automatically kill old statusbars
+# - reset on /reload
+# - position/placement settings
+#
+# 0.2
+# - automated retrieval of key bindings (thanks grep.pl authors)
+# - improved removing of statusbars
+# - got rid of status chop
+#
+# 0.1
+# - rewritten to suit my needs
+# - based on chanact 0.5.5
+