html/chanact.pl
1 use Irssi 20020101.0001 ();
2 use strict;
3 # FIXME use warning;
4 use Irssi::TextUI;
5
6 use vars qw($VERSION %IRSSI);
7
8 $VERSION = "0.5.14";
9 %IRSSI = (
10 authors => 'BC-bd, Veli',
11 contact => 'bd@bc-bd.org, veli@piipiip.net',
12 name => 'chanact',
13 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>',
14 license => 'GNU GPLv2 or later',
15 url => 'https://bc-bd.org/svn/repos/irssi/chanact'
16 );
17
18 # Adds new powerful and customizable [Act: ...] item (chanelnames,modes,alias).
19 # Lets you give alias characters to windows so that you can select those with
20 # meta-<char>.
21 #
22 # for irssi 0.8.2 by bd@bc-bd.org
23 #
24 # inspired by chanlist.pl by 'cumol@hammerhart.de'
25 #
26 #########
27 # Contributors
28 #########
29 #
30 # veli@piipiip.net /window_alias code
31 # qrczak@knm.org.pl chanact_abbreviate_names
32 # qerub@home.se Extra chanact_show_mode and chanact_chop_status
33 # madduck@madduck.net Better channel aliasing (case-sensitive, cross-network)
34 # chanact_filter_windowlist basis
35 # Jan 'jast' Krueger <jast@heapsort.de>, 2004-06-22
36 # Ivo Timmermans <ivo@o2w.nl> win->{hilight} patch
37 #
38 #########
39 # USAGE
40 ###
41 #
42 # copy the script to ~/.irssi/scripts/
43 #
44 # In irssi:
45 #
46 # /script load chanact
47 # /statusbar window add -after act chanact
48 #
49 # If you want the item to appear on another position read the help
50 # for /statusbar.
51 # To remove the [Act: 1,2,3] item type:
52 #
53 # /statusbar window remove act
54 #
55 # To see all chanact options type:
56 #
57 # / set chanact_
58 #
59 # After these steps you have your new statusbar item and you can start giving
60 # aliases to your windows. Go to the window you want to give the alias to
61 # and say:
62 #
63 # /window_alias <alias char>
64 #
65 # You can remove the aliases with from an aliased window:
66 #
67 # /window_unalias
68 #
69 # To see a list of your windows use:
70 #
71 # /window list
72 #
73 # To make your bindings permanent you will need to save the config and layout
74 # before quiting irssi:
75 #
76 # /save
77 # /layout save
78 #
79 #########
80 # OPTIONS
81 #########
82 #
83 # /set chanact_chop_status <ON|OFF>
84 # * ON : shorten (Status) to S
85 # * OFF : don't do it
86 #
87 # /set chanact_sort <refnum|activity|level+refnum|level+activity>
88 # sort by ...
89 # refnum : refnum
90 # activity : last active window
91 # level+refnum : data_level of window, then refnum
92 # level+activity : data_level then activity
93 #
94 # /set chanact_display <string>
95 # * string : Format String for one Channel. The following $'s are expanded:
96 # $C : Channel
97 # $N : Number of the Window
98 # $M : Mode in that channel
99 # $H : Start highlightning
100 # $S : Stop highlightning
101 # * example:
102 #
103 # /set chanact_display $H$N:$M.$S$C
104 #
105 # will give you on #irssi.de if you have voice
106 #
107 # [3:+.#irssi.de]
108 #
109 # with '3:+.' highlighted and the channel name printed in regular color
110 #
111 # /set chanact_display_alias <string>
112 # as 'chanact_display' but is used if the window has an alias and
113 # 'chanact_show_alias' is set to on.
114 #
115 # /set chanact_show_names <ON|OFF>
116 # * ON : show the channelnames after the number/alias
117 # * OFF : don't show the names
118 #
119 # /set chanact_abbreviate_names <int>
120 # * 0 : don't abbreviate
121 # * <int> : strip channel name prefix character and leave only
122 # that many characters of the proper name
123 #
124 # /set chanact_show_alias <ON|OFF>
125 # * ON : show the aliase instead of the refnum
126 # * OFF : shot the refnum
127 #
128 # /set chanact_header <str>
129 # * <str> : Characters to be displayed at the start of the item.
130 # Defaults to: "Act: "
131 #
132 # /set chanact_separator <str>
133 # * <str> : Charater to use between the channel entries
134 #
135 # /set chanact_autorenumber <ON|OFF>
136 # * ON : Move the window automatically to first available slot
137 # starting from "chanact_renumber_start" when assigning
138 # an alias to window. Also moves the window back to a
139 # first available slot from refnum 1 when the window
140 # loses it's alias.
141 # * OFF : Don't move the windows automatically
142 #
143 # /set chanact_renumber_start <int>
144 # * <int> : Move the window to first available slot after this
145 # num when "chanact_autorenumber" is ON.
146 #
147 # /set chanact_remove_hash <ON|OFF>
148 # * ON : Remove &#+!= from channel names
149 # * OFF : Don't touch channel names
150 #
151 # /set chanact_remove_prefix <string>
152 # * <string> : Regular expression used to remove from the
153 # beginning of the channel name.
154 # * example :
155 # To shorten a lot of debian channels:
156 #
157 # /set chanact_remove_prefix deb(ian.(devel-)?)?
158 #
159 # /set chanact_filter <int>
160 # * 0 : show all channels
161 # * 1 : hide channels without activity
162 # * 2 : hide channels with only join/part/etc messages
163 # * 3 : hide channels with text messages
164 # * 4 : hide all channels (now why would you want to do that)
165 #
166 # /set chanact_filter_windowlist <string>
167 # * <string> : space-separated list of windows for which to use
168 # chanact_filter_windowlist_level instead of
169 # chanact_filter.
170 #
171 # Alternatively, an entry can be postfixed with
172 # a comma (',') and the level to use for that
173 # window.
174 #
175 # The special string @QUERIES matches all queries.
176 #
177 # /set chanact_filter_windowlist_level <int>
178 # Use this level to filter all windows listed in chanact_filter_windowlist.
179 # You can use these two settings to apply different filter levels to different
180 # windows. Defaults to 0.
181 #
182 #########
183 # HINTS
184 #########
185 #
186 # If you have trouble with wrong colored entries your 'default.theme' might
187 # be too old. Try on a shell:
188 #
189 # $ mv ~/.irssi/default.theme /tmp/
190 #
191 # And in irssi:
192 # /reload
193 # /save
194 #
195 ###
196 #################
197
198 my %show = (
199 0 => "{%n ", # NOTHING
200 1 => "{sb_act_text ", # TEXT
201 2 => "{sb_act_msg ", # MSG
202 3 => "{sb_act_hilight ", # HILIGHT
203 );
204
205 # comparison operators for our sort methods
206 my %sort = (
207 'refnum' => '$a->{refnum} <=> $b->{refnum};',
208 'activity' => '$b->{last_line} <=> $a->{last_line};',
209 'level+refnum' => '$b->{data_level} <=> $a->{data_level} ||
210 $a->{refnum} <=> $b->{refnum};',
211 'level+activity'=> '$b->{data_level} <=> $a->{data_level} ||
212 $b->{last_line} <=> $a->{last_line};',
213 );
214
215 my ($actString,$needRemake);
216
217 sub expand {
218 my ($string, %format) = @_;
219 my ($exp, $repl);
220 $string =~ s/\$$exp/$repl/g while (($exp, $repl) = each(%format));
221 return $string;
222 }
223
224 # method will get called every time the statusbar item will be displayed
225 # but we dont need to recreate the item every time so we first
226 # check if something has changed and only then we recreate the string
227 # this might just save some cycles
228 # FIXME implement $get_size_only check, and user $item->{min|max-size}
229 sub chanact {
230 my ($item, $get_size_only) = @_;
231
232 if ($needRemake) {
233 remake();
234 }
235
236 $item->default_handler($get_size_only, $actString, undef, 1);
237 }
238
239 # build a hash to easily access special levels based on
240 # chanact_filter_windowlist
241 sub calculate_windowlist() {
242 my @matchlist = split ' ', Irssi::settings_get_str('chanact_filter_windowlist');
243 my $default = Irssi::settings_get_int('chanact_filter_windowlist_level');
244
245 my %windowlist;
246 foreach my $m (@matchlist) {
247 my ($name, $level) = split(/,/, $m);
248 $windowlist{$name} = $level ? $level : $default;
249 }
250
251 return %windowlist;
252 }
253
254 # calculate level per window
255 sub calculate_levels(@) {
256 my (@windows) = @_;
257
258 my %matches = calculate_windowlist();
259 my $default = Irssi::settings_get_int('chanact_filter');
260
261 my %levels;
262
263 foreach my $win (@windows) {
264 # FIXME we could use the next statements to weed out entries in
265 # @windows that we will not need later on
266 !ref($win) && next;
267
268 my $name = $win->get_active_name;
269
270 if (exists($matches{$name})) {
271 $levels{$name} = $matches{$name};
272 } else {
273 $levels{$name} = $default;
274 }
275 }
276
277 if (exists($matches{'@QUERIES'})) {
278 $levels{'@QUERIES'} = $matches{'@QUERIES'};
279 } else {
280 $levels{'@QUERIES'} = $default;
281 }
282
283 return %levels;
284 }
285
286 # this is the real creation method
287 sub remake() {
288 my ($afternumber,$finish,$hilight,$mode,$number,$display,@windows);
289 my $separator = Irssi::settings_get_str('chanact_separator');
290 my $abbrev = Irssi::settings_get_int('chanact_abbreviate_names');
291 my $remove_prefix = Irssi::settings_get_str('chanact_remove_prefix');
292 my $remove_hash = Irssi::settings_get_bool('chanact_remove_hash');
293
294 my $method = $sort{Irssi::settings_get_str('chanact_sort')};
295 @windows = sort { eval $method } Irssi::windows();
296
297 my %levels = calculate_levels(@windows);
298
299 $actString = "";
300 foreach my $win (@windows) {
301 # since irssi is single threaded this shouldn't happen
302 !ref($win) && next;
303
304 my $active = $win->{active};
305
306 # define $type to emtpy string and overwrite if we do have an
307 # active item. we need this to display windows without active
308 # items e.g. '(status)'
309 my $type = "";
310 $type = $active->{type} if $active;
311
312 my $name = $win->get_active_name;
313
314 my $filter_level =
315 $type eq 'QUERY' ? $levels{'@QUERIES'} : $levels{$name};
316
317 # now, skip windows with data of level lower than the
318 # filter level
319 next if ($win->{data_level} < $filter_level);
320
321 # alright, the activity is important, let's show the window
322 # after a bit of additional processing.
323
324 # (status) is an awfull long name, so make it short to 'S'
325 # some people don't like it, so make it configurable
326 if (Irssi::settings_get_bool('chanact_chop_status')
327 && $name eq "(status)") {
328 $name = "S";
329 }
330
331 # check if we should show the mode
332 $mode = "";
333 if ($type eq "CHANNEL") {
334 my $server = $win->{active_server};
335 !ref($server) && next;
336
337 my $channel = $server->channel_find($name);
338 !ref($channel) && next;
339
340 my $nick = $channel->nick_find($server->{nick});
341 !ref($nick) && next;
342
343 if ($nick->{op}) {
344 $mode = "@";
345 } elsif ($nick->{voice}) {
346 $mode = "+";
347 } elsif ($nick->{halfop}) {
348 $mode = "%";
349 }
350 }
351
352 # in case we have a specific hilightcolor use it
353 if ($win->{hilight_color}) {
354 $hilight = "{sb_act_hilight_color $win->{hilight_color} ";
355 } else {
356 $hilight = $show{$win->{data_level}};
357 }
358
359 if ($remove_prefix) {
360 $name =~ s/^([&#+!=]?)$remove_prefix/$1/;
361 }
362 if ($abbrev) {
363 if ($name =~ /^[&#+!=]/) {
364 $name = substr($name, 1, $abbrev + 1);
365 } else {
366 $name = substr($name, 0, $abbrev);
367 }
368 }
369 if ($remove_hash) {
370 $name =~ s/^[&#+!=]//;
371 }
372
373 if (Irssi::settings_get_bool('chanact_show_alias') == 1 &&
374 $win->{name} =~ /^([a-zA-Z+]):(.+)$/) {
375 $number = "$1";
376 $display = Irssi::settings_get_str('chanact_display_alias');
377 } else {
378 $number = $win->{refnum};
379 $display = Irssi::settings_get_str('chanact_display');
380 }
381
382 # fixup { and } in nicks, those are used by irssi themes
383 $name =~ s/([{}])/%$1/g;
384
385 $actString .= expand($display,"C",$name,"N",$number,"M",$mode,"H",$hilight,"S","}{sb_background}").$separator;
386 }
387
388 # assemble the final string
389 if ($actString ne "") {
390 # Remove the last separator
391 $actString =~ s/$separator$//;
392
393 $actString = "{sb ".Irssi::settings_get_str('chanact_header').$actString."}";
394 }
395
396 # no remake needed any longer
397 $needRemake = 0;
398 }
399
400 # method called because of some events. here we dont remake the item but just
401 # remember that we have to remake it the next time we are called
402 sub chanactHasChanged()
403 {
404 # if needRemake is already set, no need to trigger a redraw as we will
405 # be redrawing the item anyway.
406 return if $needRemake;
407
408 $needRemake = 1;
409
410 Irssi::statusbar_items_redraw('chanact');
411 }
412
413 sub setup_changed {
414 my $method = Irssi::settings_get_str('chanact_sort');
415
416 unless (exists($sort{$method})) {
417 Irssi::print("chanact: invalid sort method, setting to 'refnum'."
418 ." valid methods are: ".join(", ", sort(keys(%sort))));
419 my $method = Irssi::settings_set_str('chanact_sort', 'refnum');
420 }
421
422 chanactHasChanged();
423 }
424
425 # Remove alias
426 sub cmd_window_unalias {
427 my ($data, $server, $witem) = @_;
428
429 if ($data ne '') {
430 Irssi::print("chanact: /window_unalias does not take any ".
431 "parameters, Run it in the window you want to unalias");
432 return;
433 }
434
435 my $win = Irssi::active_win();
436 my $name = Irssi::active_win()->{name};
437
438 # chanact'ified windows have a name like this: X:servertag/name
439 my ($key, $tag) = split(/:/, $name);
440 ($tag, $name) = split('/', $tag);
441
442 # remove alias only of we have a single character keybinding, if we
443 # haven't the name was not set by chanact, so we won't blindly unset
444 # stuff
445 if (length($key) == 1) {
446 $server->command("/bind -delete meta-$key");
447 } else {
448 Irssi::print("chanact: could not determine keybinding. ".
449 "Won't unbind anything");
450 }
451
452 # set the windowname back to it's old one. We don't bother checking
453 # for a vaild name here, as we want to remove the current one and if
454 # worse comes to wors set an empty one.
455 $win->set_name($name);
456
457 # if autorenumbering is off, we are done.
458 return unless (Irssi::settings_get_bool('chanact_autorenumber'));
459
460 # we are renumbering, so move the window to the lowest available
461 # refnum.
462 my $refnum = 1;
463 while (Irssi::window_find_refnum($refnum) ne "") {
464 $refnum++;
465 }
466
467 $win->set_refnum($refnum);
468 Irssi::print("chanact: moved wintow to refnum $refnum");
469 }
470
471 # function by veli@piipiip.net
472 # Make an alias
473 sub cmd_window_alias {
474 my ($data, $server, $witem) = @_;
475 my $rn_start = Irssi::settings_get_int('chanact_renumber_start');
476
477 unless ($data =~ /^[a-zA-Z+]$/) {
478 Irssi::print("Usage: /window_alias <char>");
479 return;
480 }
481
482 # in case of an itemless window $witem is undef, thus future operations
483 # on it fail. to prevent this we pull in the current window.
484 #
485 # Also we need to initialize $winname, else we would get a broken name:
486 #
487 # 'name' => 'S:IRCnet/S:IRCnet/',
488 #
489 my $window;
490 my $winname = "";
491 if (defined($witem)) {
492 $window = $witem->window();
493 $winname = $witem->{name};
494 } else {
495 $window = Irssi::active_win();
496 $winname = $window->{name};
497 }
498
499 cmd_window_unalias($data, $server, $witem);
500
501 my $winnum = $window->{refnum};
502
503 if (Irssi::settings_get_bool('chanact_autorenumber') == 1 &&
504 $window->{refnum} < $rn_start) {
505 my $old_refnum = $window->{refnum};
506
507 $winnum = $rn_start;
508
509 # Find the first available slot and move the window
510 while (Irssi::window_find_refnum($winnum) ne "") { $winnum++; }
511 $window->set_refnum($winnum);
512
513 Irssi::print("Moved the window from $old_refnum to $winnum");
514 }
515
516 my $winserver = $window->{active_server}->{tag};
517 my $winhandle = "$winserver/$winname";
518 # cmd_window_unalias relies on a certain format here
519 my $name = "$data:$winhandle";
520
521 $window->set_name($name);
522 $server->command("/bind meta-$data change_window $name");
523 Irssi::print("Window $winhandle is now accessible with meta-$data");
524 }
525
526 $needRemake = 1;
527
528 # Window alias command
529 Irssi::command_bind('window_alias','cmd_window_alias');
530 Irssi::command_bind('window_unalias','cmd_window_unalias');
531
532 # our config item
533 Irssi::settings_add_str('chanact', 'chanact_display', '$H$N:$M$C$S');
534 Irssi::settings_add_str('chanact', 'chanact_display_alias', '$H$N$M$S');
535 Irssi::settings_add_int('chanact', 'chanact_abbreviate_names', 0);
536 Irssi::settings_add_bool('chanact', 'chanact_show_alias', 1);
537 Irssi::settings_add_str('chanact', 'chanact_separator', " ");
538 Irssi::settings_add_bool('chanact', 'chanact_autorenumber', 0);
539 Irssi::settings_add_bool('chanact', 'chanact_remove_hash', 0);
540 Irssi::settings_add_str('chanact', 'chanact_remove_prefix', "");
541 Irssi::settings_add_int('chanact', 'chanact_renumber_start', 50);
542 Irssi::settings_add_str('chanact', 'chanact_header', "Act: ");
543 Irssi::settings_add_bool('chanact', 'chanact_chop_status', 1);
544 Irssi::settings_add_str('chanact', 'chanact_sort', 'refnum');
545 Irssi::settings_add_int('chanact', 'chanact_filter', 0);
546 Irssi::settings_add_str('chanact', 'chanact_filter_windowlist', "");
547 Irssi::settings_add_int('chanact', 'chanact_filter_windowlist_level', 0);
548
549 # register the statusbar item
550 Irssi::statusbar_item_register('chanact', '$0', 'chanact');
551 # according to cras we shall not call this
552 # Irssi::statusbars_recreate_items();
553
554 # register all that nifty callbacks on special events
555 Irssi::signal_add_last('setup changed', 'setup_changed');
556 Irssi::signal_add_last('window changed', 'chanactHasChanged');
557 Irssi::signal_add_last('window item changed', 'chanactHasChanged');
558 Irssi::signal_add_last('window hilight', 'chanactHasChanged');
559 Irssi::signal_add_last('window item hilight', 'chanactHasChanged');
560 Irssi::signal_add("window created", "chanactHasChanged");
561 Irssi::signal_add("window destroyed", "chanactHasChanged");
562 Irssi::signal_add("window name changed", "chanactHasChanged");
563 Irssi::signal_add("window activity", "chanactHasChanged");
564 Irssi::signal_add("print text", "chanactHasChanged");
565 Irssi::signal_add('nick mode changed', 'chanactHasChanged');
566
567 ###############
568 ###
569 #
570 # Changelog
571 #
572 # 0.5.14
573 # - fix itemless window handling, thx Bazerka
574 # - fix /window_alias for itemless windows
575 # - fix /window_unalias. Also longer takes an argument
576 # - added sorting by level, based on patch by Bazerka
577 # + retired chanact_sort_by_activity, integrated in chanact_sort
578 #
579 # 0.5.13
580 # - trivial cleanup in cmd_window_alias()
581 # - updated documentation regarding /layout save, thx Bazerka
582 # - removed cmd_rebuild_aliases(), no longer working since we use channel
583 # names to select windows and not refnums
584 # - removed refnum_changed(), see cmd_rebuild_aliases() above
585 #
586 # 0.5.12
587 # - Use comma instead of colon as windowlist separator, patch by martin f.
588 # krafft, reported by James Vega
589 #
590 # 0.5.11
591 # - added chanact_filter_windowlist based on a patch by madduck@madduck.net
592 # - fixed display error for nicks/channels with { or } in them
593 # - fixed chanact_header, was hidden behind chanact_filter
594 # - fixed documentation
595 # + removed chanact_show_mode, long gone
596 #
597 # 0.5.10
598 # - fixed irssi crash when using Irssi::print from within remake()
599 # - added option to filter out some data levels, based on a patch by
600 # Juergen Jung <juergen@Winterkaelte.de>, see
601 # https://bc-bd.org/trac/irssi/ticket/15
602 # + retired chanact_show_all in favour of chanact_filter
603 #
604 # 0.5.9
605 # - changes by stefan voelkel
606 # + sort channels by activity, see
607 # https://bc-bd.org/trac/irssi/ticket/5, based on a patch by jan
608 # krueger
609 # + fixed chrash on /exec -interactive, see
610 # https://bc-bd.org/trac/irssi/ticket/7
611 #
612 # - changes by Jan 'jast' Krueger <jast@heapsort.de>, 2004-06-22
613 # + updated documentation in script's comments
614 #
615 # - changes by Ivo Timmermans <ivo@o2w.nl>
616 # + honor actcolor /hilight setting if present
617 #
618 # 0.5.8
619 # - made aliases case-sensitive and include network in channel names by madduck
620 #
621 # 0.5.7
622 # - integrated remove patch by Christoph Berg <myon@debian.org>
623 #
624 # 0.5.6
625 # - fixed a bug (#1) reported by Wouter Coekaert
626 #
627 # 0.5.5
628 # - some speedups from David Leadbeater <dgl@dgl.cx>
629 #
630 #
631 # 0.5.4
632 # - added help for chanact_display_alias
633 #
634 # 0.5.3
635 # - added '+' to the available chars of aliase's
636 # - added chanact_display_alias to allow different display modes if the window
637 # has an alias
638 #
639 # 0.5.2
640 # - removed unused chanact_show_name settings (thx to Qerub)
641 # - fixed $mode display
642 # - guarded reference operations to (hopefully) fix errors on server disconnect
643 #
644 # 0.5.1
645 # - small typo fixed
646 #
647 # 0.5.0
648 # - changed chanact_show_mode to chanact_display. reversed changes from
649 # Qerub through that, but kept funcionality.
650 # - removed chanact_color_all since it is no longer needed
651 #
652 # 0.4.3
653 # - changes by Qerub
654 # + added chanact_show_mode to show the mode just before the channel name
655 # + added chanact_chop_status to be able to control the (status) chopping
656 # [bd] minor implementation changes
657 # - moved Changelog to the end of the file since it is getting pretty big
658 #
659 # 0.4.2
660 # - changed back to old version numbering sheme
661 # - added '=' to Qrczak's chanact_abbreviate_names stuff :)
662 # - added chanact_header
663 #
664 # 0.41q
665 # - changes by Qrczak
666 # + added setting 'chanact_abbreviate_names'
667 # + windows are sorted by refnum; I didn't understand the old
668 # logic and it broke sorting for numbers above 9
669 #
670 # 0.41
671 # - minor updates
672 # + fixed channel sort [veli]
673 # + removed few typos and added some documentation [veli]
674 #
675 # 0.4
676 # - merge with window_alias.pl
677 # + added /window_alias from window_alias.pl by veli@piipiip.net
678 # + added setting 'chanact_show_alias'
679 # + added setting 'chanact_show_names'
680 # + changed setting 'chanact_show_mode' to int
681 # + added setting 'chanact_separator' [veli]
682 # + added setting 'chanact_autorenumber' [veli]
683 # + added setting 'chanact_renumber_start' [veli]
684 # + added /window_unalias [veli]
685 # + moved setting to their own group 'chanact' [veli]
686 #
687 # 0.3
688 # - merge with chanlist.pl
689 # + added setting 'chanact_show_mode'
690 # + added setting 'chanact_show_all'
691 #
692 # 0.2
693 # - added 'Act' to the item
694 # - added setting 'chanact_color_all'
695 # - finally found format for statusbar hilight
696 #
697 # 0.1
698 # - Initial Release
699 #
700 ###
701 ################