1## trackbar.pl 2# 3# This little script will do just one thing: it will draw a line each time you 4# switch away from a window. This way, you always know just upto where you've 5# been reading that window :) It also removes the previous drawn line, so you 6# don't see double lines. 7# 8# redraw trackbar only works on irssi 0.8.17 or higher. 9# 10## 11 12## Usage: 13# 14# The script works right out of the box, but if you want you can change 15# the working by /set'ing the following variables: 16# 17# Setting: trackbar_style 18# Description: This setting will be the color of your trackbar line. 19# By default the value will be '%K', only Irssi color 20# formats are allowed. If you don't know the color formats 21# by heart, you can take a look at the formats documentation. 22# You will find the proper docs on http://www.irssi.org/docs. 23# 24# Setting: trackbar_string 25# Description: This is the string that your line will display. This can 26# be multiple characters or just one. For example: '~-~-' 27# The default setting is '-'. 28# Here are some unicode characters you can try: 29# "───" => U+2500 => a line 30# "═══" => U+2550 => a double line 31# "━━━" => U+2501 => a wide line 32# "▭ " => U+25ad => a white rectangle 33# 34# Setting: trackbar_use_status_window 35# Description: If this setting is set to OFF, Irssi won't print a trackbar 36# in the statuswindow 37# 38# Setting: trackbar_ignore_windows 39# Description: A list of windows where no trackbar should be printed 40# 41# Setting: trackbar_print_timestamp 42# Description: If this setting is set to ON, Irssi will print the formatted 43# timestamp in front of the trackbar. 44# 45# Setting: trackbar_require_seen 46# Description: Only clear the trackbar if it has been scrolled to. 47# 48# Setting: trackbar_all_manual 49# Description: Never clear the trackbar until you do /mark. 50# 51# /mark is a command that will redraw the line at the bottom. 52# 53# Command: /trackbar, /trackbar goto 54# Description: Jump to where the trackbar is, to pick up reading 55# 56# Command: /trackbar keep 57# Description: Keep this window's trackbar where it is the next time 58# you switch windows (then this flag is cleared again) 59# 60# Command: /mark, /trackbar mark 61# Description: Remove the old trackbar and mark the bottom of this 62# window with a new trackbar 63# 64# Command: /trackbar markvisible 65# Description: Like mark for all visible windows 66# 67# Command: /trackbar markall 68# Description: Like mark for all windows 69# 70# Command: /trackbar remove 71# Description: Remove this window's trackbar 72# 73# Command: /trackbar removeall 74# Description: Remove all windows' trackbars 75# 76# Command: /trackbar redraw 77# Description: Force redraw of trackbars 78# 79## 80 81## 82# 83# For bugreports and other improvements contact one of the authors. 84# 85# This program is free software; you can redistribute it and/or modify 86# it under the terms of the GNU General Public License as published by 87# the Free Software Foundation; either version 2 of the License, or 88# (at your option) any later version. 89# 90# This program is distributed in the hope that it will be useful, 91# but WITHOUT ANY WARRANTY; without even the implied warranty of 92# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 93# GNU General Public License for more details. 94# 95# You should have received a copy of the GNU General Public License 96# along with this script; if not, write to the Free Software 97# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 98# 99## 100 101use strict; 102use warnings; 103use vars qw($VERSION %IRSSI); 104 105$VERSION = "2.6"; # c99d17e529ad9ce 106 107%IRSSI = ( 108 authors => "Peter 'kinlo' Leurs, Uwe Dudenhoeffer, " . 109 "Michiel Holtkamp, Nico R. Wohlgemuth, " . 110 "Geert Hauwaerts", 111 contact => 'peter@pfoe.be', 112 patchers => 'Johan Kiviniemi (UTF-8), Uwe Dudenhoeffer (on-upgrade-remove-line)', 113 name => 'trackbar', 114 description => 'Shows a bar where you have last read a window.', 115 license => 'GNU General Public License', 116 url => 'http://www.pfoe.be/~peter/trackbar/', 117 commands => 'trackbar', 118); 119 120## Comments and remarks. 121# 122# This script uses settings. 123# Use /SET to change the value or /TOGGLE to switch it on or off. 124# 125# 126# Tip: The command 'trackbar' is very usefull if you bind that to a key, 127# so you can easily jump to the trackbar. Please see 'help bind' for 128# more information about keybindings in Irssi. 129# 130# Command: /BIND meta2-P key F1 131# /BIND F1 command trackbar 132# 133## 134 135## Bugfixes and new items in this rewrite. 136# 137# * Remove all the trackbars before upgrading. 138# * New setting trackbar_use_status_window to control the statuswindow trackbar. 139# * New setting trackbar_print_timestamp to print a timestamp or not. 140# * New command 'trackbar' to scroll up to the trackbar. 141# * When resizing your terminal, Irssi will update all the trackbars to the new size. 142# * When changing trackbar settings, change all the trackbars to the new settings. 143# * New command 'trackbar mark' to draw a new trackbar (The old '/mark'). 144# * New command 'trackbar markall' to draw a new trackbar in each window. 145# * New command 'trackbar remove' to remove the trackbar from the current window. 146# * New command 'trackbar removeall' to remove all the trackbars. 147# * Don't draw a trackbar in empty windows. 148# * Added a version check to prevent Irssi redraw errors. 149# * Fixed a bookmark NULL versus 0 bug. 150# * Fixed a remove-line bug in Uwe Dudenhoeffer his patch. 151# * New command 'help trackbar' to display the trackbar commands. 152# * Fixed an Irssi startup bug, now processing each auto-created window. 153# 154## 155 156## Known bugs and the todolist. 157# 158# Todo: * Instead of drawing a line, invert the line. 159# 160## 161 162## Authors: 163# 164# - Main maintainer & author: Peter 'kinlo' Leurs 165# - Many thanks to Timo 'cras' Sirainen for placing me on my way 166# - on-upgrade-remove-line patch by Uwe Dudenhoeffer 167# - trackbar resizing by Michiel Holtkamp (02 Jul 2012) 168# - scroll to trackbar, window excludes, and timestamp options by Nico R. 169# Wohlgemuth (22 Sep 2012) 170# 171## 172 173## Version history: 174# 175# 2.7: - add /set trackbar_all_manual option 176# 2.5: - merge back on scripts.irssi.org 177# - fix /trackbar redraw broken in 2.4 178# - fix legacy encodings 179# - add workaround for irssi issue #271 180# 2.4: - add support for horizontal splits 181# 2.3: - add some features for seen tracking using other scripts 182# 2.0: - big rewrite based on 1.4 183# * removed /tb, you can have it with /alias tb trackbar if you want 184# * subcommand and settings changes: 185# /trackbar vmark => /trackbar markvisible 186# /trackbar scroll => /trackbar goto (or just /trackbar) 187# /trackbar help => /help trackbar 188# /set trackbar_hide_windows => /set trackbar_ignore_windows 189# /set trackbar_timestamp => /set trackbar_print_timestamp 190# * magic line strings were removed, just paste the unicode you want! 191# * trackbar_timestamp_styled is not currently supported 192# 1.9: - add version guard 193# 1.8: - sub draw_bar 194# 1.7: - Added /tb scroll, trackbar_hide_windows, trackbar_timestamp_timestamp 195# and trackbar_timestamp_styled 196# 1.6: - Work around Irssi resize bug, please do /upgrade! (see below) 197# 1.5: - Resize trackbars in all windows when terminal is resized 198# 1.4: - Changed our's by my's so the irssi script header is valid 199# - Removed utf-8 support. In theory, the script should work w/o any 200# problems for utf-8, just set trackbar_string to a valid utf-8 character 201# and everything *should* work. However, this script is being plagued by 202# irssi internal bugs. The function Irssi::settings_get_str does NOT handle 203# unicode strings properly, hence you will notice problems when setting the bar 204# to a unicode char. For changing your bar to utf-8 symbols, read the line sub. 205# 1.3: - Upgrade now removes the trackbars. 206# - Some code cleanups, other defaults 207# - /mark sets the line to the bottom 208# 1.2: - Support for utf-8 209# - How the bar looks can now be configured with trackbar_string 210# and trackbar_style 211# 1.1: - Fixed bug when closing window 212# 1.0: - Initial release 213# 214## 215 216use Irssi; 217use Irssi::TextUI; 218use Encode; 219 220use POSIX qw(strftime); 221 222sub cmd_help { 223 my ($args) = @_; 224 if ($args =~ /^trackbar *$/i) { 225 print CLIENTCRAP <<HELP 226%9Syntax:%9 227 228TRACKBAR 229TRACKBAR GOTO 230TRACKBAR KEEP 231TRACKBAR MARK 232TRACKBAR MARKVISIBLE 233TRACKBAR MARKALL 234TRACKBAR REMOVE 235TRACKBAR REMOVEALL 236TRACKBAR REDRAW 237 238%9Parameters:%9 239 240 GOTO: Jump to where the trackbar is, to pick up reading 241 KEEP: Keep this window's trackbar where it is the next time 242 you switch windows (then this flag is cleared again) 243 MARK: Remove the old trackbar and mark the bottom of this 244 window with a new trackbar 245 MARKVISIBLE: Like mark for all visible windows 246 MARKALL: Like mark for all windows 247 REMOVE: Remove this window's trackbar 248 REMOVEALL: Remove all windows' trackbars 249 REDRAW: Force redraw of trackbars 250 251%9Description:%9 252 253 Manage a trackbar. Without arguments, it will scroll up to the trackbar. 254 255%9Examples:%9 256 257 /TRACKBAR MARK 258 /TRACKBAR REMOVE 259HELP 260 } 261} 262 263Irssi::theme_register([ 264 'trackbar_loaded', '%R>>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.', 265 'trackbar_wrong_version', '%R>>%n %_Trackbar:%_ Please upgrade your client to 0.8.17 or above if you would like to use this feature of trackbar.', 266 'trackbar_all_removed', '%R>>%n %_Trackbar:%_ All the trackbars have been removed.', 267 'trackbar_not_found', '%R>>%n %_Trackbar:%_ No trackbar found in this window.', 268]); 269 270my $old_irssi = Irssi::version < 20140701; 271sub check_version { 272 if ($old_irssi) { 273 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_wrong_version'); 274 return; 275 } else { 276 return 1; 277 } 278} 279 280sub is_utf8 { 281 lc Irssi::settings_get_str('term_charset') eq 'utf-8' 282} 283 284my (%config, %keep_trackbar, %unseen_trackbar); 285 286sub remove_one_trackbar { 287 my $win = shift; 288 my $view = shift || $win->view; 289 my $line = $view->get_bookmark('trackbar'); 290 if (defined $line) { 291 my $bottom = $view->{bottom}; 292 $view->remove_line($line); 293 $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; 294 $view->redraw; 295 } 296} 297 298sub add_one_trackbar { 299 my $win = shift; 300 my $view = shift || $win->view; 301 $win->print(line($win->{width}), MSGLEVEL_NEVER); 302 $view->set_bookmark_bottom('trackbar'); 303 $unseen_trackbar{ $win->{_irssi} } = 1; 304 Irssi::signal_emit("window trackbar added", $win); 305 $view->redraw; 306} 307 308sub update_one_trackbar { 309 my $win = shift; 310 my $view = shift || $win->view; 311 my $force = shift; 312 my $ignored = win_ignored($win, $view); 313 remove_one_trackbar($win, $view) 314 if $force || !defined $force || !$ignored; 315 add_one_trackbar($win, $view) 316 if $force || !$ignored; 317} 318 319sub win_ignored { 320 my $win = shift; 321 my $view = shift || $win->view; 322 return 1 unless $view->{buffer}{lines_count}; 323 return 1 if $win->{name} eq '(status)' && !$config{use_status_window}; 324 no warnings 'uninitialized'; 325 return 1 if grep { $win->{name} eq $_ || $win->{refnum} eq $_ 326 || $win->get_active_name eq $_ } @{ $config{ignore_windows} }; 327 return 0; 328} 329 330sub sig_window_changed { 331 my ($newwindow, $oldwindow) = @_; 332 return unless $oldwindow; 333 redraw_one_trackbar($newwindow) unless $old_irssi; 334 trackbar_update_seen($newwindow); 335 return if delete $keep_trackbar{ $oldwindow->{_irssi} }; 336 trackbar_update_seen($oldwindow); 337 return if $config{require_seen} && $unseen_trackbar{ $oldwindow->{_irssi } }; 338 return if $config{all_manual}; 339 update_one_trackbar($oldwindow, undef, 0); 340} 341 342sub trackbar_update_seen { 343 my $win = shift; 344 return unless $win; 345 return unless $unseen_trackbar{ $win->{_irssi} }; 346 347 my $view = $win->view; 348 my $line = $view->get_bookmark('trackbar'); 349 unless ($line) { 350 delete $unseen_trackbar{ $win->{_irssi} }; 351 Irssi::signal_emit("window trackbar seen", $win); 352 return; 353 } 354 my $startline = $view->{startline}; 355 return unless $startline; 356 357 if ($startline->{info}{time} < $line->{info}{time} 358 || $startline->{_irssi} == $line->{_irssi}) { 359 delete $unseen_trackbar{ $win->{_irssi} }; 360 Irssi::signal_emit("window trackbar seen", $win); 361 } 362} 363 364sub screen_length; 365{ local $@; 366 eval { require Text::CharWidth; }; 367 unless ($@) { 368 *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; 369 } 370 else { 371 *screen_length = sub { 372 my $temp = shift; 373 Encode::_utf8_on($temp) if is_utf8(); 374 length($temp) 375 }; 376 } 377} 378 379{ my %strip_table = ( 380 (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), 381 (map { $_ => $_ } (split //, '{}%')), 382 ); 383 sub c_length { 384 my $o = Irssi::strip_codes($_[0]); 385 $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : 386 $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; 387 screen_length($o) 388 } 389} 390 391sub line { 392 my ($width, $time) = @_; 393 my $string = $config{string}; 394 $string = ' ' unless length $string; 395 $time ||= time; 396 397 Encode::_utf8_on($string) if is_utf8(); 398 my $length = c_length($string); 399 400 my $format = ''; 401 if ($config{print_timestamp}) { 402 $format = $config{timestamp_str}; 403 $format =~ y/%/\01/; 404 $format =~ s/\01\01/%/g; 405 $format = strftime($format, localtime $time); 406 $format =~ y/\01/%/; 407 } 408 409 my $times = $width / $length; 410 $times += 1 if $times != int $times; 411 my $style = "$config{style}"; 412 Encode::_utf8_on($style) if is_utf8(); 413 $format .= $style; 414 $width -= c_length($format); 415 $string x= $times; 416 chop $string while length $string && c_length($string) > $width; 417 return $format . $string; 418} 419 420sub remove_all_trackbars { 421 for my $window (Irssi::windows) { 422 next unless ref $window; 423 remove_one_trackbar($window); 424 } 425} 426 427sub UNLOAD { 428 remove_all_trackbars(); 429} 430 431sub redraw_one_trackbar { 432 my $win = shift; 433 my $view = $win->view; 434 my $line = $view->get_bookmark('trackbar'); 435 return unless $line; 436 my $bottom = $view->{bottom}; 437 $win->print_after($line, MSGLEVEL_NEVER, line($win->{width}, $line->{info}{time}), 438 $line->{info}{time}); 439 $view->set_bookmark('trackbar', $win->last_line_insert); 440 $view->remove_line($line); 441 $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; 442 $view->redraw; 443} 444 445sub redraw_trackbars { 446 return unless check_version(); 447 for my $win (Irssi::windows) { 448 next unless ref $win; 449 redraw_one_trackbar($win); 450 } 451} 452 453sub goto_trackbar { 454 my $win = Irssi::active_win; 455 my $line = $win->view->get_bookmark('trackbar'); 456 457 if ($line) { 458 $win->command("scrollback goto ". strftime("%d %H:%M:%S", localtime($line->{info}{time}))); 459 } else { 460 $win->printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_not_found'); 461 } 462} 463 464sub cmd_mark { 465 update_one_trackbar(Irssi::active_win, undef, 1); 466} 467 468sub cmd_markall { 469 for my $window (Irssi::windows) { 470 next unless ref $window; 471 update_one_trackbar($window); 472 } 473} 474 475sub signal_stop { 476 Irssi::signal_stop; 477} 478 479sub cmd_markvisible { 480 my @wins = Irssi::windows; 481 my $awin = 482 my $bwin = Irssi::active_win; 483 my $awin_counter = 0; 484 Irssi::signal_add_priority('window changed' => 'signal_stop', -99); 485 do { 486 Irssi::active_win->command('window up'); 487 $awin = Irssi::active_win; 488 update_one_trackbar($awin); 489 ++$awin_counter; 490 } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); 491 Irssi::signal_remove('window changed' => 'signal_stop'); 492} 493 494sub cmd_trackbar_remove_one { 495 remove_one_trackbar(Irssi::active_win); 496} 497 498sub cmd_remove_all_trackbars { 499 remove_all_trackbars(); 500 Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_all_removed'); 501} 502 503sub cmd_keep_once { 504 $keep_trackbar{ Irssi::active_win->{_irssi} } = 1; 505} 506 507sub trackbar_runsub { 508 my ($data, $server, $item) = @_; 509 $data =~ s/\s+$//g; 510 511 if ($data) { 512 Irssi::command_runsub('trackbar', $data, $server, $item); 513 } else { 514 goto_trackbar(); 515 } 516} 517 518sub update_config { 519 my $was_status_window = $config{use_status_window}; 520 $config{style} = Irssi::settings_get_str('trackbar_style'); 521 $config{string} = Irssi::settings_get_str('trackbar_string'); 522 $config{require_seen} = Irssi::settings_get_bool('trackbar_require_seen'); 523 $config{all_manual} = Irssi::settings_get_bool('trackbar_all_manual'); 524 $config{ignore_windows} = [ split /[,\s]+/, Irssi::settings_get_str('trackbar_ignore_windows') ]; 525 $config{use_status_window} = Irssi::settings_get_bool('trackbar_use_status_window'); 526 $config{print_timestamp} = Irssi::settings_get_bool('trackbar_print_timestamp'); 527 if (defined $was_status_window && $was_status_window != $config{use_status_window}) { 528 if (my $swin = Irssi::window_find_name('(status)')) { 529 if ($config{use_status_window}) { 530 update_one_trackbar($swin); 531 } 532 else { 533 remove_one_trackbar($swin); 534 } 535 } 536 } 537 if ($config{print_timestamp}) { 538 my $ts_format = Irssi::settings_get_str('timestamp_format'); 539 my $ts_theme = Irssi::current_theme->get_format('fe-common/core', 'timestamp'); 540 my $render_str = Irssi::current_theme->format_expand($ts_theme); 541 (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; 542 $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; 543 $config{timestamp_str} = $render_str; 544 } 545 redraw_trackbars() unless $old_irssi; 546} 547 548Irssi::settings_add_str('trackbar', 'trackbar_string', is_utf8() ? "\x{2500}" : '-'); 549Irssi::settings_add_str('trackbar', 'trackbar_style', '%K'); 550Irssi::settings_add_str('trackbar', 'trackbar_ignore_windows', ''); 551Irssi::settings_add_bool('trackbar', 'trackbar_use_status_window', 1); 552Irssi::settings_add_bool('trackbar', 'trackbar_print_timestamp', 0); 553Irssi::settings_add_bool('trackbar', 'trackbar_require_seen', 0); 554Irssi::settings_add_bool('trackbar', 'trackbar_all_manual', 0); 555 556update_config(); 557 558Irssi::signal_add_last( 'mainwindow resized' => 'redraw_trackbars') 559 unless $old_irssi; 560 561Irssi::signal_register({'window trackbar added' => [qw/Irssi::UI::Window/]}); 562Irssi::signal_register({'window trackbar seen' => [qw/Irssi::UI::Window/]}); 563Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]}); 564Irssi::signal_add_last('gui page scrolled' => 'trackbar_update_seen'); 565 566Irssi::signal_add('setup changed' => 'update_config'); 567Irssi::signal_add_priority('session save' => 'remove_all_trackbars', Irssi::SIGNAL_PRIORITY_HIGH-1); 568 569Irssi::signal_add('window changed' => 'sig_window_changed'); 570 571Irssi::command_bind('trackbar goto' => 'goto_trackbar'); 572Irssi::command_bind('trackbar keep' => 'cmd_keep_once'); 573Irssi::command_bind('trackbar mark' => 'cmd_mark'); 574Irssi::command_bind('trackbar markvisible' => 'cmd_markvisible'); 575Irssi::command_bind('trackbar markall' => 'cmd_markall'); 576Irssi::command_bind('trackbar remove' => 'cmd_trackbar_remove_one'); 577Irssi::command_bind('trackbar removeall' => 'cmd_remove_all_trackbars'); 578Irssi::command_bind('trackbar redraw' => 'redraw_trackbars'); 579Irssi::command_bind('trackbar' => 'trackbar_runsub'); 580Irssi::command_bind('mark' => 'cmd_mark'); 581Irssi::command_bind_last('help' => 'cmd_help'); 582 583Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); 584 585# workaround for issue #271 586{ package Irssi::Nick } 587