1# This script adds a nicklist to the right of irssi 2# for documentation: see http://wouter.coekaerts.be/site/irssi/nicklist 3 4# Copyright (C) 2002-2007 Wouter Coekaerts <coekie@irssi.org> 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 19 20use Irssi; 21use strict; 22use IO::Handle; # for (auto)flush 23use Fcntl; # for sysopen 24use vars qw($VERSION %IRSSI); 25$VERSION = '0.4.12'; 26%IRSSI = ( 27 authors => 'Wouter Coekaerts', 28 contact => 'coekie@irssi.org', 29 name => 'nicklist', 30 description => 'draws a nicklist to another terminal, or at the right of your irssi in the same terminal', 31 license => 'GPLv2', 32 url => 'http://wouter.coekaerts.be/irssi', 33 changed => '2018-10-02' 34); 35 36sub cmd_help { 37 print ( <<EOF 38Commands: 39NICKLIST HELP 40NICKLIST SCROLL <nr of lines> 41NICKLIST SCREEN 42NICKLIST FIFO 43NICKLIST ON 44NICKLIST OFF 45NICKLIST TOGGLE 46NICKLIST UPDATE 47 48For help see: http://wouter.coekaerts.be/site/irssi/nicklist 49 50in short: 51 521. FIFO MODE 53- in irssi: /NICKLIST FIFO (only the first time, to create the fifo) 54- in a shell, in a window where you want the nicklist: cat ~/.irssi/nicklistfifo 55- back in irssi: 56 /SET nicklist_heigth <height of nicklist> 57 /SET nicklist_width <width of nicklist> 58 /NICKLIST FIFO 59 602. SCREEN MODE 61- start irssi inside screen ("screen irssi") 62- /NICKLIST SCREEN 63EOF 64 ); 65} 66 67my $prev_lines = 0; # number of lines in previous written nicklist 68my $scroll_pos = 0; # scrolling position 69my $cursor_line; # line the cursor is currently on 70my ($OFF, $SCREEN, $FIFO) = (0,1,2); # modes 71my $mode = $OFF; # current mode 72my $need_redraw = 0; # nicklist needs redrawing 73my $screen_resizing = 0; # terminal is being resized 74my $active_channel; # (REC) 75 76my @nicklist=(); # array of hashes, containing the internal nicklist of the active channel 77 # nick => realnick 78 # modeflag => '@', '%', '+', or other mode char 79 # modepos => number representing the position in which to sort nicks with that mode 80 # status => (not used yet...) 81 # text => text to be printed 82 # cmp => text used to compare (sort) nicks 83 84# 'cached' settings 85my ($screen_prefix, $irssi_width, %prefix_mode, @prefix_status, $height, $nicklist_width, $check_friends, @prefix_friends); 86 87sub read_settings { 88 ($screen_prefix = Irssi::settings_get_str('nicklist_screen_prefix')) =~ s/\\e/\033/g; 89 90 ($prefix_mode{'@'} = Irssi::settings_get_str('nicklist_prefix_mode_op')) =~ s/\\e/\033/g; 91 ($prefix_mode{'%'} = Irssi::settings_get_str('nicklist_prefix_mode_halfop')) =~ s/\\e/\033/g; 92 ($prefix_mode{'+'} = Irssi::settings_get_str('nicklist_prefix_mode_voice')) =~ s/\\e/\033/g; 93 ($prefix_mode{' '} = Irssi::settings_get_str('nicklist_prefix_mode_normal')) =~ s/\\e/\033/g; 94 95 (my $prefix_mode_other = Irssi::settings_get_str('nicklist_prefix_mode_other')) =~ s/\\e/\033/g; 96 foreach my $p (split (/ /, $prefix_mode_other)) { 97 next if $p eq ''; 98 if ($p !~ /(.)=(.*)/) { 99 Irssi::print("Could not parse nicklist_prefix_mode_other part '$p'. Expected space separated list of <mode character>=<prefix>"); 100 last; 101 } else { 102 $prefix_mode{$1} = $2; 103 } 104 } 105 106 (my $prefix_friends = Irssi::settings_get_str('nicklist_prefix_friends')) =~ s/\\e/\033/g; 107 foreach my $p (split (/ /, $prefix_friends)) { 108 next if $p eq ''; 109 if ($p !~ /(.+?)=(.*)/) { 110 Irssi::print("Could not parse nicklist_prefix_friends part '$p'. Expected space separated list of <flags>=<prefix>"); 111 last; 112 } else { 113 push @prefix_friends, {'flags' => $1, 'prefix' => $2}; 114 } 115 } 116 117 $check_friends = ($prefix_friends ne ''); 118 119 if ($mode != $SCREEN) { 120 $height = Irssi::settings_get_int('nicklist_height'); 121 } 122 my $new_nicklist_width = Irssi::settings_get_int('nicklist_width'); 123 if ($new_nicklist_width != $nicklist_width && $mode == $SCREEN) { 124 sig_terminal_resized(); 125 } 126 $nicklist_width = $new_nicklist_width; 127} 128 129sub update { 130 read_settings(); 131 make_nicklist(); 132} 133 134################## 135##### OUTPUT ##### 136################## 137 138### on ### 139 140sub cmd_on { 141 if (uc(Irssi::settings_get_str('nicklist_automode')) eq 'SCREEN') { 142 cmd_screen_start(); 143 } elsif (uc(Irssi::settings_get_str('nicklist_automode')) eq 'FIFO') { 144 cmd_fifo_start(); 145 } 146} 147 148### off ### 149 150sub cmd_off { 151 if ($mode == $SCREEN) { 152 screen_stop(); 153 } elsif ($mode == $FIFO) { 154 fifo_stop(); 155 } 156} 157 158### toggle ### 159 160sub cmd_toggle { 161 if ($mode == $OFF) { 162 cmd_on(); 163 } else { 164 cmd_off(); 165 } 166} 167 168### fifo ### 169 170sub cmd_fifo_start { 171 read_settings(); 172 my $path = Irssi::settings_get_str('nicklist_fifo_path'); 173 unless (-p $path) { # not a pipe 174 if (-e _) { # but a something else 175 die "$0: $path exists and is not a pipe, please remove it\n"; 176 } else { 177 require POSIX; 178 POSIX::mkfifo($path, 0666) or die "can\'t mkfifo $path: $!"; 179 Irssi::print("Fifo created. Start reading it (\"cat $path\") and try again."); 180 return; 181 } 182 } 183 if (!sysopen(FIFO, $path, O_WRONLY | O_NONBLOCK)) { # or die "can't write $path: $!"; 184 Irssi::print("Couldn\'t write to the fifo ($!). Please start reading the fifo (\"cat $path\") and try again."); 185 return; 186 } 187 FIFO->autoflush(1); 188 print FIFO "\033[2J\033[1;1H"; # erase screen & jump to 0,0 189 $cursor_line = 0; 190 if ($mode == $SCREEN) { 191 screen_stop(); 192 } 193 $mode = $FIFO; 194 make_nicklist(); 195} 196 197sub fifo_stop { 198 close FIFO; 199 $mode = $OFF; 200 Irssi::print("Fifo closed."); 201} 202 203### screen ### 204 205sub cmd_screen_start { 206 if (!defined($ENV{'STY'})) { 207 Irssi::print 'screen not detected, screen mode only works inside screen'; 208 return; 209 } 210 read_settings(); 211 if ($mode == $SCREEN) {return;} 212 if ($mode == $FIFO) { 213 fifo_stop(); 214 } 215 $mode = $SCREEN; 216 Irssi::signal_add_last('gui print text finished', \&sig_gui_print_text_finished); 217 Irssi::signal_add_last('gui page scrolled', \&sig_page_scrolled); 218 Irssi::signal_add('terminal resized', \&sig_terminal_resized); 219 screen_size(); 220 make_nicklist(); 221} 222 223sub screen_stop { 224 $mode = $OFF; 225 Irssi::signal_remove('gui print text finished', \&sig_gui_print_text_finished); 226 Irssi::signal_remove('gui page scrolled', \&sig_page_scrolled); 227 Irssi::signal_remove('terminal resized', \&sig_terminal_resized); 228 system 'screen -x '.$ENV{'STY'}.' -X fit'; 229 # we wait a second to make sure the fit command was processed 230 Irssi::timeout_add_once(1000, \&screen_size, []); 231} 232 233#sub screen_size_real { 234sub screen_size { 235 if ($mode != $SCREEN) { 236 return; 237 } 238 $screen_resizing = 1; 239 # fit screen 240 system 'screen -x '.$ENV{'STY'}.' -X fit'; 241 # get size (from perldoc -q size) 242 my ($winsize, $row, $col, $xpixel, $ypixel); 243 eval 'use Term::ReadKey; ($col, $row, $xpixel, $ypixel) = GetTerminalSize'; 244 # require Term::ReadKey 'GetTerminalSize'; 245 # ($col, $row, $xpixel, $ypixel) = Term::ReadKey::GetTerminalSize; 246 #}; 247 if ($@) { # no Term::ReadKey, try the ugly way 248 eval { 249 require 'sys/ioctl.ph'; 250 # without this reloading doesn't work. workaround for some unknown bug 251 do 'asm/ioctls.ph'; 252 }; 253 254 # ugly way not working, let's try something uglier, the dg-hack(tm) (constant for linux only?) 255 if($@) { no strict 'refs'; *TIOCGWINSZ = sub { return 0x5413 } } 256 257 unless (defined &TIOCGWINSZ) { 258 die "Term::ReadKey not found, and ioctl 'workaround' failed. Install the Term::ReadKey perl module to use screen mode.\n"; 259 } 260 open(TTY, "+<","/dev/tty") or die "No tty: $!"; 261 unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) { 262 die "Term::ReadKey not found, and ioctl 'workaround' failed ($!). Install the Term::ReadKey perl module to use screen mode.\n"; 263 } 264 close(TTY); 265 ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize); 266 } 267 268 # set screen width 269 $irssi_width = $col-$nicklist_width-1; 270 $height = $row-1; 271 272 system 'screen -x '.$ENV{'STY'}.' -X width -w ' . $irssi_width; 273 # wait another second for the resizing, and then redraw. 274 Irssi::timeout_add_once(1000,sub {$screen_resizing = 0; redraw()}, []); 275} 276 277sub sig_terminal_resized { 278 if ($screen_resizing) { 279 return; 280 } 281 $screen_resizing = 1; 282 Irssi::timeout_add_once(1000,\&screen_size,[]); 283} 284 285 286### both ### 287 288sub nicklist_write_start { 289 if ($mode == $SCREEN) { 290 print STDERR "\033P\0337\033\\"; # save cursor 291 } 292} 293 294sub nicklist_write_end { 295 if ($mode == $SCREEN) { 296 print STDERR "\033P\0338\033\\"; # restore cursor 297 } 298} 299 300sub nicklist_write_line { 301 my ($line, $data) = @_; 302 if ($mode == $SCREEN) { 303 print STDERR "\033P\033[" . ($line+1) . ';'. ($irssi_width+1) .'H'. $screen_prefix . $data . "\033\\"; 304 } elsif ($mode == $FIFO) { 305 $data = "\033[m$data"; # reset color 306 if ($line == $cursor_line+1) { 307 $data = "\n$data"; # next line 308 } elsif ($line == $cursor_line) { 309 $data = "\033[1G".$data; # back to beginning of line 310 } else { 311 $data = "\033[".($line+1).";0H".$data; # jump 312 } 313 $cursor_line=$line; 314 print(FIFO $data) or fifo_stop(); 315 } 316} 317 318sub calc_prefix_friends { 319 my ($nick) = @_; 320 321 return '' unless $check_friends 322 && $nick->{'host'} 323 && is_friend($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'}); 324 325 my $flags = get_flags($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'}); 326 327 my $prefix; 328 foreach my $prefix_friend (@prefix_friends) { 329 if ($prefix_friend->{'flags'} eq 'noflag') { 330 if ($flags eq '') { 331 $prefix = $prefix_friend->{'prefix'}; 332 last; 333 } 334 } elsif (check_modes($flags, $prefix_friend->{'flags'})) { 335 $prefix = $prefix_friend->{'prefix'}; 336 } 337 } 338 339 return $prefix ? $prefix : ''; 340} 341 342# recalc the text of the nicklist item 343sub calc_text { 344 my ($nick) = @_; 345 my $tmp = $nicklist_width-3; 346 (my $text = $nick->{'nick'}) =~ s/^(.{$tmp})..+$/$1\033[34m~/; # strip nick if too long 347 348 my $prefix_mode = $prefix_mode{$nick->{'modeflag'}}; 349 if (! defined($prefix_mode) ) { 350 $prefix_mode = $nick->{'modeflag'}; 351 } 352 353 my $prefix_friends = calc_prefix_friends($nick); 354 355 $nick->{'text'} = 356 $prefix_mode . 357 $prefix_friends . 358 $text . 359 (' ' x ($nicklist_width-length($nick->{'nick'})-1)) . 360 "\033[m"; # reset 361 $nick->{'cmp'} = $nick->{'modepos'}.lc($nick->{'nick'}); 362} 363 364# redraw the given nick (nr) if it is visible 365sub redraw_nick_nr { 366 my ($nr) = @_; 367 my $line = $nr - $scroll_pos; 368 if ($line >= 0 && $line < $height) { 369 nicklist_write_line($line, $nicklist[$nr]->{'text'}); 370 } 371} 372 373# nick was inserted, redraw area if necessary 374sub draw_insert_nick_nr { 375 my ($nr) = @_; 376 my $line = $nr - $scroll_pos; 377 if ($line < 0) { # nick is inserted above visible area 378 $scroll_pos++; # 'scroll' down :) 379 } elsif ($line < $height) { # line is visible 380 if ($mode == $SCREEN) { 381 need_redraw(); 382 } elsif ($mode == $FIFO) { 383 my $data = "\033[m\033[L". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick 384 if ($line == $cursor_line) { 385 $data = "\033[1G".$data; # back to beginning of line 386 } else { 387 $data = "\033[".($line+1).";1H".$data; # jump 388 } 389 $cursor_line=$line; 390 print(FIFO $data) or fifo_stop(); 391 if ($prev_lines < $height) { 392 $prev_lines++; # the nicklist has one line more 393 } 394 } 395 } 396} 397 398sub draw_remove_nick_nr { 399 my ($nr) = @_; 400 my $line = $nr - $scroll_pos; 401 if ($line < 0) { # nick removed above visible area 402 $scroll_pos--; # 'scroll' up :) 403 } elsif ($line < $height) { # line is visible 404 if ($mode == $SCREEN) { 405 need_redraw(); 406 } elsif ($mode == $FIFO) { 407 #my $data = "\033[m\033[L[i$line]". $nicklist[$nr]->{'text'}; # reset color & insert line & write nick 408 my $data = "\033[M"; # delete line 409 if ($line != $cursor_line) { 410 $data = "\033[".($line+1)."d".$data; # jump 411 } 412 $cursor_line=$line; 413 print(FIFO $data) or fifo_stop(); 414 if (@nicklist-$scroll_pos >= $height) { 415 redraw_nick_nr($scroll_pos+$height-1); 416 } 417 } 418 } 419} 420 421# redraw the whole nicklist 422sub redraw { 423 $need_redraw = 0; 424 #make_nicklist(); 425 nicklist_write_start(); 426 my $line = 0; 427 ### draw nicklist ### 428 for (my $i=$scroll_pos;$line < $height && $i < @nicklist; $i++) { 429 nicklist_write_line($line++, $nicklist[$i]->{'text'}); 430 } 431 432 ### clean up other lines ### 433 my $real_lines = $line; 434 while($line < $prev_lines) { 435 nicklist_write_line($line++,' ' x $nicklist_width); 436 } 437 $prev_lines = $real_lines; 438 nicklist_write_end(); 439} 440 441# redraw (with little delay to avoid redrawing to much) 442sub need_redraw { 443 if(!$need_redraw) { 444 $need_redraw = 1; 445 Irssi::timeout_add_once(10,\&redraw,[]); 446 } 447} 448 449sub sig_page_scrolled { 450 $prev_lines = $height; # we'll need to redraw everything if he scrolled up 451 need_redraw; 452} 453 454# redraw (with delay) if the window is visible (only in screen mode) 455sub sig_gui_print_text_finished { 456 if ($need_redraw) { # there's already a redraw 'queued' 457 return; 458 } 459 my $window = @_[0]; 460 if ($window->{'refnum'} == Irssi::active_win->{'refnum'} || Irssi::settings_get_str('nicklist_screen_split_windows') eq '*') { 461 need_redraw; 462 return; 463 } 464 foreach my $win (split(/[ ,]/, Irssi::settings_get_str('nicklist_screen_split_windows'))) { 465 if ($window->{'refnum'} == $win || $window->{'name'} eq $win) { 466 need_redraw; 467 return; 468 } 469 } 470} 471 472################### 473##### FRIENDS ##### 474################### 475 476# checks if $has_modes is in $need_modes, copied from trigger.pl 477sub check_modes { 478 my ($has_modes, $need_modes) = @_; 479 my $matches; 480 my $switch = 1; # if a '-' if found, will be 0 (meaning the modes should not be set) 481 foreach my $need_mode (split /&/,$need_modes) { 482 $matches = 0; 483 foreach my $char (split //,$need_mode) { 484 if ($char eq '-') { 485 $switch = 0; 486 } elsif ($char eq '+') { 487 $switch = 1; 488 } elsif ((index($has_modes,$char) != -1) == $switch) { 489 $matches = 1; 490 last; 491 } 492 } 493 if (!$matches) { 494 return 0; 495 } 496 } 497 return 1; 498} 499 500# get someones flags from people.pl or friends(_shasta).pl, copied from trigger.pl 501sub get_flags { 502 my ($chatnet, $channel, $nick, $address) = @_; 503 my $flags; 504 no strict 'refs'; 505 if (%{ 'Irssi::Script::people::' }) { 506 if (defined ($channel)) { 507 $flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address)); 508 } else { 509 $flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address)); 510 } 511 $flags = join('',keys(%{$flags})); 512 } else { 513 my $shasta; 514 if (%{ 'Irssi::Script::friends_shasta::' }) { 515 $shasta = 'friends_shasta'; 516 } elsif (defined &{ 'Irssi::Script::friends::get_idx' }) { 517 $shasta = 'friends'; 518 } 519 if (!$shasta) { 520 return undef; 521 } 522 my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address)); 523 if ($idx == -1) { 524 return ''; 525 } 526 $flags = (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,undef)); 527 if ($channel) { 528 $flags .= (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,$channel)); 529 } 530 } 531 return $flags; 532} 533 534sub is_friend { 535 my ($chatnet, $channel, $nick, $address) = @_; 536 no strict 'refs'; 537 if (%{ 'Irssi::Script::people::' }) { 538 return (() != &{'Irssi::Script::people::find_users'}($chatnet, $nick, $address)); 539 my $flags; 540 if (defined ($channel)) { 541 $flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address)); 542 } else { 543 $flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address)); 544 } 545 return ($flags ne ''); # TODO: test this 546 } else { 547 my $shasta; 548 if (%{ 'Irssi::Script::friends_shasta::' }) { 549 $shasta = 'friends_shasta'; 550 } elsif (defined &{ 'Irssi::Script::friends::get_idx' }) { 551 $shasta = 'friends'; 552 } 553 if (!$shasta) { 554 return undef; 555 } 556 my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address)); 557 return ($idx != -1); 558 } 559} 560 561#################### 562##### NICKLIST ##### 563#################### 564 565# returns the position of the given nick(as string) in the (internal) nicklist 566sub find_nick { 567 my ($nick) = @_; 568 for (my $i=0;$i < @nicklist; $i++) { 569 if ($nicklist[$i]->{'nick'} eq $nick) { 570 return $i; 571 } 572 } 573 return -1; 574} 575 576# find position where nick should be inserted into the list 577sub find_insert_pos { 578 my ($cmp)= @_; 579 for (my $i=0;$i < @nicklist; $i++) { 580 if ($nicklist[$i]->{'cmp'} gt $cmp) { 581 return $i; 582 } 583 } 584 return scalar(@nicklist); #last 585} 586 587# make the (internal) nicklist (@nicklist) 588sub make_nicklist { 589 @nicklist = (); 590 $scroll_pos = 0; 591 592 ### get & check channel ### 593 my $channel = Irssi::active_win->{active}; 594 595 if (!$channel || (ref($channel) ne 'Irssi::Irc::Channel' && ref($channel) ne 'Irssi::Silc::Channel' && ref($channel) ne 'Irssi::Xmpp::Channel') || $channel->{'type'} ne 'CHANNEL' || ($channel->{chat_type} ne 'SILC' && !$channel->{'names_got'}) ) { 596 $active_channel = undef; 597 # no nicklist 598 } else { 599 $active_channel = $channel; 600 ### make nicklist ### 601 foreach my $nick ($channel->nicks()) { 602 my $thisnick = {'nick' => $nick->{'nick'}}; 603 recalc_nick($thisnick, $nick); 604 push @nicklist, $thisnick; 605 } 606 @nicklist = sort {$a->{'cmp'} cmp $b->{'cmp'}} @nicklist; 607 } 608 need_redraw(); 609} 610 611# insert nick(as hash) into nicklist 612# pre: cmp has to be calculated 613sub insert_nick { 614 my ($nick) = @_; 615 my $nr = find_insert_pos($nick->{'cmp'}); 616 splice @nicklist, $nr, 0, $nick; 617 draw_insert_nick_nr($nr); 618} 619 620# remove nick(as nr) from nicklist 621sub remove_nick { 622 my ($nr) = @_; 623 splice @nicklist, $nr, 1; 624 draw_remove_nick_nr($nr); 625} 626 627# update the mode and cmp of a nick, based on a nickrec from irssi 628sub recalc_nick { 629 my ($nick, $nickrec) = @_; 630 if (! $nickrec) { 631 $nickrec = $active_channel->nick_find($nick->{'nick'}); 632 } 633 634 my $nickflags = $active_channel->{'server'}->get_nick_flags() . ' '; 635 636 my $flag = ( 637 $nickrec->{'op'} ? '@' : 638 $nickrec->{'halfop'} ? '%' : 639 $nickrec->{'voice'} ? '+' : 640 ' ' 641 ); 642 643 if ($nickrec->{'other'} && index($nickflags, $nick->{'other'}) < index($nickflags, $flag)) { 644 $flag = chr($nickrec->{'other'}); 645 } 646 647 $nick->{'modepos'} = index($nickflags, $flag); 648 $nick->{'modeflag'} = $flag; 649 650 $nick->{'host'} = $nickrec->{'host'}; 651 calc_text($nick); 652} 653 654################### 655##### ACTIONS ##### 656################### 657 658# scroll the nicklist, arg = number of lines to scroll, positive = down, negative = up 659sub cmd_scroll { 660 my $channel = Irssi::active_win->{active}; 661 if (!$channel || !$channel->can('nicks')) { # active window is not a channel 662 return; 663 } 664 my @nicks = $channel->nicks; 665 my $nick_count = scalar(@nicks)+0; 666 667 if (!$active_channel) { # not a channel active 668 return; 669 } 670 if (!$channel || $channel->{type} ne 'CHANNEL' || !$channel->{names_got} || $nick_count <= $height) { 671 return; 672 } 673 if ($nick_count <= Irssi::settings_get_int('nicklist_height')) { 674 return; 675 } 676 $scroll_pos += @_[0]; 677 678 if ($scroll_pos > $nick_count - $height) { 679 $scroll_pos = $nick_count - $height; 680 } 681 if ($scroll_pos <= 0) { 682 $scroll_pos = 0; 683 } 684 need_redraw(); 685} 686 687sub is_active_channel { 688 my ($server,$channel) = @_; # (channel as string) 689 return ($server && $server->{'tag'} eq $active_channel->{'server'}->{'tag'} && $server->channel_find($channel) && $active_channel && $server->channel_find($channel)->{'name'} eq $active_channel->{'name'}); 690} 691 692sub sig_channel_wholist { # this is actualy a little late, when the names are received would be better 693 my ($channel) = @_; 694 if (Irssi::active_win->{'active'} && Irssi::active_win->{'active'}->{'name'} eq $channel->{'name'}) { # the channel joined is active 695 make_nicklist 696 } 697} 698 699sub sig_join { 700 my ($server,$channel,$nick,$address) = @_; 701 if (!is_active_channel($server,$channel)) { 702 return; 703 } 704 my $newnick = {'nick' => $nick}; 705 recalc_nick($newnick); 706 insert_nick($newnick); 707} 708 709sub sig_kick { 710 my ($server, $channel, $nick, $kicker, $address, $reason) = @_; 711 if (!is_active_channel($server,$channel)) { 712 return; 713 } 714 my $nr = find_nick($nick); 715 if ($nr == -1) { 716 Irssi::print("nicklist warning: $nick was kicked from $channel, but not found in nicklist"); 717 } else { 718 remove_nick($nr); 719 } 720} 721 722sub sig_part { 723 my ($server,$channel,$nick,$address, $reason) = @_; 724 if (!is_active_channel($server,$channel)) { 725 return; 726 } 727 my $nr = find_nick($nick); 728 if ($nr == -1) { 729 Irssi::print("nicklist warning: $nick has parted $channel, but was not found in nicklist"); 730 } else { 731 remove_nick($nr); 732 } 733 734} 735 736sub sig_quit { 737 my ($server,$nick,$address, $reason) = @_; 738 if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) { 739 return; 740 } 741 my $nr = find_nick($nick); 742 if ($nr != -1) { 743 remove_nick($nr); 744 } 745} 746 747sub sig_nick { 748 my ($server, $newnick, $oldnick, $address) = @_; 749 if ($server->{'tag'} ne $active_channel->{'server'}->{'tag'}) { 750 return; 751 } 752 my $nr = find_nick($oldnick); 753 if ($nr != -1) { # if nick was found (nickchange is in current channel) 754 my $nick = $nicklist[$nr]; 755 remove_nick($nr); 756 $nick->{'nick'} = $newnick; 757 calc_text($nick); 758 insert_nick($nick); 759 } 760} 761 762sub sig_mode { 763 my ($channel, $nick, $setby, $mode, $type) = @_; # (nick and channel as rec) 764 if ($channel->{'server'}->{'tag'} ne $active_channel->{'server'}->{'tag'} || $channel->{'name'} ne $active_channel->{'name'}) { 765 return; 766 } 767 my $nr = find_nick($nick->{'nick'}); 768 if ($nr == -1) { 769 Irssi::print("nicklist warning: $nick->{'nick'} had mode set on $channel->{'name'}, but was not found in nicklist"); 770 } else { 771 my $nicklist_item = $nicklist[$nr]; 772 remove_nick($nr); 773 recalc_nick($nicklist_item, $nick); 774 insert_nick($nicklist_item); 775 } 776} 777 778##### command binds ##### 779Irssi::command_bind 'nicklist' => sub { 780 my ( $data, $server, $item ) = @_; 781 $data =~ s/\s+$//g; 782 Irssi::command_runsub ('nicklist', $data, $server, $item ) ; 783}; 784Irssi::signal_add_first 'default command nicklist' => sub { 785 # gets triggered if called with unknown subcommand 786 cmd_help(); 787}; 788Irssi::command_bind('nicklist update',\&update); 789Irssi::command_bind('nicklist help',\&cmd_help); 790Irssi::command_bind('nicklist scroll',\&cmd_scroll); 791Irssi::command_bind('nicklist fifo',\&cmd_fifo_start); 792Irssi::command_bind('nicklist screen',\&cmd_screen_start); 793Irssi::command_bind('nicklist screensize',\&screen_size); 794Irssi::command_bind('nicklist on',\&cmd_on); 795Irssi::command_bind('nicklist off',\&cmd_off); 796Irssi::command_bind('nicklist toggle',\&cmd_toggle); 797 798##### signals ##### 799Irssi::signal_add_last('window item changed', \&make_nicklist); 800Irssi::signal_add_last('window changed', \&make_nicklist); 801Irssi::signal_add_last('channel wholist', \&sig_channel_wholist); 802Irssi::signal_add_first('message join', \&sig_join); # first, to be before ignores 803Irssi::signal_add_first('message part', \&sig_part); 804Irssi::signal_add_first('message kick', \&sig_kick); 805Irssi::signal_add_first('message quit', \&sig_quit); 806Irssi::signal_add_first('message nick', \&sig_nick); 807Irssi::signal_add_first('message own_nick', \&sig_nick); 808Irssi::signal_add_first('nick mode changed', \&sig_mode); 809 810Irssi::signal_add('setup changed', \&read_settings); 811 812##### settings ##### 813Irssi::settings_add_str('nicklist', 'nicklist_screen_prefix', '\e[m '); 814Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_op', '\e[32m@\e[39m'); 815Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_halfop', '\e[34m%\e[39m'); 816Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_voice', '\e[33m+\e[39m'); 817Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_normal', ' '); 818Irssi::settings_add_str('nicklist', 'nicklist_prefix_mode_other', '&=\e[31m&\e[39m ~=\e[35m~\e[39m'); 819Irssi::settings_add_str('nicklist', 'nicklist_prefix_friends', 'o=\e[32m v=\e[33m noflag=\e[1m'); 820 821Irssi::settings_add_int('nicklist', 'nicklist_width',11); 822Irssi::settings_add_int('nicklist', 'nicklist_height',24); 823Irssi::settings_add_str('nicklist', 'nicklist_fifo_path', Irssi::get_irssi_dir . '/nicklistfifo'); 824Irssi::settings_add_str('nicklist', 'nicklist_screen_split_windows', ''); 825Irssi::settings_add_str('nicklist', 'nicklist_automode', ''); 826 827read_settings(); 828cmd_on(); 829