From efb71c4e57877ae756be2365ec3925b23b11aa1a Mon Sep 17 00:00:00 2001 From: Dan Head Date: Wed, 6 Sep 2017 18:25:17 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .Xresources | 82 + .gitignore | 2 + .tern-project | 6 + bin/mediakeys-play | 9 + bin/vpn | 28 + i3/desktop.config | 186 ++ i3blocks/desktop.config | 54 + i3blocks/ipaddress.py | 16 + i3blocks/openvpn.py | 25 + i3blocks/ping.py | 17 + i3blocks/wifi.sh | 20 + irssi/default.theme | 294 +++ irssi/scripts/awaybar.pl | 43 + irssi/scripts/awl.pl | 2478 +++++++++++++++++++++++ irssi/scripts/nickcolor.pl | 145 ++ irssi/scripts/nicklist.pl | 805 ++++++++ irssi/scripts/tmux-nicklist-portable.pl | 425 ++++ irssi/scripts/tmux-nicklist.pl | 304 +++ irssi/scripts/trackbar.pl | 189 ++ irssi/scripts/trackbar22.pl | 500 +++++ irssi/scripts/usercount.pl | 171 ++ irssi/weed.theme | 601 ++++++ termite/config | 42 + termite/termite.terminfo | 54 + vim/.vimrc | 118 ++ vim/colors/wwdc16.vim | 144 ++ zsh/.zshrc | 91 + zsh/aliases.zsh | 16 + 28 files changed, 6865 insertions(+) create mode 100644 .Xresources create mode 100644 .gitignore create mode 100644 .tern-project create mode 100755 bin/mediakeys-play create mode 100755 bin/vpn create mode 100644 i3/desktop.config create mode 100644 i3blocks/desktop.config create mode 100755 i3blocks/ipaddress.py create mode 100755 i3blocks/openvpn.py create mode 100755 i3blocks/ping.py create mode 100755 i3blocks/wifi.sh create mode 100644 irssi/default.theme create mode 100755 irssi/scripts/awaybar.pl create mode 100755 irssi/scripts/awl.pl create mode 100755 irssi/scripts/nickcolor.pl create mode 100755 irssi/scripts/nicklist.pl create mode 100644 irssi/scripts/tmux-nicklist-portable.pl create mode 100644 irssi/scripts/tmux-nicklist.pl create mode 100755 irssi/scripts/trackbar.pl create mode 100644 irssi/scripts/trackbar22.pl create mode 100755 irssi/scripts/usercount.pl create mode 100644 irssi/weed.theme create mode 100644 termite/config create mode 100644 termite/termite.terminfo create mode 100644 vim/.vimrc create mode 100644 vim/colors/wwdc16.vim create mode 100644 zsh/.zshrc create mode 100644 zsh/aliases.zsh diff --git a/.Xresources b/.Xresources new file mode 100644 index 0000000..622c7cf --- /dev/null +++ b/.Xresources @@ -0,0 +1,82 @@ +! Xft settings +Xft.dpi: 96 +Xft.antialias: true +Xft.rgba: rgb +Xft.hinting: true +Xft.hintstyle: hintslight + +! URxvt settings +URxvt.termName: rxvt-256color +URxvt.depth: 32 +URxvt.geometry: 90x30 +URxvt.transparent: false +URxvt.fading: 0 +! URxvt.urgentOnBell: true +! URxvt.visualBell: true +URxvt.loginShell: true +URxvt.saveLines: 50 +URxvt.internalBorder: 3 +URxvt.lineSpace: 0 + +! Fonts +URxvt.allow_bold: false +URxvt*font: xft:Inconsolata:size=14 +! URxvt*boldFont: xft:Inconsolata:pixelsize=14 + +! Fix font space +URxvt.letterSpace: -1 + +! Scrollbar +URxvt.scrollStyle: rxvt +URxvt.scrollBar: false + +! Perl extensions +URxvt.perl-ext-common: default,matcher +URxvt.matcher.button: 1 +URxvt.urlLauncher: /usr/bin/xdg-open + +! Cursor +URxvt.cursorBlink: true +URxvt.cursorColor: #657b83 +URxvt.cursorUnderline: false + +! Pointer +URxvt.pointerBlank: true + +! Colours +*.foreground: #daba8b +*.background: #191716 +*.cursorColor: #daba8b + +! black +*.color0: #0f0e0d +*.color8: #383332 + +! red +*.color1: #845336 +*.color9: #8c4f4a + +! green +*.color2: #57553c +*.color10: #898471 + +! yellow +*.color3: #a17e3e +*.color11: #c8b491 + +! blue +*.color4: #43454f +*.color12: #65788f + +! magenta +*.color5: #604848 +*.color13: #755e4a + +! cyan +*.color6: #5c6652 +*.color14: #718062 + +! white +*.color7: #a18b62 +*.color15: #bc9d66 + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7243ae5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.swp diff --git a/.tern-project b/.tern-project new file mode 100644 index 0000000..23ffe6c --- /dev/null +++ b/.tern-project @@ -0,0 +1,6 @@ +{ + "libs": [ + "browser", + "jquery" + ] +} diff --git a/bin/mediakeys-play b/bin/mediakeys-play new file mode 100755 index 0000000..916310b --- /dev/null +++ b/bin/mediakeys-play @@ -0,0 +1,9 @@ +#!/bin/sh + +status=$(cmus-remote -C status | grep ^status | awk '{print $2}') +if [ "$status" == "playing" ]; then + exec cmus-remote -u +else + exec cmus-remote -p +fi +exit 0 diff --git a/bin/vpn b/bin/vpn new file mode 100755 index 0000000..49dbc77 --- /dev/null +++ b/bin/vpn @@ -0,0 +1,28 @@ +#!/bin/bash + +OVPNURL=https://nordvpn.com/api/files/zip +OVPNPATH=/home/dan/.openvpn +OVPNAUTH=/home/dan/.nordvpnauth +PROTO=tcp + +if [ "$1" == "--update" ] || [ "$1" == "-u" ]; then + echo "Updating..." + curl $OVPNURL > /tmp/nordvpn.zip + rm -f $OVPNPATH/* + unzip -qq /tmp/nordvpn.zip -d $OVPNPATH/ + rm /tmp/nordvpn.zip + sed -i 's,auth-user-pass,auth-user-pass '"$OVPNAUTH"',g' $OVPNPATH/* + echo "Done." + exit 0 +elif [ "$1" == "--kill" ] || [ "$1" == "-k" ]; then + sudo killall openvpn +else + find $OVPNPATH -name $1 + find $OVPNPATH -name "*$1*$PROTO*" | sort -R | tail -1 | while read file; do + sudo killall openvpn + echo "Connecting to: $file" + sudo /usr/sbin/openvpn --config $file & + done +fi + +exit 0 diff --git a/i3/desktop.config b/i3/desktop.config new file mode 100644 index 0000000..3b7ad50 --- /dev/null +++ b/i3/desktop.config @@ -0,0 +1,186 @@ +# This file has been auto-generated by i3-config-wizard(1). +# It will not be overwritten, so edit it as you like. +# +# Should you change your keyboard layout some time, delete +# this file and re-run i3-config-wizard(1). +# + +# i3 config file (v4) +# +# Please see http://i3wm.org/docs/userguide.html for a complete reference! + +set $mod Mod4 + +# Font for window titles. Will also be used by the bar unless a different font +# is used in the bar {} block below. +#font pango:Inconsolata 10 + +# This font is widely installed, provides lots of unicode glyphs, right-to-left +# text rendering and scalability on retina/hidpi displays (thanks to pango). +font pango:DejaVu Sans Mono 8 + +# Before i3 v4.8, we used to recommend this one as the default: +# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 +# The font above is very space-efficient, that is, it looks good, sharp and +# clear in small sizes. However, its unicode glyph coverage is limited, the old +# X core fonts rendering does not support right-to-left and this being a bitmap +# font, it doesn’t scale on retina/hidpi displays. + +# Use Mouse+$mod to drag floating windows to their wanted position +floating_modifier $mod + +# start a terminal +bindsym $mod+Return exec termite + +# kill focused window +bindsym $mod+Shift+q kill + +# start dmenu (a program launcher) +bindsym $mod+d exec dmenu_run +# There also is the (new) i3-dmenu-desktop which only displays applications +# shipping a .desktop file. It is a wrapper around dmenu, so you need that +# installed. +# bindsym $mod+d exec --no-startup-id i3-dmenu-desktop + +# change focus +bindsym $mod+j focus left +bindsym $mod+k focus down +bindsym $mod+l focus up +bindsym $mod+semicolon focus right + +# alternatively, you can use the cursor keys: +bindsym $mod+Left focus left +bindsym $mod+Down focus down +bindsym $mod+Up focus up +bindsym $mod+Right focus right + +# move focused window +bindsym $mod+Shift+j move left +bindsym $mod+Shift+k move down +bindsym $mod+Shift+l move up +bindsym $mod+Shift+semicolon move right + +# alternatively, you can use the cursor keys: +bindsym $mod+Shift+Left move left +bindsym $mod+Shift+Down move down +bindsym $mod+Shift+Up move up +bindsym $mod+Shift+Right move right + +# split in horizontal orientation +bindsym $mod+h split h + +# split in vertical orientation +bindsym $mod+v split v + +# enter fullscreen mode for the focused container +bindsym $mod+f fullscreen toggle + +# change container layout (stacked, tabbed, toggle split) +bindsym $mod+s layout stacking +bindsym $mod+w layout tabbed +bindsym $mod+e layout toggle split + +# toggle tiling / floating +bindsym $mod+Shift+space floating toggle + +# change focus between tiling / floating windows +bindsym $mod+space focus mode_toggle + +# focus the parent container +bindsym $mod+a focus parent + +# focus the child container +#bindsym $mod+d focus child + +# switch to workspace +bindsym $mod+1 workspace 1 +bindsym $mod+2 workspace 2 +bindsym $mod+3 workspace 3 +bindsym $mod+4 workspace 4 +bindsym $mod+5 workspace 5 +bindsym $mod+6 workspace 6 +bindsym $mod+7 workspace 7 +bindsym $mod+8 workspace 8 +bindsym $mod+9 workspace 9 +bindsym $mod+0 workspace 10 + +# move focused container to workspace +bindsym $mod+Shift+1 move container to workspace 1 +bindsym $mod+Shift+2 move container to workspace 2 +bindsym $mod+Shift+3 move container to workspace 3 +bindsym $mod+Shift+4 move container to workspace 4 +bindsym $mod+Shift+5 move container to workspace 5 +bindsym $mod+Shift+6 move container to workspace 6 +bindsym $mod+Shift+7 move container to workspace 7 +bindsym $mod+Shift+8 move container to workspace 8 +bindsym $mod+Shift+9 move container to workspace 9 +bindsym $mod+Shift+0 move container to workspace 10 + +# reload the configuration file +bindsym $mod+Shift+c reload +# restart i3 inplace (preserves your layout/session, can be used to upgrade i3) +bindsym $mod+Shift+r restart +# exit i3 (logs you out of your X session) +bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'" + +# resize window (you can also use the mouse for that) +mode "resize" { + # These bindings trigger as soon as you enter the resize mode + + # Pressing left will shrink the window’s width. + # Pressing right will grow the window’s width. + # Pressing up will shrink the window’s height. + # Pressing down will grow the window’s height. + bindsym j resize shrink width 10 px or 10 ppt + bindsym k resize grow height 10 px or 10 ppt + bindsym l resize shrink height 10 px or 10 ppt + bindsym semicolon resize grow width 10 px or 10 ppt + + # same bindings, but for the arrow keys + bindsym Left resize shrink width 10 px or 10 ppt + bindsym Down resize grow height 10 px or 10 ppt + bindsym Up resize shrink height 10 px or 10 ppt + bindsym Right resize grow width 10 px or 10 ppt + + # back to normal: Enter or Escape + bindsym Return mode "default" + bindsym Escape mode "default" +} + +bindsym $mod+r mode "resize" + +# Start i3bar to display a workspace bar (plus the system information i3status +# finds out, if available) +bar { + output DVI-D-1 + status_command i3blocks -c ~/.dotfiles/i3blocks/desktop.config + position bottom + mode dock + workspace_buttons yes + tray_output none + + font pango:DejaVu Sans Mono 10 + + colors { + background #191716 + statusline #ffffff + + focused_workspace #ffffff #285577 + active_workspace #ffffff #333333 + inactive_workspace #888888 #222222 + urgent_workspace #ffffff #900000 + } +} + +exec "setxkbmap gb" + +# Pulse Audio controls +bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume 0 +5% #increase sound volume +bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume 0 -5% #decrease sound volume +bindsym XF86AudioMute exec --no-startup-id pactl set-sink-mute 0 toggle # mute sound + +# Media keys +bindsym XF86AudioPlay exec ~/.dotfiles/bin/mediakeys-play +bindsym XF86AudioPause exec ~/.dotfiles/bin/mediakeys-play + +exec xrandr --output DVI-D-1 --primary --mode 1920x1200 --pos 1200x390 --output DVI-I-1 --mode 1920x1200 --rotate left --pos 0x0 diff --git a/i3blocks/desktop.config b/i3blocks/desktop.config new file mode 100644 index 0000000..285c6e4 --- /dev/null +++ b/i3blocks/desktop.config @@ -0,0 +1,54 @@ +command=/usr/lib/i3blocks/$BLOCK_NAME +separator_block_width=12 + +[iface] +label=IPs: +interval=10 +separator=false + +[ip] +command=$HOME/.dotfiles/i3blocks/ipaddress.py +interval=60 +separator=false + +[ping] +command=$HOME/.dotfiles/i3blocks/ping.py +interval=10 +separator=false + +[vpn] +command=$HOME/.dotfiles/i3blocks/openvpn.py +interval=10 + +[load_average] +interval=10 + +[disk] +label=/ +instance=/ +interval=30 +separator=false + +[disk] +label=/home +interval=30 +separator=false + +[disk] +label=/tmp +instance=/tmp +interval=30 + +[memory] +label=RAM +interval=30 + +[volume] +label=Vol: +interval=1 +signal=12 + +[time] +command=date '+%d-%m-%Y %H:%M:%S' +interval=1 + diff --git a/i3blocks/ipaddress.py b/i3blocks/ipaddress.py new file mode 100755 index 0000000..dfcdaca --- /dev/null +++ b/i3blocks/ipaddress.py @@ -0,0 +1,16 @@ +#!/usr/bin/python +from urllib.request import urlopen +from xml.etree import ElementTree as ET + +url = 'http://freegeoip.net/xml/' + +with urlopen(url) as res: + s = res.read() + +root = ET.fromstring(s) + +ip = root.find('IP').text +country = root.find('CountryName').text + +print('/ %s (%s)' % (ip, country)) +print('/ %s (%s)' % (ip, country)) diff --git a/i3blocks/openvpn.py b/i3blocks/openvpn.py new file mode 100755 index 0000000..ddb11e1 --- /dev/null +++ b/i3blocks/openvpn.py @@ -0,0 +1,25 @@ +#!/usr/bin/python +import subprocess +import re + +ps = subprocess.Popen('ps ax | grep openvpn | grep -v grep | tail -l | rev | cut -d \'/\' -f 1 | rev', shell=True, stdout=subprocess.PIPE) +line = str(ps.stdout.readline()) +process = str(ps.stdout.readline())[1:] +status = None +connected = False +if len(process) > 5: + connection = re.sub(r'[^0-9A-Za-z.]', '', process[1:]) + server = connection.upper().split('.')[0] + proto = re.sub(r'[^A-Z]', '', connection.upper().split('.')[3]) + status = '%s.%s' % (server, proto) + connected = True +else: + status = 'down' + +print('VPN: %s' % status) +print('VPN: %s' % status) + +if connected: + print('#00FF00') +else: + print('#FF0000') diff --git a/i3blocks/ping.py b/i3blocks/ping.py new file mode 100755 index 0000000..f1c96ad --- /dev/null +++ b/i3blocks/ping.py @@ -0,0 +1,17 @@ +#!/usr/bin/python +import subprocess +import re +url = 'google.co.uk' + +ps = subprocess.Popen('ping -c 1 %s | tail -1 | awk \'{print $4}\' | cut -d \'/\' -f 2' % url, shell=True, stdout=subprocess.PIPE) +ping = re.sub(r'[^0-9.]', '', str(ps.stdout.read())) + +print('Ping %sms' % ping) +print('Ping %sms' % ping) + +if float(ping) > 100: + print('#FF0000') +elif float(ping) > 50: + print('#DDDD00') +else: + print('#00FF00') diff --git a/i3blocks/wifi.sh b/i3blocks/wifi.sh new file mode 100755 index 0000000..d7c72ad --- /dev/null +++ b/i3blocks/wifi.sh @@ -0,0 +1,20 @@ +#!/bin/bash +INTERFACE="wlo1" + +SSID=$(iw dev $INTERFACE link | grep 'SSID' | awk -F: '/SSID/ { print $NF }') + +QUALITY=$(grep $INTERFACE /proc/net/wireless | awk '{ print int($3 * 100 / 70) }') + +echo "$SSID ($QUALITY%)" # full text +echo "$SSID ($QUALITY%)" # short text + +# color +if [[ $QUALITY -ge 80 ]]; then + echo "#00FF00" +elif [[ $QUALITY -lt 80 ]]; then + echo "#FFF600" +elif [[ $QUALITY -lt 60 ]]; then + echo "#FFAE00" +elif [[ $QUALITY -lt 40 ]]; then + echo "#FF0000" +fi diff --git a/irssi/default.theme b/irssi/default.theme new file mode 100644 index 0000000..6b6aeab --- /dev/null +++ b/irssi/default.theme @@ -0,0 +1,294 @@ +# When testing changes, the easiest way to reload the theme is with /RELOAD. +# This reloads the configuration file too, so if you did any changes remember +# to /SAVE it first. Remember also that /SAVE overwrites the theme file with +# old data so keep backups :) + +# TEMPLATES: + +# The real text formats that irssi uses are the ones you can find with +# /FORMAT command. Back in the old days all the colors and texts were mixed +# up in those formats, and it was really hard to change the colors since you +# might have had to change them in tens of different places. So, then came +# this templating system. + +# Now the /FORMATs don't have any colors in them, and they also have very +# little other styling. Most of the stuff you need to change is in this +# theme file. If you can't change something here, you can always go back +# to change the /FORMATs directly, they're also saved in these .theme files. + +# So .. the templates. They're those {blahblah} parts you see all over the +# /FORMATs and here. Their usage is simply {name parameter1 parameter2}. +# When irssi sees this kind of text, it goes to find "name" from abstracts +# block below and sets "parameter1" into $0 and "parameter2" into $1 (you +# can have more parameters of course). Templates can have subtemplates. +# Here's a small example: +# /FORMAT format hello {colorify {underline world}} +# abstracts = { colorify = "%G$0-%n"; underline = "%U$0-%U"; } +# When irssi expands the templates in "format", the final string would be: +# hello %G%Uworld%U%n +# ie. underlined bright green "world" text. +# and why "$0-", why not "$0"? $0 would only mean the first parameter, +# $0- means all the parameters. With {underline hello world} you'd really +# want to underline both of the words, not just the hello (and world would +# actually be removed entirely). + +# COLORS: + +# You can find definitions for the color format codes in docs/formats.txt. + +# There's one difference here though. %n format. Normally it means the +# default color of the terminal (white mostly), but here it means the +# "reset color back to the one it was in higher template". For example +# if there was /FORMAT test %g{foo}bar, and foo = "%Y$0%n", irssi would +# print yellow "foo" (as set with %Y) but "bar" would be green, which was +# set at the beginning before the {foo} template. If there wasn't the %g +# at start, the normal behaviour of %n would occur. If you _really_ want +# to use the terminal's default color, use %N. + +############################################################################# + +# default foreground color (%N) - -1 is the "default terminal color" +default_color = "-1"; + +# print timestamp/servertag at the end of line, not at beginning +info_eol = "false"; + +# these characters are automatically replaced with specified color +# (dark grey by default) +replaces = { "[]=" = "%K$*%n"; }; + +abstracts = { + ## + ## generic + ## + + # text to insert at the beginning of each non-message line + line_start = "%B-%n!%B-%n "; + + # timestamp styling, nothing by default + timestamp = "$*"; + + # any kind of text that needs hilighting, default is to bold + hilight = "%_$*%_"; + + # any kind of error message, default is bright red + error = "%R$*%n"; + + # channel name is printed + channel = "%_$*%_"; + + # nick is printed + nick = "%_$*%_"; + + # nick host is printed + nickhost = "[$*]"; + + # server name is printed + server = "%_$*%_"; + + # some kind of comment is printed + comment = "[$*]"; + + # reason for something is printed (part, quit, kick, ..) + reason = "{comment $*}"; + + # mode change is printed ([+o nick]) + mode = "{comment $*}"; + + ## + ## channel specific messages + ## + + # highlighted nick/host is printed (joins) + channick_hilight = "%C$*%n"; + chanhost_hilight = "{nickhost %c$*%n}"; + + # nick/host is printed (parts, quits, etc.) + channick = "%c$*%n"; + chanhost = "{nickhost $*}"; + + # highlighted channel name is printed + channelhilight = "%c$*%n"; + + # ban/ban exception/invite list mask is printed + ban = "%c$*%n"; + + ## + ## messages + ## + + # the basic styling of how to print message, $0 = nick mode, $1 = nick + msgnick = "%K<%n$0$1-%K>%n %|"; + + # message from you is printed. "ownnick" specifies the styling of the + # nick ($0 part in msgnick) and "ownmsgnick" specifies the styling of the + # whole line. + + # Example1: You want the message text to be green: + # ownmsgnick = "{msgnick $0 $1-}%g"; + # Example2.1: You want < and > chars to be yellow: + # ownmsgnick = "%Y{msgnick $0 $1-%Y}%n"; + # (you'll also have to remove <> from replaces list above) + # Example2.2: But you still want to keep <> grey for other messages: + # pubmsgnick = "%K{msgnick $0 $1-%K}%n"; + # pubmsgmenick = "%K{msgnick $0 $1-%K}%n"; + # pubmsghinick = "%K{msgnick $1 $0$2-%n%K}%n"; + # ownprivmsgnick = "%K{msgnick $*%K}%n"; + # privmsgnick = "%K{msgnick %R$*%K}%n"; + + # $0 = nick mode, $1 = nick + ownmsgnick = "{msgnick $0 $1-}"; + ownnick = "%_$*%n"; + + # public message in channel, $0 = nick mode, $1 = nick + pubmsgnick = "{msgnick $0 $1-}"; + pubnick = "%N$*%n"; + + # public message in channel meant for me, $0 = nick mode, $1 = nick + pubmsgmenick = "{msgnick $0 $1-}"; + menick = "%Y$*%n"; + + # public highlighted message in channel + # $0 = highlight color, $1 = nick mode, $2 = nick + pubmsghinick = "{msgnick $1 $0$2-%n}"; + + # channel name is printed with message + msgchannel = "%K:%c$*%n"; + + # private message, $0 = nick, $1 = host + privmsg = "[%R$0%K(%r$1-%K)%n] "; + + # private message from you, $0 = "msg", $1 = target nick + ownprivmsg = "[%r$0%K(%R$1-%K)%n] "; + + # own private message in query + ownprivmsgnick = "{msgnick $*}"; + ownprivnick = "%_$*%n"; + + # private message in query + privmsgnick = "{msgnick %R$*%n}"; + + ## + ## Actions (/ME stuff) + ## + + # used internally by this theme + action_core = "%_ * $*%n"; + + # generic one that's used by most actions + action = "{action_core $*} "; + + # own action, both private/public + ownaction = "{action $*}"; + + # own action with target, both private/public + ownaction_target = "{action_core $0}%K:%c$1%n "; + + # private action sent by others + pvtaction = "%_ (*) $*%n "; + pvtaction_query = "{action $*}"; + + # public action sent by others + pubaction = "{action $*}"; + + + ## + ## other IRC events + ## + + # whois + whois = "%# $[8]0 : $1-"; + + # notices + ownnotice = "[%r$0%K(%R$1-%K)]%n "; + notice = "%K-%M$*%K-%n "; + pubnotice_channel = "%K:%m$*"; + pvtnotice_host = "%K(%m$*%K)"; + servernotice = "%g!$*%n "; + + # CTCPs + ownctcp = "[%r$0%K(%R$1-%K)] "; + ctcp = "%g$*%n"; + + # wallops + wallop = "%_$*%n: "; + wallop_nick = "%n$*"; + wallop_action = "%_ * $*%n "; + + # netsplits + netsplit = "%R$*%n"; + netjoin = "%C$*%n"; + + # /names list + names_prefix = ""; + names_nick = "[%_$0%_$1-] "; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "[%g$*%n]"; + names_channel = "%G$*%n"; + + # DCC + dcc = "%g$*%n"; + dccfile = "%_$*%_"; + + # DCC chat, own msg/action + dccownmsg = "[%r$0%K($1-%K)%n] "; + dccownnick = "%R$*%n"; + dccownquerynick = "%_$*%n"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}%K:%c$1%n "; + + # DCC chat, others + dccmsg = "[%G$1-%K(%g$0%K)%n] "; + dccquerynick = "%G$*%n"; + dccaction = "%_ (*dcc*) $*%n %|"; + + ## + ## statusbar + ## + + # default background for all statusbars. You can also give + # the default foreground color for statusbar items. + sb_background = "%4%w"; + + # default backround for "default" statusbar group + #sb_default_bg = "%4"; + # background for prompt / input line + sb_prompt_bg = "%n"; + # background for info statusbar + sb_info_bg = "%8"; + # background for topicbar (same default) + #sb_topic_bg = "%4"; + + # text at the beginning of statusbars. sb-item already puts + # space there,so we don't use anything by default. + sbstart = ""; + # text at the end of statusbars. Use space so that it's never + # used for anything. + sbend = " "; + + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + + prompt = "[$*] "; + + sb = " %c[%n$*%c]%n"; + sbmode = "(%c+%n$*)"; + sbaway = " (%GzZzZ%n)"; + sbservertag = ":$0 (change with ^X)"; + sbnickmode = "$0"; + + # activity in statusbar + + # ',' separator + sb_act_sep = "%c$*"; + # normal text + sb_act_text = "%c$*"; + # public message + sb_act_msg = "%W$*"; + # hilight + sb_act_hilight = "%M$*"; + # hilight with specified color, $0 = color, $1 = text + sb_act_hilight_color = "$0$1-%n"; +}; diff --git a/irssi/scripts/awaybar.pl b/irssi/scripts/awaybar.pl new file mode 100755 index 0000000..ddf3792 --- /dev/null +++ b/irssi/scripts/awaybar.pl @@ -0,0 +1,43 @@ +# awaybar.pl -- initially built for Irssi 0.8.9 +# thanks to mood.pl for practically allowing me +# to copy the approach.. +use strict; +use Irssi; +use Irssi::TextUI; +use vars qw($VERSION %IRSSI); + +$VERSION = "0.1.1"; +%IRSSI = ( + authors => 'Simon Shine', + contact => 'http://shine.eu.org/', + name => 'awaybar', + description => 'Provides a menubar item with away message', + license => 'Public domain', +); + +Irssi::statusbar_item_register('awaybar', 0, 'awaybar'); +Irssi::signal_add('away mode changed', 'awaybar_redraw'); + +sub awaybar { + my ($item, $get_size_only) = @_; + my $away_reason = !Irssi::active_server() ? undef : Irssi::active_server()->{away_reason}; + + if (defined $away_reason && length $away_reason) { + my %r = ('\{' => '(', + '\}' => ')', + '%' => '%%',); + $away_reason =~ s/$_/$r{$_}/g for (keys %r); + + #my $format = $theme->format_expand("{sb_awaybar Olet AWAY. Syy: $away_reason}"); + my $format = "{sb_awaybar $away_reason}."; + + $item->{min_size} = $item->{max_size} = length($away_reason); + $item->default_handler($get_size_only, $format, 0, 1); + } else { + $item->{min_size} = $item->{max_size} = 0; + } +} + +sub awaybar_redraw { + Irssi::statusbar_items_redraw('awaybar'); +} diff --git a/irssi/scripts/awl.pl b/irssi/scripts/awl.pl new file mode 100755 index 0000000..69725bf --- /dev/null +++ b/irssi/scripts/awl.pl @@ -0,0 +1,2478 @@ +use strict; # use warnings; + +# {{{ debug + +#BEGIN { +# open STDERR, '>', '/home/ailin/wlstatwarnings'; +#}; + +# 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 + +# }}} + +# if you don't know how to operate folds, type zn + +# {{{ header + +use Irssi (); # which is the minimum required version of Irssi ? +use Irssi::TextUI; + +use vars qw($VERSION %IRSSI); + +$VERSION = '0.6ca'; +%IRSSI = ( + original_authors => q(BC-bd, Veli, Timo Sirainen, ). + q(Wouter Coekaerts, Jean-Yves Lefort), # (decadix) + original_contact => q(bd@bc-bd.org, veli@piipiip.net, tss@iki.fi, ). + q(wouter@coekaerts.be, jylefort@brutele.be), + authors => q(Nei), + contact => q(Nei@QuakeNet), + url => "http://anti.teamidiot.de/", + name => q(awl), + description => q(Adds a permanent advanced window list on the right or ). + q(in a statusbar.), + description2 => q(Based on chanact.pl which was apparently based on ). + q(lightbar.c and nicklist.pl with various other ideas ). + q(from random scripts.), + license => q(GNU GPLv2 or later), +); + +# }}} + +# {{{ *** D O C U M E N T A T I O N *** + +# 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-. +# # +# # 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 awl 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 awl. +# 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:i +# "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 awl +# +# +# 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 awl_display_nokey +# /set awl_display_key +# /set awl_display_nokey_active +# /set awl_display_key_active +# * 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 +# /+++++++++++++++++++++++++++++++++, +# | **** I M P O R T A N T : **** | +# | | +# | don't forget to use $S if you | +# | used $H before! | +# | | +# '+++++++++++++++++++++++++++++++++/ +# XXX NOTE ON *_active: there is a BUG somewhere in the length +# XXX calculation. currently it's best to NOT remove $H/$S from those +# XXX settings if you use it in the non-active settings. +# }}} +# {{{ /set awl_separator +# * string : Charater to use between the channel entries +# you'll need to escape " " space and "$" like this: +# "/set awl_separator \ " +# "/set awl_separator \$" +# and {}% like this: +# "/set awl_separator %{" +# "/set awl_separator %}" +# "/set awl_separator %%" +# (reason being, that the separator is used inside a {format }) +# }}} +# {{{ /set awl_prefer_name +# * this setting decides whether awl will use the active_name (OFF) or the +# window name as the name/caption in awl_display_*. +# That way you can rename windows using /window name myownname. +# }}} +# {{{ /set awl_hide_data +# * 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 awl_maxlines +# * num : number of lines to use for the window list (0 to disable, negative +# lock) +# }}} +# {{{ /set awl_columns +# * num : number of columns to use in screen mode (0 for unlimited) +# }}} +# {{{ /set awl_block +# * num : width of a column in screen mode (negative values = block display) +# /+++++++++++++++++++++++++++++++++, +# | ****** W A R N I N G ! ****** | +# | | +# | If your block display looks | +# | DISTORTED, you need to add the | +# | following line to your .theme | +# | file under | +# | abstracts = { : | +# | | +# | sb_act_none = "%n$*"; | +# | | +# '+++++++++++++++++++++++++++++++++/ +#.02:08:26. < shi> Irssi::current_theme()->get_format <.. can this be used? +# }}} +# {{{ /set awl_sbar_maxlength +# * if you enable the maxlength setting, the block width will be used as a +# maximum length for the non-block statusbar mode too. +# }}} +# {{{ /set awl_height_adjust +# * num : how many lines to leave empty in screen mode +# }}} +# {{{ /set awl_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 awl_placement +# /set awl_position +# * these settings correspond to /statusbar because awl will create +# statusbars for you +# (see /help statusbar to learn more) +# }}} +# {{{ /set awl_all_disable +# * if you set awl_all_disable to ON, awl will also remove the +# last statusbar it created if it is empty. +# As you might guess, this only makes sense with awl_hide_data > 0 ;) +# }}} +# {{{ /set awl_automode +# * this setting defines whether the window list is shown in statusbars or +# whether the screen hack is used (from nicklist.pl) +# }}} +# }}} +########## +# {{{ COMMANDS +######## +# {{{ /awl paste +# * enables or disables the screen hack windowlist. This is useful when you +# want to mark & copy text that you want to paste somewhere (hence the +# name). (ON means AWL disabled!) +# This is nicely bound to a function key for example. +# }}} +# {{{ /awl redraw +# * redraws the screen hack windowlist. There are many occasions where the +# screen hack windowlist can get destroyed so you can use this command to +# fix it. +# }}} +# }}} +### +# {{{ WISHES +#### +# +# if you fiddle with my mess, provide me with your fixes so I can benefit as well +# +# Nei =^.^= ( QuakeNet accountname: ailin ) +# }}} + +# }}} + +# {{{ modules + +#use Class::Classless; +#use Term::Info; + +# }}} + +# {{{ global variables + +my $replaces = '[=]'; # AARGH!!! (chars that are always surrounded by weird + # colour codes by Irssi) + +my $actString = []; # statusbar texts +my $currentLines = 0; +my $resetNeeded; # layout/screen has changed, redo everything +my $needRemake; # "normal" changes +#my $callcount = 0; +sub GLOB_QUEUE_TIMER () { 100 } +my $globTime = undef; # timer to limit remake() calls + + +my $SCREEN_MODE; +my $DISABLE_SCREEN_TEMP; +my $currentColumns = 0; +my $screenResizing; +my ($screenHeight, $screenWidth); +my $screenansi = bless { + NAME => 'Screen::ANSI', + PARENTS => [], + METHODS => { + dcs => sub { "\033P" }, + st => sub { "\033\\"}, + } +}, 'Class::Classless::X'; +#my $terminfo = new Term::Info 'xterm'; # xterm here, make this modular +# {{{{{{{{{{{{{{{ +my $terminfo = bless { # xterm here, make this modular + NAME => 'Term::Info::xterm', + PARENTS => [], + METHODS => { + # civis=\E[?25l, + civis => sub { "\033[?25l" }, + # sc=\E7, + sc => sub { "\0337" }, + # cup=\E[%i%p1%d;%p2%dH, + cup => sub { shift;shift; "\033[" . ($_[0] + 1) . ';' . ($_[1] + 1) . 'H' }, + # el=\E[K, + el => sub { "\033[K" }, + # rc=\E8, + rc => sub { "\0338" }, + # cnorm=\E[?25h, + cnorm => sub { "\033[?25h" }, + # setab=\E[4%p1%dm, + setab => sub { shift;shift; "\033[4" . $_[0] . 'm' }, + # setaf=\E[3%p1%dm, + setaf => sub { shift;shift; "\033[3" . $_[0] . 'm' }, + # bold=\E[1m, + bold => sub { "\033[1m" }, + # blink=\E[5m, + blink => sub { "\033[5m" }, + # rev=\E[7m, + rev => sub { "\033[7m" }, + # op=\E[39;49m, + op => sub { "\033[39;49m" }, + } +}, 'Class::Classless::X'; +# }}}}}}}}}}}}}}} + + +sub setc () { + $IRSSI{'name'} +} +sub set ($) { + setc . '_' . shift +} + +# }}} + + +# {{{ sbar mode + +my %statusbars; # currently active statusbars + +# maybe I should just tie the array ? +sub add_statusbar { + for (@_) { + # add subs + for my $l ($_) { { + no strict 'refs'; # :P + *{set$l} = sub { awl($l, @_) }; + }; } + Irssi::command('statusbar ' . (set$_) . ' reset'); + Irssi::command('statusbar ' . (set$_) . ' enable'); + if (lc Irssi::settings_get_str(set 'placement') eq 'top') { + Irssi::command('statusbar ' . (set$_) . ' placement top'); + } + if ((my $x = int Irssi::settings_get_int(set 'position')) != 0) { + Irssi::command('statusbar ' . (set$_) . ' position ' . $x); + } + Irssi::command('statusbar ' . (set$_) . ' add -priority 100 -alignment left barstart'); + Irssi::command('statusbar ' . (set$_) . ' add ' . (set$_)); + Irssi::command('statusbar ' . (set$_) . ' add -priority 100 -alignment right barend'); + Irssi::command('statusbar ' . (set$_) . ' disable'); + Irssi::statusbar_item_register(set$_, '$0', set$_); + $statusbars{$_} = {}; + } +} + +sub remove_statusbar { + for (@_) { + Irssi::command('statusbar ' . (set$_) . ' reset'); + Irssi::statusbar_item_unregister(set$_); # XXX does this actually work ? + # DO NOT REMOVE the sub before you have unregistered it :)) + for my $l ($_) { { + no strict 'refs'; + undef &{set$l}; + }; } + delete $statusbars{$_}; + } +} + +sub syncLines { + my $temp = $currentLines; + $currentLines = @$actString; + #Irssi::print("current lines: $temp new lines: $currentLines"); + my $currMaxLines = Irssi::settings_get_int(set 'maxlines'); + if ($currMaxLines > 0 and @$actString > $currMaxLines) { + $currentLines = $currMaxLines; + } + elsif ($currMaxLines < 0) { + $currentLines = abs($currMaxLines); + } + return if ($temp == $currentLines); + if ($currentLines > $temp) { + for ($temp .. ($currentLines - 1)) { + add_statusbar($_); + Irssi::command('statusbar ' . (set$_) . ' enable'); + } + } + else { + for ($_ = ($temp - 1); $_ >= $currentLines; $_--) { + Irssi::command('statusbar ' . (set$_) . ' disable'); + remove_statusbar($_); + } + } +} + +# FIXME implement $get_size_only check, and user $item->{min|max-size} ?? +sub awl { + 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); +} + +# 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'}) + ) { + my $name = quotemeta(set ''); + if ($cont_stripped =~ m/^$name(\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(); + +# end sbar mode }}} + + +# {{{ keymaps + +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+)/) { + my ($level, $key, $window) = ($1, $2, $3); + my $numlevel = ($level =~ y/-//) - 1; + $keymap{$window} = ('-' x $numlevel) . "$key"; + } + 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(); + +# end keymaps }}} + +# {{{ format handling + +# a bad way do do expansions but who cares +sub expand { + my ($string, %format) = @_; + my ($exp, $repl); + $string =~ s/\$$exp/$repl/g while (($exp, $repl) = each(%format)); + return $string; +} + +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; + #if ($_[0]) { # for the future?!? + # eval { + # $o = $_[0]->parse_special($i); + # }; + # unless ($@) { + # return $o; + # } + #} + my $win = shift || 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 ir_parse_special_protected { + my $o; my $i = shift; + $i =~ s/ + ( \\. ) | # skip over escapes (maybe) + ( \$[^% $\]+ ) # catch special variables + / + if ($1) { $1 } + elsif ($2) { my $i2 = $2; ir_fe(ir_parse_special($i2, @_)) } + else { $& } + /gex; + $i +} + + +sub sb_ctfe { # Irssi::current_theme->format_expand wrapper + Irssi::current_theme->format_expand( + shift, + ( + Irssi::EXPAND_FLAG_IGNORE_REPLACES + | + ($_[0]?0:Irssi::EXPAND_FLAG_IGNORE_EMPTY) + ) + ) +} +sub sb_expand { # expand {format }s (and apply parse_special for $vars) + ir_parse_special( + sb_ctfe(shift) + ) +} +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) + my $term_type = 'term_type'; + if (Irssi::version > 20040819) { # this is probably wrong, but I don't know + # when the setting name got changed + $term_type = 'term_charset'; + } + #if (lc Irssi::settings_get_str($term_type) eq '8bit' + # or Irssi::settings_get_str($term_type) =~ /^iso/i + #) { + # length(sb_strip(shift)) + #} + #else { + my $temp = sb_strip(shift); + # try to get the displayed width + my $length; + eval { + require Text::CharWidth; + $length = Text::CharWidth::mbswidth($temp); + }; + unless ($@) { + return $length; + } + else { + if (lc Irssi::settings_get_str($term_type) eq 'utf-8') { + # 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); + }; + } + # there is nothing more I can do + length($temp) + } + #} +} + +# !!! 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; } + +## 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; # XXX HOW CAN I HANDLE THE SPACES CORRECTLY XXX + $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 ir_ve { # escapes special vars but leave colours alone + my $x = shift; + #$x =~ s/([%{}])/%$1/g; + $x =~ s/(\\|\$|[ ])/\\$1/g; + $x +} + +my %ansi_table; +{ + my ($i, $j, $k) = (0, 0, 0); + %ansi_table = ( + # fe-common::core::formats.c:format_expand_styles + # do format_backs + (map { $_ => $terminfo->setab($i++) } (split //, '01234567' )), + # do format_fores + (map { $_ => $terminfo->setaf($j++) } (split //, 'krgybmcw' )), + # do bold_fores + (map { $_ => $terminfo->bold() . + $terminfo->setaf($k++) } (split //, 'KRGYBMCW')), + # reset + #(map { $_ => $terminfo->op() } (split //, 'nN')), + (map { $_ => $terminfo->op() } (split //, 'n')), + (map { $_ => "\033[0m" } (split //, 'N')), # XXX quick and DIRTY + # flash/bright + F => $terminfo->blink(), + # reverse + 8 => $terminfo->rev(), + # bold + (map { $_ => $terminfo->bold() } (split //, '9_')), + # delete other stuff + (map { $_ => '' } (split //, ':|>#[')), + # escape + (map { $_ => $_ } (split //, '{}%')), + ) +} +sub formats_to_ansi_basic { + my $o = shift; + $o =~ s/(%(.))/exists $ansi_table{$2} ? $ansi_table{$2} : $1/gex; + $o +} + +sub lc1459 ($) { my $x = shift; $x =~ y/A-Z][\^/a-z}{|~/; $x } +Irssi::settings_add_str(setc, 'banned_channels', ''); +Irssi::settings_add_bool(setc, 'banned_channels_on', 0); +my %banned_channels = map { lc1459($_) => undef } +split ' ', Irssi::settings_get_str('banned_channels'); +Irssi::settings_add_str(setc, 'fancy_abbrev', 'fancy'); + +# }}} + +# {{{ main + +sub remake () { + #$callcount++; + #my $xx = $callcount; Irssi::print("starting remake [ $xx ]"); + my ($hilight, $number, $display); + my $separator = '{sb_act_sep ' . Irssi::settings_get_str(set 'separator') . + '}'; + my $custSort = Irssi::settings_get_str(set 'sort'); + my $custSortDir = 1; + if ($custSort =~ /^[-!](.*)/) { + $custSortDir = -1; + $custSort = $1; + } + + my @wins = + sort { + ( + ( (int($a->{$custSort}) <=> int($b->{$custSort})) * $custSortDir ) + || + ($a->{'refnum'} <=> $b->{'refnum'}) + ) + } Irssi::windows; + my $block = Irssi::settings_get_int(set 'block'); + my $columns = $currentColumns; + my $oldActString = $actString if $SCREEN_MODE; + $actString = $SCREEN_MODE ? [' A W L'] : []; + my $line = $SCREEN_MODE ? 1 : 0; + my $width = $SCREEN_MODE + ? + $screenWidth - abs($block)*$columns + 1 + : + ([Irssi::windows]->[0]{'width'} - sb_length('{sb x}')); + my $height = $screenHeight - abs(Irssi::settings_get_int(set + 'height_adjust')); + my ($numPad, $keyPad) = (0, 0); + my %abbrevList; + if ($SCREEN_MODE or Irssi::settings_get_bool(set 'sbar_maxlength') + or ($block < 0) + ) { + %abbrevList = (); + if (Irssi::settings_get_str('fancy_abbrev') !~ /^(no|off|head)/i) { + my @nameList = map { ref $_ ? $_->get_active_name : '' } @wins; + for (my $i = 0; $i < @nameList - 1; ++$i) { + my ($x, $y) = ($nameList[$i], $nameList[$i + 1]); + for ($x, $y) { s/^[+#!=]// } + my $res = Algorithm::LCSS::LCSS($x, $y); + if (defined $res) { + #Irssi::print("common pattern $x $y : $res"); + #Irssi::print("found at $nameList[$i] ".index($nameList[$i], + # $res)); + $abbrevList{$nameList[$i]} = int (index($nameList[$i], $res) + + (length($res) / 2)); + #Irssi::print("found at ".$nameList[$i+1]." ".index($nameList[$i+1], + # $res)); + $abbrevList{$nameList[$i+1]} = int (index($nameList[$i+1], $res) + + (length($res) / 2)); + } + } + } + if ($SCREEN_MODE or ($block < 0)) { + $numPad = length((sort { length($b) <=> length($a) } keys %keymap)[0]); + $keyPad = length((sort { length($b) <=> length($a) } values %keymap)[0]); + } + } + if ($SCREEN_MODE) { + print STDERR $screenansi->dcs(). + $terminfo->civis(). + $terminfo->sc(). + $screenansi->st(); + if (@$oldActString < 1) { + print STDERR $screenansi->dcs(). + $terminfo->cup(0, $width). + $actString->[0]. + $terminfo->el(). + $screenansi->st(); + } + } + foreach my $win (@wins) { + unless ($SCREEN_MODE) { + $actString->[$line] = '' unless defined $actString->[$line] + or Irssi::settings_get_bool(set '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; + $name = '*' if (Irssi::settings_get_bool('banned_channels_on') and exists + $banned_channels{lc1459($name)}); + $name = $win->{'name'} if $name ne '*' and $win->{'name'} ne '' + and Irssi::settings_get_bool(set 'prefer_name'); + my $active = $win->{'active'}; + my $colour = $win->{'hilight_color'}; + if (!defined $colour) { $colour = ''; } + + if ($win->{'data_level'} < Irssi::settings_get_int(set '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'}; + my @display = ('display_nokey'); + if (defined $keymap{$number} and $keymap{$number} ne '') { + unshift @display, map { (my $cpy = $_) =~ s/_no/_/; $cpy } @display; + } + if (Irssi::active_win->{'refnum'} == $number) { + unshift @display, map { my $cpy = $_; $cpy .= '_active'; $cpy } @display; + } + #Irssi::print("win $number [@display]: " . join '.', split //, join '<<', map { + # Irssi::settings_get_str(set $_) } @display); + $display = (grep { $_ } + map { Irssi::settings_get_str(set $_) } + @display)[0]; + #Irssi::print("win $number : " . join '.', split //, $display); + + if ($SCREEN_MODE or Irssi::settings_get_bool(set 'sbar_maxlength') + or ($block < 0) + ) { + my $baseLength = sb_length(ir_escape(ir_ve(ir_parse_special_protected(sb_ctfe( + '{sb_background}' . expand($display, + C => ir_fe('x'), + N => $number . (' 'x($numPad - length($number))), + Q => ir_fe((' 'x($keyPad - length($keymap{$number}))) . $keymap{$number}), + H => $hilight, + S => '}{sb_background}' + ), 1), $win)))) - 1; + my $diff = abs($block) - (length($name) + $baseLength); + if ($diff < 0) { # too long + if (abs($diff) >= length($name)) { $name = '' } # forget it + elsif (abs($diff) + 1 >= length($name)) { $name = substr($name, + 0, 1); } + else { + my $middle = exists $abbrevList{$name} ? + (($abbrevList{$name} + (2*(length($name) / 2)))/3) : + ((Irssi::settings_get_str('fancy_abbrev') =~ /^head/i) ? + length($name) : + (length($name) / 2)); + my $cut = int($middle - (abs($diff) / 2) + .55); + $cut = 1 if $cut < 1; + $cut = length($name) - abs($diff) - 1 if $cut > (length($name) - + abs($diff) - 1); + $name = substr($name, 0, $cut) . '~' . substr($name, $cut + + abs($diff) + 1); + } + } + elsif ($SCREEN_MODE or ($block < 0)) { + $name .= (' ' x $diff); + } + } + + my $add = ir_ve(ir_parse_special_protected(sb_ctfe('{sb_background}' . expand($display, + C => ir_fe($name), + N => $number . (' 'x($numPad - length($number))), + Q => ir_fe((' 'x($keyPad - length($keymap{$number}))) . $keymap{$number}), + H => $hilight, + S => '}{sb_background}' + ), 1), $win)); + if ($SCREEN_MODE) { + $actString->[$line] = $add; + if ((!defined $oldActString->[$line] + or $oldActString->[$line] ne $actString->[$line]) + and + $line <= ($columns * $height) + ) { + print STDERR $screenansi->dcs(). + $terminfo->cup(($line-1) % $height+1, $width + ( + abs($block) * int(($line-1) / $height))). + formats_to_ansi_basic(sb_expand(ir_escape($actString->[$line]))). + #$terminfo->el(). + $screenansi->st(); + } + $line++; + } + else { + #$temp =~ s/\{\S+?(?:\s(.*?))?\}/$1/g; + #$temp =~ s/\\\\\\\\/\\/g; # XXX I'm actually guessing here, someone point me + # # XXX to docs please + $actString->[$line] = '' unless defined $actString->[$line]; + + # XXX how can I check whether the content still fits in the bar? this would + # XXX allow awlstatus 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); + } + } + + if ($SCREEN_MODE) { + while ($line <= ($columns * $height)) { + print STDERR $screenansi->dcs(). + $terminfo->cup(($line-1) % $height+1, $width + ( + abs($block) * int(($line-1) / $height))). + $terminfo->el(). + $screenansi->st(); + $line++; + } + print STDERR $screenansi->dcs(). + $terminfo->rc(). + $terminfo->cnorm(). + $screenansi->st(); + } + else { + # 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 + my $x = $actString->[$p]; # properly, etc. + $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 awlHasChanged () { + $globTime = undef; + my $temp = ($SCREEN_MODE ? + "\\\n" . Irssi::settings_get_int(set 'block'). + Irssi::settings_get_int(set 'height_adjust') + : "!\n" . Irssi::settings_get_str(set 'placement'). + Irssi::settings_get_int(set 'position')). + Irssi::settings_get_str(set 'automode'); + if ($temp ne $resetNeeded) { wlreset(); return; } + #Irssi::print("awl has changed, calls to remake so far: $callcount"); + $needRemake = 1; + + #remake(); + if ( + ($SCREEN_MODE and !$DISABLE_SCREEN_TEMP) + or + ($needRemake and Irssi::settings_get_bool(set 'all_disable')) + or + (!Irssi::settings_get_bool(set 'all_disable') and $currentLines < 1) + ) { + $needRemake = undef; + remake(); + } + + unless ($SCREEN_MODE) { + # XXX Irssi crashes if I try to do this without timer, why ? What's the minimum + # XXX delay I need to use in the timer ? + Irssi::timeout_add_once(100, 'syncLines', undef); + + for (keys %statusbars) { + Irssi::statusbar_items_redraw(set$_); + } + } + else { + Irssi::timeout_add_once(100, 'syncColumns', undef); + } +} + +sub eventChanged () { # Implement a change queue/blocker -.-) + if (defined $globTime) { + Irssi::timeout_remove($globTime); + } # delay the update further + $globTime = Irssi::timeout_add_once(GLOB_QUEUE_TIMER, 'awlHasChanged', undef); +} + +# }}} + + +# {{{ screen mode + +sub screenFullRedraw { + my ($window) = @_; + if (!ref $window or $window->{'refnum'} == Irssi::active_win->{'refnum'}) { + $actString = []; + eventChanged(); + } +} + +sub screenSize { # from nicklist.pl + $screenResizing = 1; + # fit screen + system 'screen -x '.$ENV{'STY'}.' -X fit'; + # get size + my ($row, $col) = split ' ', `stty size`; + # set screen width + $screenWidth = $col-1; + $screenHeight = $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(100, sub { + my ($new_irssi_width) = @_; + $new_irssi_width -= abs(Irssi::settings_get_int(set + 'block'))*$currentColumns - 1; + 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(10,sub {$screenResizing = 0; screenFullRedraw()}, []); + }, $screenWidth); +} + +sub screenOff { + my ($unloadMode) = @_; + Irssi::signal_remove('gui print text finished' => 'screenFullRedraw'); + Irssi::signal_remove('gui page scrolled' => 'screenFullRedraw'); + Irssi::signal_remove('window changed' => 'screenFullRedraw'); + Irssi::signal_remove('window changed automatic' => 'screenFullRedraw'); + if ($unloadMode) { + Irssi::signal_remove('terminal resized' => 'resizeTerm'); + } + system 'screen -x '.$ENV{'STY'}.' -X fit'; +} + +sub syncColumns { + return if (@$actString == 0); + my $temp = $currentColumns; + #Irssi::print("current columns $temp"); + my $height = $screenHeight - abs(Irssi::settings_get_int(set + 'height_adjust')); + $currentColumns = int(($#$actString-1) / $height) + 1; + #Irssi::print("objects in actstring:".scalar(@$actString).", screen height:". + # $height); + my $currMaxColumns = Irssi::settings_get_int(set 'columns'); + if ($currMaxColumns > 0 and $currentColumns > $currMaxColumns) { + $currentColumns = $currMaxColumns; + } + elsif ($currMaxColumns < 0) { + $currentColumns = abs($currMaxColumns); + } + return if ($temp == $currentColumns); + screenSize(); +} + +#$needRemake = 1; +sub resizeTerm () { + if ($SCREEN_MODE and !$screenResizing) { + $screenResizing = 1; + Irssi::timeout_add_once(10, 'screenSize', undef); + } + Irssi::timeout_add_once(100, 'eventChanged', undef); +} + +# }}} + + +# {{{ settings add + +Irssi::settings_add_str(setc, set 'display_nokey', '[$N]$H$C$S'); +Irssi::settings_add_str(setc, set 'display_key', '[$Q=$N]$H$C$S'); +Irssi::settings_add_str(setc, set 'display_nokey_active', ''); +Irssi::settings_add_str(setc, set 'display_key_active', ''); +Irssi::settings_add_str(setc, set 'separator', "\\ "); +Irssi::settings_add_bool(setc, set 'prefer_name', 0); +Irssi::settings_add_int(setc, set 'hide_data', 0); +Irssi::settings_add_int(setc, set 'maxlines', 9); +Irssi::settings_add_int(setc, set 'columns', 1); +Irssi::settings_add_int(setc, set 'block', 20); +Irssi::settings_add_bool(setc, set 'sbar_maxlength', 0); +Irssi::settings_add_int(setc, set 'height_adjust', 2); +Irssi::settings_add_str(setc, set 'sort', 'refnum'); +Irssi::settings_add_str(setc, set 'placement', 'bottom'); +Irssi::settings_add_int(setc, set 'position', 0); +Irssi::settings_add_bool(setc, set 'all_disable', 0); +Irssi::settings_add_str(setc, set 'automode', 'sbar'); + +# }}} + + +# {{{ init + +sub wlreset { + $actString = []; + $currentLines = 0; # 1; # mhmmmm .. we actually enable one line down there so + # let's try this. + #update_keymap(); + killOldStatus(); + # Register statusbar + #add_statusbar(0); + #Irssi::command('statusbar wl0 enable'); + my $was_screen_mode = $SCREEN_MODE; + if ($SCREEN_MODE = (Irssi::settings_get_str(set 'automode') =~ /screen/i) + and + !$was_screen_mode + ) { + if (!defined $ENV{'STY'}) { + Irssi::print('Screen mode can only be used in GNU screen but no '. + 'screen was found.', MSGLEVEL_CLIENTERROR); + $SCREEN_MODE = undef; + } + else { + Irssi::signal_add_last('gui print text finished' => 'screenFullRedraw'); + Irssi::signal_add_last('gui page scrolled' => 'screenFullRedraw'); + Irssi::signal_add('window changed' => 'screenFullRedraw'); + Irssi::signal_add('window changed automatic' => 'screenFullRedraw'); + } + } + elsif ($was_screen_mode and !$SCREEN_MODE) { + screenOff(); + } + $resetNeeded = ($SCREEN_MODE ? + "\\\n" . Irssi::settings_get_int(set 'block'). + Irssi::settings_get_int(set 'height_adjust') + : "!\n" . Irssi::settings_get_str(set 'placement'). + Irssi::settings_get_int(set 'position')). + Irssi::settings_get_str(set 'automode'); + resizeTerm(); +} + +wlreset(); + +# }}} + + +# {{{ unload/deinit + +my $Unload; +sub unload ($$$) { + $Unload = 1; + # pretend we didn't do anything ASAP + Irssi::timeout_add_once(10, sub { $Unload = undef; }, undef); +} +# last try to catch a sigsegv +Irssi::signal_add_first('gui exit' => sub { $Unload = undef; }); +sub UNLOAD { + # this might well crash Irssi... try /eval /script unload someotherscript ; + # /quit (= SEGFAULT !) + if ($Unload) { + $actString = ['']; # syncLines(); # XXX Irssi crashes when trying to disable + killOldStatus(); # XXX all statusbars ? + if ($SCREEN_MODE) { + screenOff('unload mode'); + } + } +} + +# }}} + + +# {{{ signals + +sub addPrintTextHook { # update on print text + return if $_[0]->{'level'} == 262144 and $_[0]->{'target'} eq '' + and !defined($_[0]->{'server'}); + if (Irssi::settings_get_str(set '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 + +# }}} + +# {{{ commands + + +sub runsub { + my ($cmd) = @_; + sub { + my ($data, $server, $item) = @_; + Irssi::command_runsub($cmd, $data, $server, $item); + }; +} +Irssi::command_bind( setc() => runsub(setc()) ); +Irssi::command_bind( setc() . ' paste' => runsub(setc() . ' paste') ); +Irssi::command_bind( + setc() . ' paste on' => sub { + return unless $SCREEN_MODE; + my $was_disabled = $DISABLE_SCREEN_TEMP; + $DISABLE_SCREEN_TEMP = 1; + Irssi::print('Paste mode is now ON, '.uc(setc()).' is temporarily '. + 'disabled.'); + if (!$was_disabled) { + $screenResizing = 1; + screenOff(); + } + } +); +Irssi::command_bind( + setc() . ' paste off' => sub { + return unless $SCREEN_MODE; + my $was_disabled = $DISABLE_SCREEN_TEMP; + $DISABLE_SCREEN_TEMP = undef; + Irssi::print('Paste mode is now OFF, '.uc(setc()).' is enabled.'); + if ($was_disabled) { + $SCREEN_MODE = undef; + $screenResizing = 0; + wlreset(); + } + } +); +Irssi::command_bind( + setc() . ' paste toggle' => sub { + if ($DISABLE_SCREEN_TEMP) { + Irssi::command(setc() . ' paste off'); + } + else { + Irssi::command(setc() . ' paste on'); + } + } +); +Irssi::command_bind( + setc() . ' redraw' => sub { + return unless $SCREEN_MODE; + screenFullRedraw(); + } +); + + +# }}} + +# {{{ Algorithm::LCSS module +{ + package Algorithm::Diff; + # Skip to first "=head" line for documentation. + use strict; + + use integer; # see below in _replaceNextLargerWith() for mod to make + # if you don't use this + + # McIlroy-Hunt diff algorithm + # Adapted from the Smalltalk code of Mario I. Wolczko, + # by Ned Konz, perl@bike-nomad.com + # Updates by Tye McQueen, http://perlmonks.org/?node=tye + + # Create a hash that maps each element of $aCollection to the set of + # positions it occupies in $aCollection, restricted to the elements + # within the range of indexes specified by $start and $end. + # The fourth parameter is a subroutine reference that will be called to + # generate a string to use as a key. + # Additional parameters, if any, will be passed to this subroutine. + # + # my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen ); + + sub _withPositionsOfInInterval + { + my $aCollection = shift; # array ref + my $start = shift; + my $end = shift; + my $keyGen = shift; + my %d; + my $index; + for ( $index = $start ; $index <= $end ; $index++ ) + { + my $element = $aCollection->[$index]; + my $key = &$keyGen( $element, @_ ); + if ( exists( $d{$key} ) ) + { + unshift ( @{ $d{$key} }, $index ); + } + else + { + $d{$key} = [$index]; + } + } + return wantarray ? %d : \%d; + } + + # Find the place at which aValue would normally be inserted into the + # array. If that place is already occupied by aValue, do nothing, and + # return undef. If the place does not exist (i.e., it is off the end of + # the array), add it to the end, otherwise replace the element at that + # point with aValue. It is assumed that the array's values are numeric. + # This is where the bulk (75%) of the time is spent in this module, so + # try to make it fast! + + sub _replaceNextLargerWith + { + my ( $array, $aValue, $high ) = @_; + $high ||= $#$array; + + # off the end? + if ( $high == -1 || $aValue > $array->[-1] ) + { + push ( @$array, $aValue ); + return $high + 1; + } + + # binary search for insertion point... + my $low = 0; + my $index; + my $found; + while ( $low <= $high ) + { + $index = ( $high + $low ) / 2; + + # $index = int(( $high + $low ) / 2); # without 'use integer' + $found = $array->[$index]; + + if ( $aValue == $found ) + { + return undef; + } + elsif ( $aValue > $found ) + { + $low = $index + 1; + } + else + { + $high = $index - 1; + } + } + + # now insertion point is in $low. + $array->[$low] = $aValue; # overwrite next larger + return $low; + } + + # This method computes the longest common subsequence in $a and $b. + + # Result is array or ref, whose contents is such that + # $a->[ $i ] == $b->[ $result[ $i ] ] + # foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined. + + # An additional argument may be passed; this is a hash or key generating + # function that should return a string that uniquely identifies the given + # element. It should be the case that if the key is the same, the elements + # will compare the same. If this parameter is undef or missing, the key + # will be the element as a string. + + # By default, comparisons will use "eq" and elements will be turned into keys + # using the default stringizing operator '""'. + + # Additional parameters, if any, will be passed to the key generation + # routine. + + sub _longestCommonSubsequence + { + my $a = shift; # array ref or hash ref + my $b = shift; # array ref or hash ref + my $counting = shift; # scalar + my $keyGen = shift; # code ref + my $compare; # code ref + + if ( ref($a) eq 'HASH' ) + { # prepared hash must be in $b + my $tmp = $b; + $b = $a; + $a = $tmp; + } + + # Check for bogus (non-ref) argument values + if ( !ref($a) || !ref($b) ) + { + my @callerInfo = caller(1); + die 'error: must pass array or hash references to ' . $callerInfo[3]; + } + + # set up code refs + # Note that these are optimized. + if ( !defined($keyGen) ) # optimize for strings + { + $keyGen = sub { $_[0] }; + $compare = sub { my ( $a, $b ) = @_; $a eq $b }; + } + else + { + $compare = sub { + my $a = shift; + my $b = shift; + &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ ); + }; + } + + my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] ); + my ( $prunedCount, $bMatches ) = ( 0, {} ); + + if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us? + { + $bMatches = $b; + } + else + { + my ( $bStart, $bFinish ) = ( 0, $#$b ); + + # First we prune off any common elements at the beginning + while ( $aStart <= $aFinish + and $bStart <= $bFinish + and &$compare( $a->[$aStart], $b->[$bStart], @_ ) ) + { + $matchVector->[ $aStart++ ] = $bStart++; + $prunedCount++; + } + + # now the end + while ( $aStart <= $aFinish + and $bStart <= $bFinish + and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) ) + { + $matchVector->[ $aFinish-- ] = $bFinish--; + $prunedCount++; + } + + # Now compute the equivalence classes of positions of elements + $bMatches = + _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ ); + } + my $thresh = []; + my $links = []; + + my ( $i, $ai, $j, $k ); + for ( $i = $aStart ; $i <= $aFinish ; $i++ ) + { + $ai = &$keyGen( $a->[$i], @_ ); + if ( exists( $bMatches->{$ai} ) ) + { + $k = 0; + for $j ( @{ $bMatches->{$ai} } ) + { + + # optimization: most of the time this will be true + if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j ) + { + $thresh->[$k] = $j; + } + else + { + $k = _replaceNextLargerWith( $thresh, $j, $k ); + } + + # oddly, it's faster to always test this (CPU cache?). + if ( defined($k) ) + { + $links->[$k] = + [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ]; + } + } + } + } + + if (@$thresh) + { + return $prunedCount + @$thresh if $counting; + for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] ) + { + $matchVector->[ $link->[1] ] = $link->[2]; + } + } + elsif ($counting) + { + return $prunedCount; + } + + return wantarray ? @$matchVector : $matchVector; + } + + sub traverse_sequences + { + my $a = shift; # array ref + my $b = shift; # array ref + my $callbacks = shift || {}; + my $keyGen = shift; + my $matchCallback = $callbacks->{'MATCH'} || sub { }; + my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; + my $finishedACallback = $callbacks->{'A_FINISHED'}; + my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; + my $finishedBCallback = $callbacks->{'B_FINISHED'}; + my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); + + # Process all the lines in @$matchVector + my $lastA = $#$a; + my $lastB = $#$b; + my $bi = 0; + my $ai; + + for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ ) + { + my $bLine = $matchVector->[$ai]; + if ( defined($bLine) ) # matched + { + &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine; + &$matchCallback( $ai, $bi++, @_ ); + } + else + { + &$discardACallback( $ai, $bi, @_ ); + } + } + + # The last entry (if any) processed was a match. + # $ai and $bi point just past the last matching lines in their sequences. + + while ( $ai <= $lastA or $bi <= $lastB ) + { + + # last A? + if ( $ai == $lastA + 1 and $bi <= $lastB ) + { + if ( defined($finishedACallback) ) + { + &$finishedACallback( $lastA, @_ ); + $finishedACallback = undef; + } + else + { + &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB; + } + } + + # last B? + if ( $bi == $lastB + 1 and $ai <= $lastA ) + { + if ( defined($finishedBCallback) ) + { + &$finishedBCallback( $lastB, @_ ); + $finishedBCallback = undef; + } + else + { + &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA; + } + } + + &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA; + &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB; + } + + return 1; + } + + sub traverse_balanced + { + my $a = shift; # array ref + my $b = shift; # array ref + my $callbacks = shift || {}; + my $keyGen = shift; + my $matchCallback = $callbacks->{'MATCH'} || sub { }; + my $discardACallback = $callbacks->{'DISCARD_A'} || sub { }; + my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { }; + my $changeCallback = $callbacks->{'CHANGE'}; + my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ ); + + # Process all the lines in match vector + my $lastA = $#$a; + my $lastB = $#$b; + my $bi = 0; + my $ai = 0; + my $ma = -1; + my $mb; + + while (1) + { + + # Find next match indices $ma and $mb + do { + $ma++; + } while( + $ma <= $#$matchVector + && !defined $matchVector->[$ma] + ); + + last if $ma > $#$matchVector; # end of matchVector? + $mb = $matchVector->[$ma]; + + # Proceed with discard a/b or change events until + # next match + while ( $ai < $ma || $bi < $mb ) + { + + if ( $ai < $ma && $bi < $mb ) + { + + # Change + if ( defined $changeCallback ) + { + &$changeCallback( $ai++, $bi++, @_ ); + } + else + { + &$discardACallback( $ai++, $bi, @_ ); + &$discardBCallback( $ai, $bi++, @_ ); + } + } + elsif ( $ai < $ma ) + { + &$discardACallback( $ai++, $bi, @_ ); + } + else + { + + # $bi < $mb + &$discardBCallback( $ai, $bi++, @_ ); + } + } + + # Match + &$matchCallback( $ai++, $bi++, @_ ); + } + + while ( $ai <= $lastA || $bi <= $lastB ) + { + if ( $ai <= $lastA && $bi <= $lastB ) + { + + # Change + if ( defined $changeCallback ) + { + &$changeCallback( $ai++, $bi++, @_ ); + } + else + { + &$discardACallback( $ai++, $bi, @_ ); + &$discardBCallback( $ai, $bi++, @_ ); + } + } + elsif ( $ai <= $lastA ) + { + &$discardACallback( $ai++, $bi, @_ ); + } + else + { + + # $bi <= $lastB + &$discardBCallback( $ai, $bi++, @_ ); + } + } + + return 1; + } + + sub prepare + { + my $a = shift; # array ref + my $keyGen = shift; # code ref + + # set up code ref + $keyGen = sub { $_[0] } unless defined($keyGen); + + return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ ); + } + + sub LCS + { + my $a = shift; # array ref + my $b = shift; # array ref or hash ref + my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ ); + my @retval; + my $i; + for ( $i = 0 ; $i <= $#$matchVector ; $i++ ) + { + if ( defined( $matchVector->[$i] ) ) + { + push ( @retval, $a->[$i] ); + } + } + return wantarray ? @retval : \@retval; + } + + sub LCS_length + { + my $a = shift; # array ref + my $b = shift; # array ref or hash ref + return _longestCommonSubsequence( $a, $b, 1, @_ ); + } + + sub LCSidx + { + my $a= shift @_; + my $b= shift @_; + my $match= _longestCommonSubsequence( $a, $b, 0, @_ ); + my @am= grep defined $match->[$_], 0..$#$match; + my @bm= @{$match}[@am]; + return \@am, \@bm; + } + + sub compact_diff + { + my $a= shift @_; + my $b= shift @_; + my( $am, $bm )= LCSidx( $a, $b, @_ ); + my @cdiff; + my( $ai, $bi )= ( 0, 0 ); + push @cdiff, $ai, $bi; + while( 1 ) { + while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) { + shift @$am; + shift @$bm; + ++$ai, ++$bi; + } + push @cdiff, $ai, $bi; + last if ! @$am; + $ai = $am->[0]; + $bi = $bm->[0]; + push @cdiff, $ai, $bi; + } + push @cdiff, 0+@$a, 0+@$b + if $ai < @$a || $bi < @$b; + return wantarray ? @cdiff : \@cdiff; + } + + sub diff + { + my $a = shift; # array ref + my $b = shift; # array ref + my $retval = []; + my $hunk = []; + my $discard = sub { + push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ]; + }; + my $add = sub { + push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ]; + }; + my $match = sub { + push @$retval, $hunk + if 0 < @$hunk; + $hunk = [] + }; + traverse_sequences( $a, $b, + { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ ); + &$match(); + return wantarray ? @$retval : $retval; + } + + sub sdiff + { + my $a = shift; # array ref + my $b = shift; # array ref + my $retval = []; + my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) }; + my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) }; + my $change = sub { + push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] ); + }; + my $match = sub { + push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] ); + }; + traverse_balanced( + $a, + $b, + { + MATCH => $match, + DISCARD_A => $discard, + DISCARD_B => $add, + CHANGE => $change, + }, + @_ + ); + return wantarray ? @$retval : $retval; + } + + ######################################## + my $Root= __PACKAGE__; + package Algorithm::Diff::_impl; + use strict; + + sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices + # 1 # $me->[1]: Ref to first sequence + # 2 # $me->[2]: Ref to second sequence + sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos + sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items + sub _Base() { 5 } # $me->[_Base]: Added to range's min and max + sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected + sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position + sub _Min() { -2 } # Added to _Off to get min instead of max+1 + + sub Die + { + require Carp; + Carp::confess( @_ ); + } + + sub _ChkPos + { + my( $me )= @_; + return if $me->[_Pos]; + my $meth= ( caller(1) )[3]; + Die( "Called $meth on 'reset' object" ); + } + + sub _ChkSeq + { + my( $me, $seq )= @_; + return $seq + $me->[_Off] + if 1 == $seq || 2 == $seq; + my $meth= ( caller(1) )[3]; + Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" ); + } + + sub getObjPkg + { + my( $us )= @_; + return ref $us if ref $us; + return $us . "::_obj"; + } + + sub new + { + my( $us, $seq1, $seq2, $opts ) = @_; + my @args; + for( $opts->{keyGen} ) { + push @args, $_ if $_; + } + for( $opts->{keyGenArgs} ) { + push @args, @$_ if $_; + } + my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args ); + my $same= 1; + if( 0 == $cdif->[2] && 0 == $cdif->[3] ) { + $same= 0; + splice @$cdif, 0, 2; + } + my @obj= ( $cdif, $seq1, $seq2 ); + $obj[_End] = (1+@$cdif)/2; + $obj[_Same] = $same; + $obj[_Base] = 0; + my $me = bless \@obj, $us->getObjPkg(); + $me->Reset( 0 ); + return $me; + } + + sub Reset + { + my( $me, $pos )= @_; + $pos= int( $pos || 0 ); + $pos += $me->[_End] + if $pos < 0; + $pos= 0 + if $pos < 0 || $me->[_End] <= $pos; + $me->[_Pos]= $pos || !1; + $me->[_Off]= 2*$pos - 1; + return $me; + } + + sub Base + { + my( $me, $base )= @_; + my $oldBase= $me->[_Base]; + $me->[_Base]= 0+$base if defined $base; + return $oldBase; + } + + sub Copy + { + my( $me, $pos, $base )= @_; + my @obj= @$me; + my $you= bless \@obj, ref($me); + $you->Reset( $pos ) if defined $pos; + $you->Base( $base ); + return $you; + } + + sub Next { + my( $me, $steps )= @_; + $steps= 1 if ! defined $steps; + if( $steps ) { + my $pos= $me->[_Pos]; + my $new= $pos + $steps; + $new= 0 if $pos && $new < 0; + $me->Reset( $new ) + } + return $me->[_Pos]; + } + + sub Prev { + my( $me, $steps )= @_; + $steps= 1 if ! defined $steps; + my $pos= $me->Next(-$steps); + $pos -= $me->[_End] if $pos; + return $pos; + } + + sub Diff { + my( $me )= @_; + $me->_ChkPos(); + return 0 if $me->[_Same] == ( 1 & $me->[_Pos] ); + my $ret= 0; + my $off= $me->[_Off]; + for my $seq ( 1, 2 ) { + $ret |= $seq + if $me->[_Idx][ $off + $seq + _Min ] + < $me->[_Idx][ $off + $seq ]; + } + return $ret; + } + + sub Min { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off= $me->_ChkSeq($seq); + $base= $me->[_Base] if !defined $base; + return $base + $me->[_Idx][ $off + _Min ]; + } + + sub Max { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off= $me->_ChkSeq($seq); + $base= $me->[_Base] if !defined $base; + return $base + $me->[_Idx][ $off ] -1; + } + + sub Range { + my( $me, $seq, $base )= @_; + $me->_ChkPos(); + my $off = $me->_ChkSeq($seq); + if( !wantarray ) { + return $me->[_Idx][ $off ] + - $me->[_Idx][ $off + _Min ]; + } + $base= $me->[_Base] if !defined $base; + return ( $base + $me->[_Idx][ $off + _Min ] ) + .. ( $base + $me->[_Idx][ $off ] - 1 ); + } + + sub Items { + my( $me, $seq )= @_; + $me->_ChkPos(); + my $off = $me->_ChkSeq($seq); + if( !wantarray ) { + return $me->[_Idx][ $off ] + - $me->[_Idx][ $off + _Min ]; + } + return + @{$me->[$seq]}[ + $me->[_Idx][ $off + _Min ] + .. ( $me->[_Idx][ $off ] - 1 ) + ]; + } + + sub Same { + my( $me )= @_; + $me->_ChkPos(); + return wantarray ? () : 0 + if $me->[_Same] != ( 1 & $me->[_Pos] ); + return $me->Items(1); + } + + my %getName; + %getName= ( + same => \&Same, + diff => \&Diff, + base => \&Base, + min => \&Min, + max => \&Max, + range=> \&Range, + items=> \&Items, # same thing + ); + + sub Get + { + my $me= shift @_; + $me->_ChkPos(); + my @value; + for my $arg ( @_ ) { + for my $word ( split ' ', $arg ) { + my $meth; + if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/ + || not $meth= $getName{ lc $2 } + ) { + Die( $Root, ", Get: Invalid request ($word)" ); + } + my( $base, $name, $seq )= ( $1, $2, $3 ); + push @value, scalar( + 4 == length($name) + ? $meth->( $me ) + : $meth->( $me, $seq, $base ) + ); + } + } + if( wantarray ) { + return @value; + } elsif( 1 == @value ) { + return $value[0]; + } + Die( 0+@value, " values requested from ", + $Root, "'s Get in scalar context" ); + } + + + my $Obj= getObjPkg($Root); + no strict 'refs'; + + for my $meth ( qw( new getObjPkg ) ) { + *{$Root."::".$meth} = \&{$meth}; + *{$Obj ."::".$meth} = \&{$meth}; + } + for my $meth ( qw( + Next Prev Reset Copy Base Diff + Same Items Range Min Max Get + _ChkPos _ChkSeq + ) ) { + *{$Obj."::".$meth} = \&{$meth}; + } + +}; +{ + package Algorithm::LCSS; + + use strict; + { + no strict 'refs'; + *traverse_sequences = \&Algorithm::Diff::traverse_sequences; + } + + sub _tokenize { [split //, $_[0]] } + + sub CSS { + my $is_array = ref $_[0] eq 'ARRAY' ? 1 : 0; + my ( $seq1, $seq2, @match, $from_match ); + my $i = 0; + if ( $is_array ) { + $seq1 = $_[0]; + $seq2 = $_[1]; + traverse_sequences( $seq1, $seq2, { + MATCH => sub { push @{$match[$i]}, $seq1->[$_[0]]; $from_match = 1 }, + DISCARD_A => sub { do{$i++; $from_match = 0} if $from_match }, + DISCARD_B => sub { do{$i++; $from_match = 0} if $from_match }, + }); + } + else { + $seq1 = _tokenize($_[0]); + $seq2 = _tokenize($_[1]); + traverse_sequences( $seq1, $seq2, { + MATCH => sub { $match[$i] .= $seq1->[$_[0]]; $from_match = 1 }, + DISCARD_A => sub { do{$i++; $from_match = 0} if $from_match }, + DISCARD_B => sub { do{$i++; $from_match = 0} if $from_match }, + }); + } + return \@match; + } + + sub CSS_Sorted { + my $match = CSS(@_); + if ( ref $_[0] eq 'ARRAY' ) { + @$match = map{$_->[0]}sort{$b->[1]<=>$a->[1]}map{[$_,scalar(@$_)]}@$match + } + else { + @$match = map{$_->[0]}sort{$b->[1]<=>$a->[1]}map{[$_,length($_)]}@$match + } + return $match; + } + + sub LCSS { + my $is_array = ref $_[0] eq 'ARRAY' ? 1 : 0; + my $css = CSS(@_); + my $index; + my $length = 0; + if ( $is_array ) { + for( my $i = 0; $i < @$css; $i++ ) { + next unless @{$css->[$i]}>$length; + $index = $i; + $length = @{$css->[$i]}; + } + } + else { + for( my $i = 0; $i < @$css; $i++ ) { + next unless length($css->[$i])>$length; + $index = $i; + $length = length($css->[$i]); + } + } + return $css->[$index]; + } + +}; +# }}} +#{{{ Class::Classless module +{ + package Class::Classless; + use strict; + use vars qw(@ISA); + use Carp; + + @ISA = (); + + ########################################################################### + + @Class::Classless::X::ISA = (); + + ########################################################################### + ########################################################################### + + sub Class::Classless::X::AUTOLOAD { + # This's the big dispatcher. + + my $it = shift @_; + my $m = ($Class::Classless::X::AUTOLOAD =~ m/([^:]+)$/s ) + ? $1 : $Class::Classless::X::AUTOLOAD; + + croak "Can't call Class::Classless methods (like $m) without an object" + unless ref $it; # sanity, basically. + + my $prevstate; + $prevstate = ${shift @_} + if scalar(@_) && defined($_[0]) && + ref($_[0]) eq 'Class::Classless::CALLSTATE::SHIMMY' + ; # A shim! we were called via $callstate->NEXT + + my $no_fail = $prevstate ? $prevstate->[3] : undef; + my $i = $prevstate ? ($prevstate->[1] + 1) : 0; + # where to start scanning + my $lineage; + + # Get the linearization of the ISA tree + if($prevstate) { + $lineage = $prevstate->[2]; + } elsif(defined $it->{'ISA_CACHE'} and ref $it->{'ISA_CACHE'} ){ + $lineage = $it->{'ISA_CACHE'}; + } else { + $lineage = [ &Class::Classless::X::ISA_TREE($it) ]; + } + + # Was: + #my @lineage = + # $prevstate ? @{$prevstate->[2]} + # : &Class::Classless::X::ISA_TREE($it); + # # Get the linearization of the ISA tree + # # ISA-memoization happens in the ISA_TREE function. + + for(; $i < @$lineage; ++$i) { + + if( !defined($no_fail) and exists($lineage->[$i]{'NO_FAIL'}) ) { + $no_fail = ($lineage->[$i]{'NO_FAIL'} || 0); + # so the first NO_FAIL sets it + } + + if( ref($lineage->[$i]{'METHODS'} || 0) # sanity + && exists($lineage->[$i]{'METHODS'}{$m}) + ){ + # We found what we were after. Now see what to do with it. + my $v = $lineage->[$i]{'METHODS'}{$m}; + return $v unless defined $v and ref $v; + + if(ref($v) eq 'CODE') { # normal case, I expect! + # Used to have copying of the arglist here. + # But it was apparently useless, so I deleted it + unshift @_, + $it, # $_[0] -- target object + # a NEW callstate + bless([$m, $i, $lineage, $no_fail, $prevstate ? 1 : 0], + 'Class::Classless::CALLSTATE' + ), # $_[1] -- the callstate + ; + goto &{ $v }; # yes, magic goto! bimskalabim! + } + return @$v if ref($v) eq '_deref_array'; + return $$v if ref($v) eq '_deref_scalar'; + return $v; # fallthru + } + } + + if($m eq 'DESTROY') { # mitigate DESTROY-lookup failure at global destruction + # should be impossible + } else { + if($no_fail || 0) { + return; + } + croak "Can't find ", $prevstate ? 'NEXT method' : 'method', + " $m in ", $it->{'NAME'} || $it, + " or any ancestors\n"; + } + } + + ########################################################################### + ########################################################################### + + sub Class::Classless::X::DESTROY { + # noop + } + + ########################################################################### + sub Class::Classless::X::ISA_TREE { + # The linearizer! + # Returns the search path for $_[0], starting with $_[0] + # Possibly memoized. + + # I stopped being able to understand this algorithm about five + # minutes after I wrote it. + use strict; + + my $set_cache = 0; # flag to set the cache on the way out + + if(exists($_[0]{'ISA_CACHE'})) { + return @{$_[0]{'ISA_CACHE'}} + if defined $_[0]{'ISA_CACHE'} + and ref $_[0]{'ISA_CACHE'}; + + # Otherwise, if exists but is not a ref, it's a signal that it should + # be replaced at the earliest, with a listref + $set_cache = 1; + } + + my $has_mi = 0; # set to 0 on the first node we see with 2 parents! + # First, just figure out what's in the tree. + my %last_child = ($_[0] => 1); # as if already seen + + # if $last_child{$x} == $y, that means: + # 1) incidentally, we've passed the node $x before. + # 2) $x is the last child of $y, + # so that means that $y can be pushed to the stack only after + # we've pushed $x to the stack. + + my @tree_nodes; + { + my $current; + my @in_stack = ($_[0]); + while(@in_stack) { + next unless + defined($current = shift @in_stack) + && ref($current) # sanity + && ref($current->{'PARENTS'} || 0) # sanity + ; + + push @tree_nodes, $current; + + $has_mi = 1 if @{$current->{'PARENTS'}} > 1; + unshift + @in_stack, + map { + if(exists $last_child{$_}) { # seen before! + $last_child{$_} = $current; + (); # seen -- don't re-explore + } else { # first time seen + $last_child{$_} = $current; + $_; # first time seen -- explore now + } + } + @{$current->{'PARENTS'}} + ; + } + + # If there was no MI, then that first scan was sufficient. + unless($has_mi) { + $_[0]{'ISA_CACHE'} = \@tree_nodes if $set_cache; + return @tree_nodes; + } + + # Otherwise, toss this list and rescan, consulting %last_child + } + + # $last_child{$parent} holds the last (or only) child of $parent + # in this tree. When walking the tree this time, only that + # child is authorized to put its parent on the @in_stack. + # And that's the only way a node can get added to @in_stack, + # except for $_[0] (the start node) being there at the beginning. + + # Now, walk again, but this time exploring parents the LAST + # time seen in the tree, not the first. + + my @out; + { + my $current; + my @in_stack = ($_[0]); + while(@in_stack) { + next unless defined($current = shift @in_stack) && ref($current); + push @out, $current; # finally. + unshift + @in_stack, + grep( + ( + defined($_) # sanity + && ref($_) # sanity + && $last_child{$_} eq $current, + ), + # I'm lastborn (or onlyborn) of this parent + # so it's OK to explore now + @{$current->{'PARENTS'}} + ) + if ref($current->{'PARENTS'} || 0) # sanity + ; + } + + unless(scalar(@out) == scalar(keys(%last_child))) { + # the counts should be equal + my %good_ones; + @good_ones{@out} = (); + croak + "ISA tree for " . + ($_[0]{'NAME'} || $_[0]) . + " is apparently cyclic, probably involving the nodes " . + nodelist( grep { ref($_) && !exists $good_ones{$_} } + values(%last_child) ) + . "\n"; + } + } + #print "Contents of out: ", nodelist(@out), "\n"; + + $_[0]{'ISA_CACHE'} = \@out if $set_cache; + return @out; + } + + ########################################################################### + + sub Class::Classless::X::can { # NOT like UNIVERSAL::can ... + # return 1 if $it is capable of the method given -- otherwise 0 + my($it, $m) = @_[0,1]; + return undef unless ref $it; + + croak "undef is not a valid method name" unless defined($m); + croak "null-string is not a valid method name" unless length($m); + + foreach my $o (&Class::Classless::X::ISA_TREE($it)) { + return 1 + if ref($o->{'METHODS'} || 0) # sanity + && exists $o->{'METHODS'}{$m}; + } + + return 0; + } + + + ########################################################################### + + sub Class::Classless::X::isa { # Like UNIVERSAL::isa + # Returns true for $X->isa($Y) iff $Y is $X or is an ancestor of $X. + + return unless ref($_[0]) && ref($_[1]); + return scalar(grep {$_ eq $_[1]} &Class::Classless::X::ISA_TREE($_[0])); + } + + ########################################################################### + + sub nodelist { join ', ', map { "" . ($_->{'NAME'} || $_) . ""} @_ } + + ########################################################################### + ########################################################################### + ########################################################################### + # Methods for the CALLSTATE class. + # Basically, CALLSTATE objects represent the state of the dispatcher, + # frozen at the moment when the method call was dispatched to the + # appropriate sub. + # In the grand scheme of things, this needn't be a class -- I could + # have just made the callstate data-object be a hash with documented + # keys, or a closure that responded to only certain parameters, + # etc. But I like it this way. And I like being able to say simply + # $cs->NEXT + # Yes, these are a bit cryptically written, but it's behoovy for + # them to be very very efficient. + + @Class::Classless::ISA = (); + sub Class::Classless::CALLSTATE::found_name { $_[0][0] } + # the method name called and found + sub Class::Classless::CALLSTATE::found_depth { $_[0][1] } + # my depth in the lineage + sub Class::Classless::CALLSTATE::lineage { @{$_[0][2]} } + # my lineage + sub Class::Classless::CALLSTATE::target { $_[0][2][ 0 ] } + # the object that's the target -- same as $_[0] for the method called + sub Class::Classless::CALLSTATE::home { $_[0][2][ $_[0][1] ] } + # the object I was found in + sub Class::Classless::CALLSTATE::sub_found { + $_[0][2][ $_[0][1] ]{'METHODS'}{ $_[0][0] } + } # the routine called + + sub Class::Classless::CALLSTATE::no_fail { $_[0][3] } + sub Class::Classless::CALLSTATE::set_no_fail_true { $_[0][3] = 1 } + sub Class::Classless::CALLSTATE::set_fail_false { $_[0][3] = 0 } + sub Class::Classless::CALLSTATE::set_fail_undef { $_[0][3] = undef } + + sub Class::Classless::CALLSTATE::via_next { $_[0][4] } + + sub Class::Classless::CALLSTATE::NEXT { + #croak "NEXT needs at least one argument: \$cs->NEXT('method'...)" + # unless @_ > 1; + # no longer true. + my $cs = shift @_; + my $m = shift @_; # which may be (or come out) undef... + $m = $cs->[0] unless defined $m; # the method name called and found + + ($cs->[2][0])->$m( + bless( \$cs, 'Class::Classless::CALLSTATE::SHIMMY' ), + @_ + ); + } + + ########################################################################### +}; +#}}} + +############### +### +# +# {{{ *** C h a n g e l o g *** +# +# 0.6ca +# - add screen support (from nicklist.pl) +# - rename to adv_windowlist.pl (advanced window list) since it isn't just a +# window list status bar (wlstat) anymore +# - names can now have a max length and window names can be used +# - fixed a bug with block display in screen mode and statusbar mode +# - added space handling to ir_fe and removed it again +# - now handling formats on my own +# - added warning about missing sb_act_none abstract leading to +# - display*active settings +# - added warning about the bug in awl_display_(no)key_active settings +# +# 0.5d +# - add setting to also hide the last statusbar if empty (awl_all_disable) +# - reverted to old utf8 code to also calculate broken utf8 length correctly +# - simplified dealing with statusbars in wlreset +# - added a little tweak for the renamed term_type somewhere after Irssi 0.8.9 +# - fixed bug in handling channel #$$ +# - typo on line 200 spotted by f0rked +# - reset background colour at the beginning of an entry +# +# 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 awl_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 +# }}} +# vim: se fdm=marker tw=80 : diff --git a/irssi/scripts/nickcolor.pl b/irssi/scripts/nickcolor.pl new file mode 100755 index 0000000..394f055 --- /dev/null +++ b/irssi/scripts/nickcolor.pl @@ -0,0 +1,145 @@ +use strict; +use Irssi 20020101.0250 (); +use vars qw($VERSION %IRSSI); +$VERSION = "2"; +%IRSSI = ( + authors => "Timo Sirainen, Ian Peters, David Leadbeater", + contact => "tss\@iki.fi", + name => "Nick Color", + description => "assign a different color for each nick", + license => "Public Domain", + url => "http://irssi.org/", + changed => "Sun 15 Jun 19:10:44 BST 2014", +); + +# Settings: +# nickcolor_colors: List of color codes to use. +# e.g. /set nickcolor_colors 2 3 4 5 6 7 9 10 11 12 13 +# (avoid 8, as used for hilights in the default theme). + +my %saved_colors; +my %session_colors = {}; + +sub load_colors { + open my $color_fh, "<", "$ENV{HOME}/.irssi/saved_colors"; + while (<$color_fh>) { + chomp; + my($nick, $color) = split ":"; + $saved_colors{$nick} = $color; + } +} + +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; + } + + my @colors = split / /, Irssi::settings_get_str('nickcolor_colors'); + $counter = $colors[$counter % @colors]; + + return $counter; +} + +sub sig_public { + my ($server, $msg, $nick, $address, $target) = @_; + + # 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 = sprintf "\003%02d", $color; + $server->command('/^format pubmsg {pubmsgnick $2 {pubnick '.chr(3).$color.'$[-10]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 (save/set/clear/list/preview)"); + } 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::settings_add_str('misc', 'nickcolor_colors', '2 3 4 5 6 7 9 10 11 12 13'); +Irssi::command_bind('color', 'cmd_color'); + +Irssi::signal_add('message public', 'sig_public'); +Irssi::signal_add('event nick', 'sig_nick'); diff --git a/irssi/scripts/nicklist.pl b/irssi/scripts/nicklist.pl new file mode 100755 index 0000000..452cc82 --- /dev/null +++ b/irssi/scripts/nicklist.pl @@ -0,0 +1,805 @@ +# This script adds a nicklist to the right of irssi +# for documentation: see http://wouter.coekaerts.be/site/irssi/nicklist + +# Copyright (C) 2002-2007 Wouter Coekaerts +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +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 => '05/05/2007' +); + +sub cmd_help { + print ( < +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 + /SET nicklist_width + /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 + # modeflag => '@', '%', '+', or other mode char + # modepos => number representing the position in which to sort nicks with that mode + # status => (not used yet...) + #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, $check_friends, @prefix_friends); + +sub read_settings { + ($screen_prefix = Irssi::settings_get_str('nicklist_screen_prefix')) =~ s/\\e/\033/g; + + ($prefix_mode{'@'} = Irssi::settings_get_str('nicklist_prefix_mode_op')) =~ s/\\e/\033/g; + ($prefix_mode{'%'} = Irssi::settings_get_str('nicklist_prefix_mode_halfop')) =~ s/\\e/\033/g; + ($prefix_mode{'+'} = Irssi::settings_get_str('nicklist_prefix_mode_voice')) =~ s/\\e/\033/g; + ($prefix_mode{' '} = Irssi::settings_get_str('nicklist_prefix_mode_normal')) =~ s/\\e/\033/g; + + (my $prefix_mode_other = Irssi::settings_get_str('nicklist_prefix_mode_other')) =~ s/\\e/\033/g; + foreach my $p (split (/ /, $prefix_mode_other)) { + next if $p eq ''; + if ($p !~ /(.)=(.*)/) { + Irssi::print("Could not parse nicklist_prefix_mode_other part '$p'. Expected space separated list of ="); + last; + } else { + $prefix_mode{$1} = $2; + } + } + + (my $prefix_friends = Irssi::settings_get_str('nicklist_prefix_friends')) =~ s/\\e/\033/g; + foreach my $p (split (/ /, $prefix_friends)) { + next if $p eq ''; + if ($p !~ /(.+?)=(.*)/) { + Irssi::print("Could not parse nicklist_prefix_friends part '$p'. Expected space separated list of ="); + last; + } else { + push @prefix_friends, {'flags' => $1, 'prefix' => $2}; + } + } + + $check_friends = ($prefix_friends ne ''); + + 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'; + # we wait a second to make sure the fit command was processed + Irssi::timeout_add_once(1000, \&screen_size_real, []); +} + +sub screen_size_real { + # 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, "+{'host'} + && is_friend($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'}); + + my $flags = get_flags($active_channel->{'server'}->{'chatnet'}, $active_channel->{'name'}, $nick->{'nick'}, $nick->{'host'}); + + my $prefix; + foreach my $prefix_friend (@prefix_friends) { + if ($prefix_friend->{'flags'} eq 'noflag') { + if ($flags eq '') { + $prefix = $prefix_friend->{'prefix'}; + last; + } + } elsif (check_modes($flags, $prefix_friend->{'flags'})) { + $prefix = $prefix_friend->{'prefix'}; + } + } + + return $prefix ? $prefix : ''; +} + +# 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~/; # strip nick if too long + + my $prefix_mode = $prefix_mode{$nick->{'modeflag'}}; + if (! defined($prefix_mode) ) { + $prefix_mode = $nick->{'modeflag'}; + } + + my $prefix_friends = calc_prefix_friends($nick); + + $nick->{'text'} = + $prefix_mode . + $prefix_friends . + $text . + (' ' x ($nicklist_width-length($nick->{'nick'})-1)) . + "\033[m"; # reset + $nick->{'cmp'} = $nick->{'modepos'}.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; + } + } +} + +################### +##### FRIENDS ##### +################### + +# checks if $has_modes is in $need_modes, copied from trigger.pl +sub check_modes { + my ($has_modes, $need_modes) = @_; + my $matches; + my $switch = 1; # if a '-' if found, will be 0 (meaning the modes should not be set) + foreach my $need_mode (split /&/,$need_modes) { + $matches = 0; + foreach my $char (split //,$need_mode) { + if ($char eq '-') { + $switch = 0; + } elsif ($char eq '+') { + $switch = 1; + } elsif ((index($has_modes,$char) != -1) == $switch) { + $matches = 1; + last; + } + } + if (!$matches) { + return 0; + } + } + return 1; +} + +# get someones flags from people.pl or friends(_shasta).pl, copied from trigger.pl +sub get_flags { + my ($chatnet, $channel, $nick, $address) = @_; + my $flags; + no strict 'refs'; + if (defined %{ 'Irssi::Script::people::' }) { + if (defined ($channel)) { + $flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address)); + } else { + $flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address)); + } + $flags = join('',keys(%{$flags})); + } else { + my $shasta; + if (defined %{ 'Irssi::Script::friends_shasta::' }) { + $shasta = 'friends_shasta'; + } elsif (defined &{ 'Irssi::Script::friends::get_idx' }) { + $shasta = 'friends'; + } + if (!$shasta) { + return undef; + } + my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address)); + if ($idx == -1) { + return ''; + } + $flags = (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,undef)); + if ($channel) { + $flags .= (&{ 'Irssi::Script::'.$shasta.'::get_friends_flags' }($idx,$channel)); + } + } + return $flags; +} + +sub is_friend { + my ($chatnet, $channel, $nick, $address) = @_; + no strict 'refs'; + if (defined %{ 'Irssi::Script::people::' }) { + return (() != &{'Irssi::Script::people::find_users'}($chatnet, $nick, $address)); + my $flags; + if (defined ($channel)) { + $flags = (&{ 'Irssi::Script::people::find_local_flags' }($chatnet,$channel,$nick,$address)); + } else { + $flags = (&{ 'Irssi::Script::people::find_global_flags' }($chatnet,$nick,$address)); + } + return ($flags ne ''); # TODO: test this + } else { + my $shasta; + if (defined %{ 'Irssi::Script::friends_shasta::' }) { + $shasta = 'friends_shasta'; + } elsif (defined &{ 'Irssi::Script::friends::get_idx' }) { + $shasta = 'friends'; + } + if (!$shasta) { + return undef; + } + my $idx = (&{ 'Irssi::Script::'.$shasta.'::get_idx' }($nick,$address)); + return ($idx != -1); + } +} + +#################### +##### 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 ### + foreach my $nick ($channel->nicks()) { + my $thisnick = {'nick' => $nick->{'nick'}}; + recalc_nick($thisnick, $nick); + push @nicklist, $thisnick; + } + @nicklist = sort {$a->{'cmp'} cmp $b->{'cmp'}} @nicklist; + } + 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); +} + +# update the mode and cmp of a nick, based on a nickrec from irssi +sub recalc_nick { + my ($nick, $nickrec) = @_; + if (! $nickrec) { + $nickrec = $active_channel->nick_find($nick->{'nick'}); + } + + my $nickflags = $active_channel->{'server'}->get_nick_flags() . ' '; + + my $flag = ( + $nickrec->{'op'} ? '@' : + $nickrec->{'halfop'} ? '%' : + $nickrec->{'voice'} ? '+' : + ' ' + ); + + if ($nickrec->{'other'} && index($nickflags, $nick->{'other'}) < index($nickflags, $flag)) { + $flag = chr($nickrec->{'other'}); + } + + $nick->{'modepos'} = index($nickflags, $flag); + $nick->{'modeflag'} = $flag; + + $nick->{'host'} = $nickrec->{'host'}; + calc_text($nick); +} + +################### +##### 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}; + recalc_nick($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); + recalc_nick($nicklist_item, $nick); + 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_str('nicklist', 'nicklist_prefix_mode_other', '&=\e[31m&\e[39m ~=\e[35m~\e[39m'); +Irssi::settings_add_str('nicklist', 'nicklist_prefix_friends', 'o=\e[32m v=\e[33m noflag=\e[1m'); + +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(); +} diff --git a/irssi/scripts/tmux-nicklist-portable.pl b/irssi/scripts/tmux-nicklist-portable.pl new file mode 100644 index 0000000..e8ae3a4 --- /dev/null +++ b/irssi/scripts/tmux-nicklist-portable.pl @@ -0,0 +1,425 @@ +# based on the nicklist.pl script +################################################################################ +# tmux_nicklist.pl +# This script integrates tmux and irssi to display a list of nicks in a +# vertical right pane with 20% width. Right now theres no configuration +# or setup, simply initialize the script with irssi and by default you +# will get the nicklist for every channel(customize by altering +# the regex in /set nicklist_channel_re) +# +# /set nicklist_channel_re +# * only show on channels matching this regular expression +# +# /set nicklist_max_users +# * only show when the channel has so many users or less (0 = always) +# +# /set nicklist_smallest_main +# * only show when main window is larger than this (0 = always) +# +# /set nicklist_pane_width +# * width of the nicklist pane +# +# /set nicklist_color +# * colourise the nicks in the nicklist (required nickcolor script +# with get_nick_color2 and debug_ansicolour functions) +# +# /set nicklist_gone_sort +# * sort away people below +# +# It supports mouse scrolling and the following keys: +# k/up arrow: up one line +# j/down arrow: down one line +# pageup: up 20 lines +# pagedown: down 20 lines +# gg: go to top +# G: go to bottom +# +# For better integration, unrecognized sequences will be sent to irssi and +# its pane will be focused. +# +# to toggle the nicklist if it is in the way you can make a key binding: +# /bind meta-Z /script exec Irssi::Script::tmux_nicklist_portable::toggle_nicklist +################################################################################ + +use strict; +use warnings; +use IO::Handle; +use IO::Select; +use POSIX; +use File::Temp qw/ :mktemp /; +use File::Basename; +our $VERSION = '0.1.6'; # aea329934f6a48c +our %IRSSI = ( + authors => 'Thiago de Arruda', + contact => 'tpadilha84@gmail.com', + name => 'tmux-nicklist', + description => 'displays a list of nicks in a separate tmux pane', + license => 'WTFPL', +); + +# "other" prefixes by danielg4 + +{ package Irssi::Nick } + +if ($#ARGV == -1) { +require Irssi; + +my $enabled = 0; +my $nicklist_toggle = 1; +my $script_path = __FILE__; +my $tmpdir; +my $fifo_path; +my $fifo; +my $just_launched; +my $resize_timer; + +sub enable_nicklist { + return if ($enabled); + $tmpdir = mkdtemp Irssi::get_irssi_dir()."/nicklist-XXXXXXXX"; + $fifo_path = "$tmpdir/fifo"; + POSIX::mkfifo($fifo_path, 0600) or die "can't mkfifo $fifo_path: $!"; + my $cmd = "perl $script_path $fifo_path $ENV{TMUX_PANE}"; + my $width = Irssi::settings_get_int('nicklist_pane_width'); + system('tmux', 'split-window', '-dh', '-l', $width, '-t', $ENV{TMUX_PANE}, $cmd); + open_fifo(); + Irssi::timeout_remove($just_launched) if defined $just_launched; + $just_launched = Irssi::timeout_add_once(300, sub { $just_launched = undef; }, ''); +} + +sub open_fifo { + # The next system call will block until the other pane has opened the pipe + # for reading, so synchronization is not an issue here. + open $fifo, ">", $fifo_path or do { + if ($! == 4) { + Irssi::timeout_add_once(300, \&open_fifo, ''); + $enabled = -1 unless $enabled; + return; + } + die "can't open $fifo_path: $!"; + }; + $fifo->autoflush(1); + if ($enabled < -1) { + $enabled = 1; + disable_nicklist(); + } elsif ($enabled == -1) { + $enabled = 1; + reset_nicklist("enabled"); + } else { + $enabled = 1; + } +} + +sub disable_nicklist { + return unless ($enabled); + if ($enabled > 0) { + print $fifo "EXIT\n"; + close $fifo; + $fifo = undef; + unlink $fifo_path; + rmdir $tmpdir; + } + $enabled--; +} + +sub reset_nicklist { + my $event = shift; + my $active = Irssi::active_win(); + my $channel = $active->{active}; + return disable_nicklist unless $channel && ref $channel; + if ($event =~ /^nick/) { + # check if that nick event is for the current channel/nicklist + my ($event_channel) = @_; + return unless $channel->{_irssi} == $event_channel->{_irssi}; + } + my ($colourer, $ansifier); + if (Irssi::settings_get_bool('nicklist_color')) { + for my $script (sort map { my $z = $_; $z =~ s/::$//; $z } grep { /^nickcolor|nm/ } keys %Irssi::Script::) { + if ($colourer = "Irssi::Script::$script"->can('get_nick_color2')) { + $ansifier = "Irssi::Script::$script"->can('debug_ansicolour'); + last; + } + } + } + my $channel_pattern = Irssi::settings_get_str('nicklist_channel_re'); + { local $@; + $channel_pattern = eval { qr/$channel_pattern/ }; + $channel_pattern = qr/(?!)/ if $@; + } + my $smallest_main = Irssi::settings_get_int('nicklist_smallest_main'); + if (!$nicklist_toggle + || !$channel || !ref($channel) + || !$channel->isa('Irssi::Channel') + || !$channel->{'names_got'} + || $channel->{'name'} !~ $channel_pattern + || ($smallest_main && $channel->window->{width} < $smallest_main)) { + disable_nicklist; + } else { + my %colour; + my @nicks = $channel->nicks(); + my $max_nicks = Irssi::settings_get_int('nicklist_max_users'); + if ($max_nicks && @nicks > $max_nicks) { + disable_nicklist; + } else { + enable_nicklist; + return unless $enabled > 0; + foreach my $nick (sort { $a->{_irssi} <=> $b->{_irssi} } @nicks) { + $colour{$nick->{nick}} = ($ansifier && $colourer) ? $ansifier->($colourer->($active->{active}{server}{tag}, $channel->{name}, $nick->{nick}, 0)) : ''; + } + print($fifo "BEGIN\n"); + my $gone_sort = Irssi::settings_get_bool('nicklist_gone_sort'); + my $prefer_real; + if (exists $Irssi::Script::{'realnames::'}) { + my $code = "Irssi::Script::realnames"->can('use_realnames'); + $prefer_real = $code && $code->($channel); + } + my $_real = sub { + my $nick = shift; + $prefer_real && length $nick->{'realname'} ? $nick->{'realname'} : $nick->{'nick'} + }; + foreach my $nick (sort {($a->{'op'}?'1':$a->{'halfop'}?'2':$a->{'voice'}?'3':$a->{'other'}>32?'0':'4').($gone_sort?($a->{'gone'}?1:0):'').lc($_real->($a)) + cmp ($b->{'op'}?'1':$b->{'halfop'}?'2':$b->{'voice'}?'3':$b->{'other'}>32?'0':'4').($gone_sort?($b->{'gone'}?1:0):'').lc($_real->($b))} @nicks) { + my $colour = $colour{$nick->{nick}} || "\e[39m"; + $colour = "\e[37m" if $nick->{'gone'}; + print($fifo "NICK"); + if ($nick->{'op'}) { + print($fifo "\e[32m\@$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'halfop'}) { + print($fifo "\e[34m%$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'voice'}) { + print($fifo "\e[33m+$colour".$_real->($nick)."\e[39m"); + } elsif ($nick->{'other'}>32) { + print($fifo "\e[31m".(chr $nick->{'other'})."$colour".$_real->($nick)."\e[39m"); + } else { + print($fifo " $colour".$_real->($nick)."\e[39m"); + } + print($fifo "\n"); + } + print($fifo "END\n"); + } + } +} + +sub toggle_nicklist { + if ($enabled) { + $nicklist_toggle = undef + } else { + $nicklist_toggle = 1; + } + reset_nicklist("toggle"); +} + +sub switch_channel { + print $fifo "SWITCH_CHANNEL\n" if $fifo; + &reset_nicklist; +} + +sub resized_timed { + Irssi::timeout_remove($resize_timer) if defined $resize_timer; + return if defined $just_launched; + $resize_timer = Irssi::timeout_add_once(1100, \&resized, ''); + #resized(); +} +sub resized { + $resize_timer = undef; + return if defined $just_launched; + return unless $enabled >= 0; + disable_nicklist; + Irssi::timeout_add_once(200, sub{reset_nicklist("terminal resized")}, ''); +} +sub UNLOAD { + disable_nicklist; +} + +Irssi::settings_add_str('tmux_nicklist', 'nicklist_channel_re', '.*'); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_max_users', 0); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_smallest_main', 0); +Irssi::settings_add_int('tmux_nicklist', 'nicklist_pane_width', 13); +Irssi::settings_add_bool('tmux_nicklist', 'nicklist_color', 1); +Irssi::settings_add_bool('tmux_nicklist', 'nicklist_gone_sort', 0); +Irssi::signal_add_last('window item changed', sub{switch_channel("window item changed",@_)}); +Irssi::signal_add_last('window changed', sub{switch_channel("window changed",@_)}); +Irssi::signal_add_last('channel joined', sub{switch_channel("channel joined",@_)}); +Irssi::signal_add('nicklist new', sub{reset_nicklist("nicklist new",@_)}); +Irssi::signal_add('nicklist remove', sub{reset_nicklist("nicklist remove",@_)}); +Irssi::signal_add('nicklist changed', sub{reset_nicklist("nicklist changed",@_)}); +Irssi::signal_add_first('nick mode changed', sub{reset_nicklist("nick mode changed",@_)}); +Irssi::signal_add('gui exit', \&disable_nicklist); +Irssi::signal_add_last('terminal resized', \&resized_timed); + +} else { +my $fifo_path = $ARGV[0]; +my $irssi_pane = $ARGV[1]; +# array to store the current channel nicknames +my @nicknames = (); + +# helper functions for manipulating the terminal +# escape sequences taken from +# http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html +sub enable_mouse { print "\e[?1000h"; } +# recognized sequences +my $MOUSE_SCROLL_DOWN="\e[Ma"; +my $MOUSE_SCROLL_UP="\e[M`"; +my $ARROW_DOWN="\e[B"; +my $ARROW_UP="\e[A"; +my $UP="k"; +my $DOWN="j"; +my $PAGE_DOWN="\e[6~"; +my $PAGE_UP="\e[5~"; +my $GO_TOP="gg"; +my $GO_BOTTOM="G"; + +my $current_line = 0; +my $sequence = ''; +my ($rows, $cols); + +sub term_size { + split ' ', `stty size`; +} + +sub redraw { + my $last_nick_idx = @nicknames; + my $last_idx = $current_line + $rows; + # normalize last visible index + if ($last_idx > ($last_nick_idx)) { + $last_idx = $last_nick_idx; + } + # redraw visible nicks + for my $i (reverse 1..$rows) { + print "\e[$i;1H\e[K"; + my $idx = $current_line + $i - 1; + if ($idx < $last_idx) { + my $z = 0; my $col = $cols; + for (split /(\e\[(?:\d|;|:|\?|\s)*.)/, $nicknames[$idx]) { + if ($z ^= 1) { + print +(substr $_, 0, $col) if $col > 0; + $col -= length; + } else { + print + } + } + } + } +} + +sub move_down { + $sequence = ''; + my $count = int $_[0]; + my $nickcount = $#nicknames; + return if ($nickcount <= $rows); + if ($count == -1) { + $current_line = $nickcount - $rows - 1; + redraw; + return; + } + my $visible = $nickcount - $current_line - $count; + if ($visible > $rows) { + $current_line += $count; + redraw; + } elsif (($visible + $count) > $rows) { + # scroll the maximum we can + $current_line = $nickcount - $rows - 1; + redraw; + } +} + +sub move_up { + $sequence = ''; + my $count = int $_[0]; + if ($count == -1) { + $current_line = 0; + redraw; + return; + } + return if ($current_line == 0); + $count = 1 if $count == 0; + $current_line -= $count; + $current_line = 0 if $current_line < 0; + redraw; +} + +$SIG{INT} = 'IGNORE'; + +STDOUT->autoflush(1); +# setup terminal so we can listen for individual key presses without echo +`stty -icanon -echo`; + +# open named pipe and setup the 'select' wrapper object for listening on both +# fds(fifo and sdtin) +open my $fifo, "<", $fifo_path or die "can't open $fifo_path: $!"; +my $select = IO::Select->new(); +my @ready; +$select->add($fifo); +$select->add(\*STDIN); + +enable_mouse; +system('tput', 'smcup'); +print "\e[?7l"; #system('tput', 'rmam'); +system('tput', 'civis'); +MAIN: { + while (@ready = $select->can_read) { + foreach my $fd (@ready) { + ($rows, $cols) = term_size; + if ($fd == $fifo) { + while (<$fifo>) { + my $line = $_; + if ($line =~ /^BEGIN/) { + @nicknames = (); + } elsif ($line =~ /^SWITCH_CHANNEL/) { + $current_line = 0; + } elsif ($line =~ /^NICK(.+)$/) { + push @nicknames, $1; + } elsif ($line =~ /^END$/) { + redraw; + last; + } elsif ($line =~ /^EXIT$/) { + last MAIN; + } + } + } else { + my $key = ''; + sysread(STDIN, $key, 1); + $sequence .= $key; + if ($MOUSE_SCROLL_DOWN =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_DOWN eq $sequence) { + move_down 3; + # mouse scroll has two more bytes that I dont use here + # so consume them now to avoid sending unwanted bytes to + # irssi + sysread(STDIN, $key, 2); + } + } elsif ($MOUSE_SCROLL_UP =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_UP eq $sequence) { + move_up 3; + sysread(STDIN, $key, 2); + } + } elsif ($ARROW_DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($ARROW_DOWN eq $sequence); + } elsif ($ARROW_UP =~ /^\Q$sequence\E/) { + move_up 1 if ($ARROW_UP eq $sequence); + } elsif ($DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($DOWN eq $sequence); + } elsif ($UP =~ /^\Q$sequence\E/) { + move_up 1 if ($UP eq $sequence); + } elsif ($PAGE_DOWN =~ /^\Q$sequence\E/) { + move_down $rows/2 if ($PAGE_DOWN eq $sequence); + } elsif ($PAGE_UP =~ /^\Q$sequence\E/) { + move_up $rows/2 if ($PAGE_UP eq $sequence); + } elsif ($GO_BOTTOM =~ /^\Q$sequence\E/) { + move_down -1 if ($GO_BOTTOM eq $sequence); + } elsif ($GO_TOP =~ /^\Q$sequence\E/) { + move_up -1 if ($GO_TOP eq $sequence); + } else { + # Unrecognized sequences will be send to irssi and its pane + # will be focused + system('tmux', 'send-keys', '-t', $irssi_pane, $sequence); + system('tmux', 'select-pane', '-t', $irssi_pane); + $sequence = ''; + } + } + } + } +} + +close $fifo; + +} diff --git a/irssi/scripts/tmux-nicklist.pl b/irssi/scripts/tmux-nicklist.pl new file mode 100644 index 0000000..8cbb550 --- /dev/null +++ b/irssi/scripts/tmux-nicklist.pl @@ -0,0 +1,304 @@ +# based on the nicklist.pl script +################################################################################ +# tmux_nicklist.pl +# This script integrates tmux and irssi to display a list of nicks in a +# vertical right pane with 20% width. Right now theres no configuration +# or setup, simply initialize the script with irssi and by default you +# will get the nicklist for every channel(customize by altering +# the regex in '$channel_pattern'. +# +# It supports mouse scrolling and the following keys: +# k/up arrow: up one line +# j/down arrow: down one line +# pageup: up 20 lines +# pagedown: down 20 lines +# gg: go to top +# G: go to bottom +# +# For better integration, unrecognized sequences will be sent to irssi and +# its pane will be focused. +################################################################################ + +use strict; +use IO::Handle; +use IO::Select; +use POSIX; +use File::Temp qw/ :mktemp /; +use File::Basename; +use vars qw($VERSION %IRSSI); +$VERSION = '0.0.0'; +%IRSSI = ( + authors => 'Thiago de Arruda', + contact => 'tpadilha84@gmail.com', + name => 'tmux-nicklist', + description => 'displays a list of nicks in a separate tmux pane', + license => 'WTFPL', +); + +if ($#ARGV == -1) { +require Irssi; + +my $enabled = 0; +my $script_path = __FILE__; +my $tmpdir; +my $fifo_path; +# my $channel_pattern = '^&bitlbee$'; +my $channel_pattern = '^.+$'; + +sub enable_nicklist { + return if ($enabled); + $tmpdir = mkdtemp "/tmp/nicklist-XXXXXXXX"; + $fifo_path = "$tmpdir/fifo"; + POSIX::mkfifo($fifo_path, 0600) or die "can\'t mkfifo $fifo_path: $!"; + my $cmd = "perl $script_path $fifo_path $ENV{'TMUX_PANE'}"; + system('tmux', 'split-window', '-dh', '-p', '20', $cmd); + # The next system call will block until the other pane has opened the pipe + # for reading, so synchronization is not an issue here. + open (FIFO, "> $fifo_path") or die "can't open $fifo_path: $!"; + FIFO->autoflush(1); + $enabled = 1; +} + +sub disable_nicklist { + return unless ($enabled); + print(FIFO "EXIT\n"); + close FIFO; + unlink $fifo_path; + rmdir $tmpdir; + $enabled = 0; +} + +sub reset_nicklist { + my $active = Irssi::active_win(); + my $channel = $active->{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'})) || + ($channel->{'name'} !~ /$channel_pattern/ )) { + disable_nicklist; + } else { + enable_nicklist; + print(FIFO "BEGIN\n"); + 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()) { + print(FIFO "NICK"); + if ($nick->{'op'}) { + print(FIFO "\e[32m\@$nick->{'nick'}\e[39m"); + } elsif ($nick->{'halfop'}) { + print(FIFO "\e[34m%$nick->{'nick'}\e[39m"); + } elsif ($nick->{'voice'}) { + print(FIFO "\e[33m+$nick->{'nick'}\e[39m"); + } else { + print(FIFO " $nick->{'nick'}"); + } + print(FIFO "\n"); + } + print(FIFO "END\n"); + } +} + +sub switch_channel { + print(FIFO "SWITCH_CHANNEL\n"); + reset_nicklist; +} + +Irssi::signal_add_last('window item changed', \&switch_channel); +Irssi::signal_add_last('window changed', \&switch_channel); +Irssi::signal_add_last('channel wholist', \&reset_nicklist); +# first, to be before ignores +Irssi::signal_add_first('message join', \&reset_nicklist); +Irssi::signal_add_first('message part', \&reset_nicklist); +Irssi::signal_add_first('message kick', \&reset_nicklist); +Irssi::signal_add_first('message quit', \&reset_nicklist); +Irssi::signal_add_first('message nick', \&reset_nicklist); +Irssi::signal_add_first('message own_nick', \&reset_nicklist); +Irssi::signal_add_first('nick mode changed', \&reset_nicklist); +Irssi::signal_add('gui exit', \&disable_nicklist); + +} else { +require 'sys/ioctl.ph'; +# open STDERR, '>', "$ENV{'HOME'}/.nickbar-errors.log"; +my $fifo_path = $ARGV[0]; +my $irssi_pane = $ARGV[1]; +# array to store the current channel nicknames +my @nicknames = (); + +# helper functions for manipulating the terminal +# escape sequences taken from +# http://www.tldp.org/HOWTO/Bash-Prompt-HOWTO/x361.html +sub clear_screen { print "\e[2J"; } +sub save_cursor { print "\e[s"; } +sub restore_cursor { print "\e[u"; } +sub enable_mouse { print "\e[?1000h"; } +# recognized sequences +my $MOUSE_SCROLL_DOWN="\e[Ma"; +my $MOUSE_SCROLL_UP="\e[M`"; +my $ARROW_DOWN="\e[B"; +my $ARROW_UP="\e[A"; +my $UP="k"; +my $DOWN="j"; +my $PAGE_DOWN="\e[6~"; +my $PAGE_UP="\e[5~"; +my $GO_TOP="gg"; +my $GO_BOTTOM="G"; + +my $current_line = 0; +my $sequence = ''; + +sub term_row_count { + # from http://stackoverflow.com/questions/4286158/how-do-i-get-width-and-height-of-my-terminal-with-ioctl + my $terminal_size; + ioctl(STDOUT, TIOCGWINSZ() , $terminal_size); + my ($rows, $cols, $xpix, $ypix) = unpack 'S4', $terminal_size; + return $rows; +} + +sub redraw { + my $rows = term_row_count; + my $last_nick_idx = $#nicknames; + my $last_idx = $current_line + $rows; + # normalize last visible index + if ($last_idx > ($last_nick_idx)) { + $last_idx = $last_nick_idx; + } + # redraw visible nicks + restore_cursor; + clear_screen; + for (my $idx = $current_line; $idx <= $last_idx; $idx++) { + print "$nicknames[$idx]\n"; + } +} + +sub move_down { + $sequence = ''; + my $count = $_[0]; + my $nickcount = $#nicknames; + my $rows = term_row_count; + return if ($nickcount <= $rows); + if ($count == -1) { + $current_line = $nickcount - $rows - 1; + redraw; + return; + } + my $visible = $nickcount - $current_line - $count; + if ($visible > $rows) { + $current_line += $count; + redraw; + } elsif (($visible + $count) > $rows) { + # scroll the maximum we can + $current_line = $nickcount - $rows - 1; + redraw; + } +} + +sub move_up { + $sequence = ''; + my $count = $_[0]; + if ($count == -1) { + $current_line = 0; + redraw; + return; + } + return if ($current_line == 0); + if (($current_line - $count) >= 0) { + $current_line -= $count; + redraw; + } +} + +$SIG{INT} = 'IGNORE'; + +# setup terminal so we can listen for individual key presses without echo +my ($term, $oterm, $echo, $noecho, $fd_stdin); +$fd_stdin = fileno(STDIN); +$term = POSIX::Termios->new(); +$term->getattr($fd_stdin); +$oterm = $term->getlflag(); +$echo = ECHO | ECHOK | ICANON; +$noecho = $oterm & ~$echo; +$term->setlflag($noecho); +$term->setcc(VTIME, 1); +$term->setattr($fd_stdin, TCSANOW); + +# open named pipe and setup the 'select' wrapper object for listening on both +# fds(fifo and sdtin) +open (FIFO, "< $fifo_path") or die "can't open $fifo_path: $!"; +my $fifo = \*FIFO; +my $stdin = \*STDIN; +my $select = IO::Select->new(); +my @ready; +$select->add($fifo); +$select->add($stdin); + +save_cursor; +enable_mouse; +system('tput', 'civis'); +MAIN: { + while (@ready = $select->can_read) { + foreach my $fd (@ready) { + if ($fd == $fifo) { + while (<$fifo>) { + my $line = $_; + if ($line =~ /^BEGIN/) { + @nicknames = (); + } elsif ($line =~ /^SWITCH_CHANNEL/) { + $current_line = 0; + } elsif ($line =~ /^NICK(.+)$/) { + push @nicknames, $1; + } elsif ($line =~ /^END$/) { + redraw; + last; + } elsif ($line =~ /^EXIT$/) { + last MAIN; + } + } + } else { + my $key = ''; + sysread(STDIN, $key, 1); + $sequence .= $key; + if ($MOUSE_SCROLL_DOWN =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_DOWN eq $sequence) { + move_down 3; + # mouse scroll has two more bytes that I dont use here + # so consume them now to avoid sending unwanted bytes to + # irssi + sysread(STDIN, $key, 2); + } + } elsif ($MOUSE_SCROLL_UP =~ /^\Q$sequence\E/) { + if ($MOUSE_SCROLL_UP eq $sequence) { + move_up 3; + sysread(STDIN, $key, 2); + } + } elsif ($ARROW_DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($ARROW_DOWN eq $sequence); + } elsif ($ARROW_UP =~ /^\Q$sequence\E/) { + move_up 1 if ($ARROW_UP eq $sequence); + } elsif ($DOWN =~ /^\Q$sequence\E/) { + move_down 1 if ($DOWN eq $sequence); + } elsif ($UP =~ /^\Q$sequence\E/) { + move_up 1 if ($UP eq $sequence); + } elsif ($PAGE_DOWN =~ /^\Q$sequence\E/) { + move_down 20 if ($PAGE_DOWN eq $sequence); + } elsif ($PAGE_UP =~ /^\Q$sequence\E/) { + move_up 20 if ($PAGE_UP eq $sequence); + } elsif ($GO_BOTTOM =~ /^\Q$sequence\E/) { + move_down -1 if ($GO_BOTTOM eq $sequence); + } elsif ($GO_TOP =~ /^\Q$sequence\E/) { + move_up -1 if ($GO_TOP eq $sequence); + } else { + # Unrecognized sequences will be send to irssi and its pane + # will be focused + system('tmux', 'send-keys', '-l', '-t', $irssi_pane, $sequence); + system('tmux', 'select-pane', '-t', $irssi_pane); + $sequence = ''; + } + } + } + } +} + +close FIFO; + +} diff --git a/irssi/scripts/trackbar.pl b/irssi/scripts/trackbar.pl new file mode 100755 index 0000000..8538381 --- /dev/null +++ b/irssi/scripts/trackbar.pl @@ -0,0 +1,189 @@ +# trackbar.pl +# +# This little script will do just one thing: it will draw a line each time you +# switch away from a window. This way, you always know just upto where you've +# been reading that window :) It also removes the previous drawn line, so you +# don't see double lines. +# +# Usage: +# +# The script works right out of the box, but if you want you can change +# the working by /set'ing the following variables: +# +# trackbar_string The characters to repeat to draw the bar +# trackbar_style The style for the bar, %r is red for example +# See formats.txt that came with irssi +# +# /mark is a command that will redraw the line at the bottom. However! This +# requires irssi version after 20021228. otherwise you'll get the error +# redraw: unknown command, and your screen is all goofed up :) +# +# /upgrade & buf.pl notice: This version tries to remove the trackbars before +# the upgrade is done, so buf.pl does not restore them, as they are not removeable +# afterwards by trackbar. Unfortiounatly, to make this work, trackbar and buf.pl +# need to be loaded in a specific order. Please experiment to see which order works +# for you (strangely, it differs from configuration to configuration, something I will +# try to fix in a next version) +# +# Authors: +# - Main maintainer & author: Peter 'kinlo' Leurs +# - Many thanks to Timo 'cras' Sirainen for placing me on my way +# - on-upgrade-remove-line patch by Uwe Dudenhoeffer +# +# Version history: +# 1.4: - Changed our's by my's so the irssi script header is valid +# - Removed utf-8 support. In theory, the script should work w/o any +# problems for utf-8, just set trackbar_string to a valid utf-8 character +# and everything *should* work. However, this script is being plagued by +# irssi internal bugs. The function Irssi::settings_get_str does NOT handle +# unicode strings properly, hence you will notice problems when setting the bar +# to a unicode char. For changing your bar to utf-8 symbols, read the line sub. +# 1.3: - Upgrade now removes the trackbars. +# - Some code cleanups, other defaults +# - /mark sets the line to the bottom +# 1.2: - Support for utf-8 +# - How the bar looks can now be configured with trackbar_string +# and trackbar_style +# 1.1: - Fixed bug when closing window +# 1.0: - Initial release +# +# +# Call for help! +# +# There is a trackbar version 2.0 that properly handles resizes and immediate config change +# activation. However, there is/are some bug(s) in irssi's main buffer/window code that causes +# irssi to 'forget' lines, which is ofcourse completly unaccepteable. I haven't found the time +# nor do I know the irssi's internals enough to find and fix this bug, if you want to help, please +# contact me, I'll give you a copy of the 2.0 version that will immediatly show you the problems. +# +# Known bugs: +# - if you /clear a window, it will be uncleared when returning to the window +# - UTF-8 characters in the trackbar_string doesnt work. This is an irssi bug. +# - if you resize your irssi (in xterm or so) the bar is not resized +# - changing the trackbar style is only visible after returning to a window +# however, changing style/resize takes in effect after you left the window. +# +# Whishlist/todo: +# - instead of drawing a line, just invert timestamp or something, +# to save a line (but I don't think this is possible with current irssi) +# - some pageup keybinding possibility, to scroll up upto the trackbar +# - <@coekie> kinlo: if i switch to another window, in another split window, i +# want the trackbar to go down in the previouswindow in that splitwindow :) +# - < bob_2> anyway to clear the line once the window is read? +# - < elho> kinlo: wishlist item: a string that gets prepended to the repeating pattern +# - < elho> an option to still have the timestamp in front of the bar +# - < elho> oh and an option to not draw it in the status window :P +# +# BTW: when you have feature requests, mailing a patch that works is the fastest way +# to get it added :p + +use strict; +use 5.6.1; +use Irssi; +use Irssi::TextUI; + +my $VERSION = "1.4"; + +my %IRSSI = ( + authors => "Peter 'kinlo' Leurs", + contact => "peter\@pfoe.be", + name => "trackbar", + description => "Shows a bar where you've last read a window", + license => "GPLv2", + url => "http://www.pfoe.be/~peter/trackbar/", + changed => "Thu Feb 20 16:18:08 2003", +); + +my %config; + +Irssi::settings_add_str('trackbar', 'trackbar_string' => '_'); +$config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string'); + +Irssi::settings_add_str('trackbar', 'trackbar_style' => '%K'); +$config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style'); + +Irssi::signal_add( + 'setup changed' => sub { + $config{'trackbar_string'} = Irssi::settings_get_str('trackbar_string'); + $config{'trackbar_style'} = Irssi::settings_get_str('trackbar_style'); + if ($config{'trackbar_style'} =~ /(? sub { + my (undef, $oldwindow) = @_; + + if ($oldwindow) { + my $line = $oldwindow->view()->get_bookmark('trackbar'); + $oldwindow->view()->remove_line($line) if defined $line; + $oldwindow->print(line($oldwindow->{'width'}), MSGLEVEL_NEVER); + $oldwindow->view()->set_bookmark_bottom('trackbar'); + } + } +); + +sub line { + my $width = shift; + my $string = $config{'trackbar_string'}; + $string = '-' unless defined $string; + + # There is a bug in irssi's utf-8 handling on config file settings, as you + # can reproduce/see yourself by the following code sniplet: + # + # my $quake = pack 'U*', 8364; # EUR symbol + # Irssi::settings_add_str 'temp', 'temp_foo' => $quake; + # Irssi::print length $quake; + # # prints 1 + # Irssi::print length Irssi::settings_get_str 'temp_foo'; + # # prints 3 + # + # + # Trackbar used to have a workaround, but on recent versions of perl/irssi + # it does no longer work. Therefore, if you want your trackbar to contain + # unicode characters, uncomment the line below for a nice full line, or set + # the string to whatever char you want. + + # $string = pack('U*', 0x2500); + + + my $length = length $string; + + if ($length == 0) { + $string = '-'; + $length = 1; + } + + my $times = $width / $length; + $times = int(1 + $times) if $times != int($times); + $string =~ s/%/%%/g; + return $config{'trackbar_style'} . substr($string x $times, 0, $width); +} + +# Remove trackbars on upgrade - but this doesn't really work if the scripts are not loaded in the correct order... watch out! + +Irssi::signal_add_first( 'session save' => sub { + for my $window (Irssi::windows) { + next unless defined $window; + my $line = $window->view()->get_bookmark('trackbar'); + $window->view()->remove_line($line) if defined $line; + } + } +); + +sub cmd_mark { + my $window = Irssi::active_win(); +# return unless defined $window; + my $line = $window->view()->get_bookmark('trackbar'); + $window->view()->remove_line($line) if defined $line; + $window->print(line($window->{'width'}), MSGLEVEL_NEVER); + $window->view()->set_bookmark_bottom('trackbar'); + Irssi::command("redraw"); +} + +Irssi::command_bind('mark', 'cmd_mark'); diff --git a/irssi/scripts/trackbar22.pl b/irssi/scripts/trackbar22.pl new file mode 100644 index 0000000..30015a2 --- /dev/null +++ b/irssi/scripts/trackbar22.pl @@ -0,0 +1,500 @@ +# trackbar.pl +# +# This little script will do just one thing: it will draw a line each time you +# switch away from a window. This way, you always know just upto where you've +# been reading that window :) It also removes the previous drawn line, so you +# don't see double lines. +# +# redraw trackbar only works on irssi 0.8.17 or higher. +# +# +# Usage: +# +# The script works right out of the box, but if you want you can change +# the working by /set'ing the following variables: +# +# Setting: trackbar_style +# Description: This setting will be the color of your trackbar line. +# By default the value will be '%K', only Irssi color +# formats are allowed. If you don't know the color formats +# by heart, you can take a look at the formats documentation. +# You will find the proper docs on http://www.irssi.org/docs. +# +# Setting: trackbar_string +# Description: This is the string that your line will display. This can +# be multiple characters or just one. For example: '~-~-' +# The default setting is '-'. +# +# Setting: trackbar_use_status_window +# Description: If this setting is set to OFF, Irssi won't print a trackbar +# in the statuswindow +# +# Setting: trackbar_ignore_windows +# Description: A list of windows where no trackbar should be printed +# +# Setting: trackbar_print_timestamp +# Description: If this setting is set to ON, Irssi will print the formatted +# timestamp in front of the trackbar. +# +# Setting: trackbar_require_seen +# Description: Only clear the trackbar if it has been scrolled to. +# +# /mark is a command that will redraw the line at the bottom. +# +# Command: /trackbar, /trackbar goto +# Description: Jump to where the trackbar is, to pick up reading +# +# Command: /trackbar keep +# Description: Keep this window's trackbar where it is the next time +# you switch windows (then this flag is cleared again) +# +# Command: /mark, /trackbar mark +# Description: Remove the old trackbar and mark the bottom of this +# window with a new trackbar +# +# Command: /trackbar markvisible +# Description: Like mark for all visible windows +# +# Command: /trackbar markall +# Description: Like mark for all windows +# +# Command: /trackbar remove +# Description: Remove this window's trackbar +# +# Command: /trackbar removeall +# Description: Remove all windows' trackbars +# +# Command: /trackbar redraw +# Description: Force redraw of trackbars +# + + +# For bugreports and other improvements contact one of the authors. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this script; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +## + +use strict; +use warnings; +use Irssi; +use Irssi::TextUI; +use Encode; + +use POSIX qw(strftime); +use vars qw($VERSION %IRSSI); + +$VERSION = "2.2"; # cb3189a33c8e5f9 + +%IRSSI = ( + authors => 'Peter Leurs and Geert Hauwaerts', + contact => 'peter@pfoe.be', + patchers => 'Johan Kiviniemi (UTF-8), Uwe Dudenhoeffer (on-upgrade-remove-line)', + name => 'trackbar', + description => 'Shows a bar where you have last read a window.', + license => 'GNU General Public License', + url => 'http://www.pfoe.be/~peter/trackbar/', + changed => 'Fri Jan 23 23:59:11 2004', + commands => 'trackbar', +); + +## Comments and remarks. +# +# This script uses settings. +# Use /SET to change the value or /TOGGLE to switch it on or off. +# +# +# Tip: The command 'trackbar' is very usefull if you bind that to a key, +# so you can easily jump to the trackbar. Please see 'help bind' for +# more information about keybindings in Irssi. +# +# Command: /BIND meta2-P key F1 +# /BIND F1 command trackbar +# +## + +## Bugfixes and new items in this rewrite. +# +# * Remove all the trackbars before upgrading. +# * New setting trackbar_use_status_window to control the statuswindow trackbar. +# * New setting trackbar_print_timestamp to print a timestamp or not. +# * New command 'trackbar' to scroll up to the trackbar. +# * When resizing your terminal, Irssi will update all the trackbars to the new size. +# * When changing trackbar settings, change all the trackbars to the new settings. +# * New command 'trackbar mark' to draw a new trackbar (The old '/mark'). +# * New command 'trackbar markall' to draw a new trackbar in each window. +# * New command 'trackbar remove' to remove the trackbar from the current window. +# * New command 'trackbar removeall' to remove all the trackbars. +# * Don't draw a trackbar in empty windows. +# * Added a version check to prevent Irssi redraw errors. +# * Fixed a bookmark NULL versus 0 bug. +# * Fixed a remove-line bug in Uwe Dudenhoeffer his patch. +# * New command 'help trackbar' to display the trackbar commands. +# * Fixed an Irssi startup bug, now processing each auto-created window. +# +## + +## Known bugs and the todolist. +# +# Todo: * Instead of drawing a line, invert the line. +# +## + +sub cmd_help { + my ($args) = @_; + if ($args =~ /^trackbar *$/i) { + print CLIENTCRAP <>%n %_Scriptinfo:%_ Loaded $0 version $1 by $2.', + '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.', + 'trackbar_all_removed', '%R>>%n %_Trackbar:%_ All the trackbars have been removed.', + 'trackbar_not_found', '%R>>%n %_Trackbar:%_ No trackbar found in this window.', +]); + +my $old_irssi = Irssi::version < 20140701; +sub check_version { + if ($old_irssi) { + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_wrong_version'); + return; + } else { + return 1; + } +} + +sub is_utf8 { + lc Irssi::settings_get_str('term_charset') eq 'utf-8' +} + +my (%config, %keep_trackbar, %unseen_trackbar); + +sub remove_one_trackbar { + my $win = shift; + my $view = shift || $win->view; + my $line = $view->get_bookmark('trackbar'); + if (defined $line) { + my $bottom = $view->{bottom}; + $view->remove_line($line); + $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; + $view->redraw; + } +} + +sub add_one_trackbar { + my $win = shift; + my $view = shift || $win->view; + $win->print(line($win->{width}), MSGLEVEL_NEVER); + $view->set_bookmark_bottom('trackbar'); + $unseen_trackbar{ $win->{_irssi} } = 1; + $view->redraw; +} + +sub update_one_trackbar { + my $win = shift; + my $view = shift || $win->view; + my $force = shift; + my $ignored = win_ignored($win, $view); + remove_one_trackbar($win, $view) + if $force || !defined $force || !$ignored; + add_one_trackbar($win, $view) + if $force || !$ignored; +} + +sub win_ignored { + my $win = shift; + my $view = shift || $win->view; + return 1 unless $view->{buffer}{lines_count}; + return 1 if $win->{name} eq '(status)' && !$config{use_status_window}; + return 1 if grep { $win->{name} eq $_ || $win->{refnum} eq $_ + || $win->get_active_name eq $_ } @{ $config{ignore_windows} }; + return 0; +} + +sub sig_window_changed { + my ($newwindow, $oldwindow) = @_; + return unless $oldwindow; + trackbar_update_seen($newwindow); + return if delete $keep_trackbar{ $oldwindow->{_irssi} }; + trackbar_update_seen($oldwindow); + return if $config{require_seen} && $unseen_trackbar{ $oldwindow->{_irssi } }; + update_one_trackbar($oldwindow, undef, 0); +} + +sub trackbar_update_seen { + my $win = shift; + return unless $win; + my $view = $win->view; + my $line = $view->get_bookmark('trackbar'); + unless ($line) { + delete $unseen_trackbar{ $win->{_irssi} }; + return; + } + my $startline = $view->{startline}; + return unless $startline; + + if ($startline->{info}{time} < $line->{info}{time} + || $startline->{_irssi} == $line->{_irssi}) { + delete $unseen_trackbar{ $win->{_irssi} }; + } +} + +sub screen_length; +{ local $@; + eval { require Text::CharWidth; }; + unless ($@) { + *screen_length = sub { Text::CharWidth::mbswidth($_[0]) }; + } + else { + *screen_length = sub { + my $temp = shift; + if (is_utf8()) { + Encode::_utf8_on($temp); + } + length($temp) + }; + } +} + +{ my %strip_table = ( + (map { $_ => '' } (split //, '04261537' . 'kbgcrmyw' . 'KBGCRMYW' . 'U9_8I:|FnN>#[' . 'pP')), + (map { $_ => $_ } (split //, '{}%')), + ); + sub c_length { + my $o = Irssi::strip_codes($_[0]); + $o =~ s/(%(%|Z.{6}|z.{6}|X..|x..|.))/exists $strip_table{$2} ? $strip_table{$2} : + $2 =~ m{x(?:0[a-f]|[1-6][0-9a-z]|7[a-x])|z[0-9a-f]{6}}i ? '' : $1/gex; + screen_length($o) + } +} + +sub line { + my ($width, $time) = @_; + my $string = $config{string}; + $string = ' ' unless length $string; + $time ||= time; + + Encode::_utf8_on($string); + my $length = c_length($string); + + my $format = ''; + if ($config{print_timestamp}) { + $format = $config{timestamp_str}; + $format =~ y/%/\01/; + $format =~ s/\01\01/%/g; + $format = strftime($format, localtime $time); + $format =~ y/\01/%/; + } + + my $times = $width / $length; + $times += 1 if $times != int $times; + $format .= $config{style}; + $width -= c_length($format); + $string x= $times; + chop $string while length $string && c_length($string) > $width; + return $format . $string; +} + +sub remove_all_trackbars { + for my $window (Irssi::windows) { + next unless ref $window; + remove_one_trackbar($window); + } +} + +sub UNLOAD { + remove_all_trackbars(); +} + +sub redraw_trackbars { + return unless check_version(); + for my $win (Irssi::windows) { + next unless ref $win; + my $view = $win->view; + my $line = $view->get_bookmark('trackbar'); + next unless $line; + my $bottom = $view->{bottom}; + $win->print_after($line, MSGLEVEL_NEVER, line($win->{width}, $line->{info}{time}), + $line->{info}{time}); + $view->set_bookmark('trackbar', $win->last_line_insert); + $view->redraw; + $view->remove_line($line); + $win->command('^scrollback end') if $bottom && !$win->view->{bottom}; + $view->redraw; + } +} + +sub goto_trackbar { + my $win = Irssi::active_win; + my $line = $win->view->get_bookmark('trackbar'); + + if ($line) { + $win->command("scrollback goto ". strftime("%d %H:%M:%S", localtime($line->{info}{time}))); + } else { + $win->printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_not_found'); + } +} + +sub cmd_mark { + update_one_trackbar(Irssi::active_win, undef, 1); +} + +sub cmd_markall { + for my $window (Irssi::windows) { + next unless ref $window; + update_one_trackbar($window); + } +} + +sub signal_stop { + Irssi::signal_stop; +} + +sub cmd_markvisible { + my @wins = Irssi::windows; + my $awin = + my $bwin = Irssi::active_win; + my $awin_counter = 0; + Irssi::signal_add_priority('window changed' => 'signal_stop', -99); + do { + Irssi::active_win->command('window up'); + $awin = Irssi::active_win; + update_one_trackbar($awin); + ++$awin_counter; + } until ($awin->{refnum} == $bwin->{refnum} || $awin_counter >= @wins); + Irssi::signal_remove('window changed' => 'signal_stop'); +} + +sub cmd_trackbar_remove_one { + remove_one_trackbar(Irssi::active_win); +} + +sub cmd_remove_all_trackbars { + remove_all_trackbars(); + Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_all_removed'); +} + +sub cmd_keep_once { + $keep_trackbar{ Irssi::active_win->{_irssi} } = 1; +} + +sub trackbar_runsub { + my ($data, $server, $item) = @_; + $data =~ s/\s+$//g; + + if ($data) { + Irssi::command_runsub('trackbar', $data, $server, $item); + } else { + goto_trackbar(); + } +} + +sub update_config { + my $was_status_window = $config{use_status_window}; + $config{style} = Irssi::settings_get_str('trackbar_style'); + $config{string} = Irssi::settings_get_str('trackbar_string'); + $config{require_seen} = Irssi::settings_get_bool('trackbar_require_seen'); + $config{ignore_windows} = [ split /[,\s]+/, Irssi::settings_get_str('trackbar_ignore_windows') ]; + $config{use_status_window} = Irssi::settings_get_bool('trackbar_use_status_window'); + $config{print_timestamp} = Irssi::settings_get_bool('trackbar_print_timestamp'); + if (defined $was_status_window && $was_status_window != $config{use_status_window}) { + if (my $swin = Irssi::window_find_name('(status)')) { + if ($config{use_status_window}) { + update_one_trackbar($swin); + } + else { + remove_one_trackbar($swin); + } + } + } + if ($config{print_timestamp}) { + my $ts_format = Irssi::settings_get_str('timestamp_format'); + my $ts_theme = Irssi::current_theme->get_format('fe-common/core', 'timestamp'); + my $render_str = Irssi::current_theme->format_expand($ts_theme); + (my $ts_escaped = $ts_format) =~ s/([%\$])/$1$1/g; + $render_str =~ s/(?|\$(.)(?!\w)|\$\{(\w+)\})/$1 eq 'Z' ? $ts_escaped : $1/ge; + $config{timestamp_str} = $render_str; + } + redraw_trackbars() unless $old_irssi; +} + +Irssi::settings_add_str('trackbar', 'trackbar_string', is_utf8() ? "\x{2500}" : '-'); +Irssi::settings_add_str('trackbar', 'trackbar_style', '%K'); +Irssi::settings_add_str('trackbar', 'trackbar_ignore_windows', ''); +Irssi::settings_add_bool('trackbar', 'trackbar_use_status_window', 1); +Irssi::settings_add_bool('trackbar', 'trackbar_print_timestamp', 0); +Irssi::settings_add_bool('trackbar', 'trackbar_require_seen', 0); + +update_config(); + +Irssi::signal_add_last( 'mainwindow resized' => 'redraw_trackbars') + unless $old_irssi; + +Irssi::signal_register({'gui page scrolled' => [qw/Irssi::UI::Window/]}); +Irssi::signal_add_last('gui page scrolled' => 'trackbar_update_seen'); + +Irssi::signal_add('setup changed' => 'update_config'); +Irssi::signal_add_priority('session save' => 'remove_all_trackbars', Irssi::SIGNAL_PRIORITY_HIGH-1); + +Irssi::signal_add('window changed' => 'sig_window_changed'); + +Irssi::command_bind('trackbar goto' => 'goto_trackbar'); +Irssi::command_bind('trackbar keep' => 'cmd_keep_once'); +Irssi::command_bind('trackbar mark' => 'cmd_mark'); +Irssi::command_bind('trackbar markvisible' => 'cmd_markvisible'); +Irssi::command_bind('trackbar markall' => 'cmd_markall'); +Irssi::command_bind('trackbar remove' => 'cmd_trackbar_remove_one'); +Irssi::command_bind('trackbar removeall' => 'cmd_remove_all_trackbars'); +Irssi::command_bind('trackbar redraw' => 'redraw_trackbars'); +Irssi::command_bind('trackbar' => 'trackbar_runsub'); +Irssi::command_bind('mark' => 'cmd_mark'); +Irssi::command_bind_last('help' => 'cmd_help'); + +Irssi::printformat(MSGLEVEL_CLIENTCRAP, 'trackbar_loaded', $IRSSI{name}, $VERSION, $IRSSI{authors}); diff --git a/irssi/scripts/usercount.pl b/irssi/scripts/usercount.pl new file mode 100755 index 0000000..e93a379 --- /dev/null +++ b/irssi/scripts/usercount.pl @@ -0,0 +1,171 @@ +use Irssi 20020101.0250 (); +$VERSION = "1.16"; +%IRSSI = ( + authors => 'David Leadbeater, Timo Sirainen, Georg Lukas', + contact => 'dgl@dgl.cx, tss@iki.fi, georg@boerde.de', + name => 'usercount', + description => 'Adds a usercount for a channel as a statusbar item', + license => 'GNU GPLv2 or later', + url => 'http://irssi.dgl.yi.org/', +); + +# Once you have loaded this script run the following command: +# /statusbar window add usercount +# You can also add -alignment left|right option + +# /set usercount_show_zero on or off to show users when 0 users of that type +# /set usercount_show_ircops (default off) +# /set usercount_show_halfops (default on) + +# you can customize the look of this item from theme file: +# sb_usercount = "{sb %_$0%_ nicks ($1-)}"; +# sb_uc_ircops = "%_*%_$*"; +# sb_uc_ops = "%_@%_$*"; +# sb_uc_halfops = "%_%%%_$*"; +# sb_uc_voices = "%_+%_$*"; +# sb_uc_normal = "$*"; +# sb_uc_space = " "; + + +use strict; +use Irssi::TextUI; + +my ($ircops, $ops, $halfops, $voices, $normal, $total); +my ($timeout_tag, $recalc); + +# Called to make the status bar item +sub usercount { + my ($item, $get_size_only) = @_; + my $wi = !Irssi::active_win() ? undef : Irssi::active_win()->{active}; + + if(!ref $wi || $wi->{type} ne "CHANNEL") { # only works on channels + return unless ref $item; + $item->{min_size} = $item->{max_size} = 0; + return; + } + + if ($recalc) { + $recalc = 0; + calc_users($wi); + } + + my $theme = Irssi::current_theme(); + my $format = $theme->format_expand("{sb_usercount}"); + if ($format) { + # use theme-specific look + my $ircopstr = $theme->format_expand("{sb_uc_ircops $ircops}", + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + my $opstr = $theme->format_expand("{sb_uc_ops $ops}", + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + my $halfopstr = $theme->format_expand("{sb_uc_halfops $halfops}", + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + my $voicestr = $theme->format_expand("{sb_uc_voices $voices}", + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + my $normalstr = $theme->format_expand("{sb_uc_normal $normal}", + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + my $space = $theme->format_expand('{sb_uc_space}', + Irssi::EXPAND_FLAG_IGNORE_EMPTY); + $space = " " unless $space; + + my $str = ""; + $str .= $ircopstr.$space if defined $ircops; + $str .= $opstr.$space if defined $ops; + $str .= $halfopstr.$space if defined $halfops; + $str .= $voicestr.$space if defined $voices; + $str .= $normalstr.$space if defined $normal; + $str =~ s/\Q$space\E$//; + + $format = $theme->format_expand("{sb_usercount $total $str}", + Irssi::EXPAND_FLAG_IGNORE_REPLACES); + } else { + # use the default look + $format = "{sb \%_$total\%_ nicks \%c(\%n"; + $format .= '*'.$ircops.' ' if (defined $ircops); + $format .= '@'.$ops.' ' if (defined $ops); + $format .= '%%'.$halfops.' ' if (defined $halfops); + $format .= "+$voices " if (defined $voices); + $format .= "$normal " if (defined $normal); + $format =~ s/ $//; + $format .= "\%c)}"; + } + + $item->default_handler($get_size_only, $format, undef, 1); +} + +sub calc_users() { + my $channel = shift; + my $server = $channel->{server}; + + $ircops = $ops = $halfops = $voices = $normal = 0; + for ($channel->nicks()) { + if ($_->{serverop}) { + $ircops++; + } + + if ($_->{op}) { + $ops++; + } elsif ($_->{halfop}) { + $halfops++; + } elsif ($_->{voice}) { + $voices++; + } else { + $normal++; + } + } + + $total = $ops+$halfops+$voices+$normal; + if (!Irssi::settings_get_bool('usercount_show_zero')) { + $ircops = undef if ($ircops == 0); + $ops = undef if ($ops == 0); + $halfops = undef if ($halfops == 0); + $voices = undef if ($voices == 0); + $normal = undef if ($normal == 0); + } + $halfops = undef unless Irssi::settings_get_bool('usercount_show_halfops'); + $ircops = undef unless Irssi::settings_get_bool('usercount_show_ircops'); +} + +sub refresh { + if ($timeout_tag > 0) { + Irssi::timeout_remove($timeout_tag); + $timeout_tag = 0; + } + Irssi::statusbar_items_redraw('usercount'); +} + +sub refresh_check { + my $channel = shift; + my $wi = ref Irssi::active_win() ? Irssi::active_win()->{active} : 0; + + return unless ref $wi && ref $channel; + return if $wi->{name} ne $channel->{name}; + return if $wi->{server}->{tag} ne $channel->{server}->{tag}; + + # don't refresh immediately, or we'll end up refreshing + # a lot around netsplits + $recalc = 1; + Irssi::timeout_remove($timeout_tag) if ($timeout_tag > 0); + $timeout_tag = Irssi::timeout_add(500, 'refresh', undef); +} + +sub refresh_recalc { + $recalc = 1; + refresh(); +} + +$recalc = 1; +$timeout_tag = 0; + +Irssi::settings_add_bool('usercount', 'usercount_show_zero', 1); +Irssi::settings_add_bool('usercount', 'usercount_show_ircops', 0); +Irssi::settings_add_bool('usercount', 'usercount_show_halfops', 1); + +Irssi::statusbar_item_register('usercount', undef, 'usercount'); +Irssi::statusbars_recreate_items(); + +Irssi::signal_add_last('nicklist new', 'refresh_check'); +Irssi::signal_add_last('nicklist remove', 'refresh_check'); +Irssi::signal_add_last('nick mode changed', 'refresh_check'); +Irssi::signal_add_last('setup changed', 'refresh_recalc'); +Irssi::signal_add_last('window changed', 'refresh_recalc'); +Irssi::signal_add_last('window item changed', 'refresh_recalc'); diff --git a/irssi/weed.theme b/irssi/weed.theme new file mode 100644 index 0000000..ffe9612 --- /dev/null +++ b/irssi/weed.theme @@ -0,0 +1,601 @@ +# __ __ ___ ___ ___ +# | T__T T / _] / _]| \ +# | | | | / [_ / [_ | \ +# | | | |Y _]Y _]| D Y +# l ` ' !| [_ | [_ | | +# \ / | T| T| | +# \_/\_/ l_____jl_____jl_____j +# +# Beautiful X-Chat inspired irssi theme by rolle @ QuakeNet +# http://roni.laukkarinen.info +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=~ +# +# NOTE FOR FINNISH USERS: +# +# Jos olet suomalainen ja pidät teemasta, joini ihmeessä +# kanavalle #pulina quakenetissä. Lisää tietoa kanavasta: +# http://www.pulina.fi +# +# Ja jos tykkäsit, mikset sanoisi kiitokset privassa, +# jotta tiedän että tätä käyttää joku :-) +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=~ +# +# Inspired by X-Chat. +# (not the xchat-irssi-theme: THE X-Chat-theme) +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=~ +# +# IMPORTANT: +# +# Make sure this is the latest version and use the tutorial +# provided in https://github.com/ronilaukkarinen/weed +# +# Without actions in the tutorial the theme WILL NOT WORK +# PROPERLY! +# +# Always backup your ~/.irssi -folder before trying! +# +#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=~ + +default_color = "-1"; +# Timestamp/servertag to the end, not first +info_eol = "false"; +replaces = { "[]=" = "$*"; }; + +abstracts = { + + # Disabling client crap + # msglevel_clientcrap = "%p*%K | %W%K$*"; + + # For awaybar.pl + sb_awaybar = " %1 {sb %W$0-} %N"; + + # For usercount.pl + sb_usercount = " %0 {sb %G$0} %N "; + sb_uc_ircops = "%r*$*"; + sb_uc_ops = "%Ro%K$*"; + sb_uc_halfops = "%Bho%b$*"; + sb_uc_voices = "%Gv%K$*"; + sb_uc_normal = "%w$*%K"; + sb_uc_space = " "; + + line_start = ""; + timestamp = " %_$* "; + hilight = "$*"; + error = "$*"; + channel = "$*"; + nick = "$*"; + nickhost = "$*"; + server = "$*"; + comment = "$*"; + reason = "{comment $*}"; + mode = "{comment $*}"; + channick_hilight = "$*"; + chanhost_hilight = "{nickhost $*}"; + channick = "$*"; + chanhost = "{nickhost $*}"; + channelhilight = "$*"; + ban = "$*"; + + # All of below: $0 = mode, $1 = nick + msgnick = "$0$1- %|"; + ownmsgnick = "{msgnick %G$1%K%_|}%w"; + ownnick = "$*"; + + # v3.05-hilight is now on red background and white text + # Can be seen better in communicator phone. + # (if you liked the old fashioned hilight, comment out next lines below and enable the noted ones) + # + + pubmsgnick = "{msgnick %r$1%K%_|}%w"; + pubnick = "$*"; + pubmsgmenick = "%1%W{msgnick $1-|}"; + menick = "$*%n"; + # $0 = hilight color, $1 = mode, $2 = nick + pubmsghinick = "%1%W{msgnick $2-|}"; + + # Got rid of on version 3.05: + # + #pubmsgnick = "{msgnick %r$1%K%_|}%w"; + #pubnick = "$*"; + #pubmsgmenick = "%G%K{msgnick $1-%R|}%0%W"; + #menick = "$*%n"; + #pubmsghinick = "%G%K{msgnick $2-%R|}%0%W"; + + msgchannel = ":$*"; + # $0 = nick, $1 = host + # + # msn-styled: + #privmsg = "%:[$0($1-)]"; + privmsg = "[$0($1-)]"; + + # + # $0 = "msg", + # $1 = target nick + # + + # + # msn-styled: + #ownprivmsg = "%:%w[$0(%1-)]"; + ownprivmsg = "%w[$0(%1-)]"; + + ownprivmsgnick = "{msgnick $*}"; + + # + # msn-styled privmsgs: + #ownprivnick = "%B$*%K says:%:"; + #privmsgnick = "%r$*%K says:%:"; + ownprivnick = "%B$*%K%_|"; + privmsgnick = "%r$*%K%_|"; + action_core = " %C*%K | %C$0- "; + action = "%C{action_core $*}%W"; + ownaction = "{action $*}"; + ownaction_target = "{action_core $*}:$1 "; + pvtaction = " (>>>) $* "; + pvtaction_query = "{action $*}"; + pubaction = "{action $*}"; + ownnotice = "[$0($1-)] "; + notice = "$*"; + whois = "%# $[8]0 : $1-"; + pubnotice_channel = ":$*"; + # pvtnotice_host = "($*)"; + pvtnotice_host = ""; + servernotice = "%r!$* %K"; + ownctcp = "[$0($1-)] "; + ctcp = "$*"; + wallop = "$*: "; + wallop_nick = "$*"; + wallop_action = " * $* "; + netsplit = "$*"; + netjoin = "$*"; + names_prefix = " %p*%K | "; + names_nick = "$0$1- "; + names_nick_op = "{names_nick $*}"; + names_nick_halfop = "{names_nick $*}"; + names_nick_voice = "{names_nick $*}"; + names_users = "$*"; + names_channel = "%K$*"; + dcc = "$*"; + dccfile = "$*"; + dccownmsg = "[$0($1-)] "; + dccownnick = "$*"; + dccownquerynick = "$*"; + dccownaction = "{action $*}"; + dccownaction_target = "{action_core $0}:$1 "; + dccmsg = "[$1-($0)] "; + dccquerynick = "$*"; + dccaction = " (*dcc*) $* %|"; + sb_background = "%N"; + sb_window_bg = "%k%0"; + sb_default_bg = "%N"; + sb_prompt_bg = "%N"; + sb_info_bg = "%N"; + sb_topic_bg = "%N"; + sbstart = ""; + sbend = " "; + topicsbstart = "{sbstart $*}"; + topicsbend = "{sbend $*}"; + #prompt = "%r$*%r: "; + prompt = ""; + sb = "%K$*%K"; + sbmode = ""; + sbaway = "%raway!%n"; + sbservertag = "$0%K"; + sbnickmode = ""; + sb_act_sep = "%w$*%n"; + sb_act_text = "%K$*%n"; + sb_act_msg = "%2%W$*%n"; + sb_act_hilight = "%5%W $0%W$1-%n"; + sb_act_hilight_color = "%5%W$0%W$1-%n"; +}; +formats = { + "fe-common/core" = { + query_start = " %g*%K | %gStarting query%W on {server $1} w/ {nick $0}"; + join = " %g*%K | %K{channick_hilight $0} %gjoined%K {channel $2}"; + part = " %r*%K | %K{channick $0} %rleft%K {channel $2}"; + quit = " %r*%K | %K{channick $0} %rquit%k %w(%KReason: {reason $2}%w)"; + quit_once = " %r*%K | %K{channick $0} [{chanhost $1}] %rquit%K %w(%KReason: \012{reason $2}%w)"; + nick_changed = " %y*%K | %K{channick_hilight $0} %yis now known as%K {channick_hilight $1}"; + + own_msg = "{ownmsgnick $2 {ownnick $[-10]0}}%W$1"; + own_msg_channel = "{ownmsgnick $3 {ownnick $[-10]0}{msgchannel $1}}%w%_$2"; + pubmsg_me = "{pubmsgmenick $2 {menick $[-10]0}}%_$1"; + pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-10]0}{msgchannel $1}}$2"; + pubmsg_hilight = "{pubmsghinick $0 $3 $[-10]1}$2"; + pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-10]1{msgchannel $2}}$3"; + pubmsg = "{pubmsgnick $2 {pubnick $nickcolor$[-10]0}}$1"; + pubmsg_channel = "{pubmsgnick $3 {pubnick $[-10]0}{msgchannel $1}}$2"; + + # own_msg = "{ownmsgnick $2 {ownnick $[-10]0}}$1"; + # own_msg_channel = "{ownmsgnick $3 {ownnick $[-10]0}{msgchannel $1}}$2"; + #pubmsg_me = "{pubmsgmenick $2 {menick $[-9]0}}$1"; + # pubmsg_me_channel = "{pubmsgmenick $3 {menick $[-10]0}{msgchannel $1}}$2"; + # pubmsg_hilight = "{pubmsghinick $0 $3 $[-10]1}$2"; + # pubmsg_hilight_channel = "{pubmsghinick $0 $4 $[-10]1{msgchannel $2}}$3"; + # pubmsg = "{pubmsgnick $2 {pubnick \0030$0}}$1"; + # pubmsg_channel = "{pubmsgnick $3 {pubnick $[-10]0}{msgchannel $1}}$2"; + # line_start = "{line_start}"; + # line_start_irssi = "{line_start}{hilight Irssi} %W|%n "; + line_start_irssi = "%K{line_start}"; + timestamp = "%K%_{timestamp %_$Z} %W"; + # servertag = "$[-11]0 %W|%n "; + servertag = "%K"; + daychange = " %K%_00:00:00%p * | Day changed! Still up? Today is %%d. %%b, Anno Domini %%Y.%n"; + talking_with = " %p*%K | %W%KYou are now having a conversation with {nick $0}."; + refnum_too_low = " %r*%K | Window number must be greater than one."; + error_server_sticky = " %r*%K | Window's server is %rsticky%K and it cannot be changed without -unsticky option"; + set_server_sticky = " %g*%K | Window's server %gset sticky%K"; + unset_server_sticky = " %g*%K | Window's server isn't sticky anymore"; + window_name_not_unique = " %r*%K | Window names must be unique"; + window_level = " %g*%K | Window level is now $0"; + windowlist_header = "Ref Name Active item Server Level"; + windowlist_line = "$[3]0 %|$[20]1 $[15]2 $[15]3 $4"; + windowlist_footer = ""; + windows_layout_saved = " %g*%K | Layout of windows is now %gremembered%K next time you start irssi"; + windows_layout_reset = " %g*%K | Layout of windows %greset%K to defaults"; + window_info_header = ""; + window_info_footer = ""; + window_info_refnum = "Window : {hilight #$0}"; + window_info_refnum_sticky = "Window : {hilight #$0 (sticky)}"; + window_info_name = "Name : $0"; + window_info_history = "History : $0"; + window_info_size = "Size : $0x$1"; + window_info_level = "Level : $0"; + window_info_server = "Server : $0"; + window_info_server_sticky = "Server : $0 (sticky)"; + window_info_theme = "Theme : $0$1"; + window_info_bound_items_header = "Bounds : {hilight Name Server tag}"; + window_info_bound_item = " : $[!30]0 $[!15]1 $2"; + window_info_bound_items_footer = ""; + window_info_items_header = "Items : {hilight Name Server tag}"; + window_info_item = " $[7]0: $[!30]1 $2"; + window_info_items_footer = ""; + looking_up = " %g*%K | %gLooking up%K {server $0}"; + connecting = " %g*%K | %gConnecting%K to {server $0} [$1] port {hilight $2}"; + connection_established = " %g*%K | Connection to {server $0} %gestablished%K"; + cant_connect = " %r*%K | %rUnable to connect%K server {server $0} port {hilight $1} {reason $2}"; + connection_lost = " %r*%K | %rConnection lost%K to {server $0}"; + lag_disconnected = " %r*%K | %rNo PONG reply%K from server {server $0} in $1 seconds, disconnecting"; + disconnected = " %r*%K | %rDisconnected%K from {server $0} {reason $1}"; + server_quit = " %r*%K | %rDisconnecting%K from server {server $0}: {reason $1}"; + server_changed = " %y*%K | %yChanged%K to {hilight $2} server {server $1}"; + unknown_server_tag = " %r*%K | %rUnknown%K server tag {server $0}"; + no_connected_servers = " %r*%K | %rNot connected%K to any servers"; + server_list = "{server $0}: $1:$2 ($3)"; + server_lookup_list = "{server $0}: $1:$2 ($3) (connecting...)"; + server_reconnect_list = "{server $0}: $1:$2 ($3) ($5 left before reconnecting)"; + server_reconnect_removed = " %g*%K | %gRemoved reconnection%K to server {server $0} port {hilight $1}"; + server_reconnect_not_found = " %r*%K | Reconnection tag {server $0} %rnot found%K"; + setupserver_added = " %g*%K | Server {server $0} %gsaved%K"; + setupserver_removed = " %g*%K | Server {server $0} %gremoved%K"; + setupserver_not_found = " %r*%K | Server {server $0} %rnot found%K"; + your_nick = " %p*%K | %W%KYour nickname is {nick $0}"; + kick = " %r*%K | {channick $0} was %rkicked%K from {channel $1} by {nick $2} ({reason $3})"; + invite = " %y*%K | {channick_hilight $0} %yinvites%K you to {channel $1}"; + not_invited = "You have not been invited to a channel!"; + new_topic = " %y*%K | {channick_hilight $0} %ychanged topic%W of {channel $1} to: {hilight $2}"; + topic_unset = " %y*%K | {channick_hilight $0} %yremoved the topic%W on {channel $1}"; + your_nick_changed = " %y*%K | You %yare now better known as%K nickname {channick_hilight $1}"; + talking_in = " %p*%K | %W%KYou are now on {channel $0}"; + not_in_channels = " %p*%K | %W%KYou are not on any channels"; + current_channel = " %p*%K | %W%KCurrent channel {channel $0}"; + names = " %p*%K | %W%K{names_users Users: {names_channel $0}}: {comment $1}"; + names_prefix = "%K{names_prefix $0}"; + names_nick_op = "%p{names_nick_op $0 $1}"; + names_nick_halfop = "%p{names_nick_halfop $0 $1}"; + names_nick_voice = "%p{names_nick_voice $0 $1}"; + names_nick = "%p{names_nick $0 $1}"; + endofnames = " %p*%K | {channel $0}: Total: {hilight $1} users ({comment {hilight $2} ops, {hilight $3} halfops, \012 %p*%K | {hilight $4} voiced, {hilight $5} normal users.})"; + chanlist_header = " %p*%K | %W%KYou are on the following channel:"; + chanlist_line = "{channel $[-9]0} %|+$1 ($2): $3"; + chansetup_not_found = "Channel {channel $0} not found"; + chansetup_added = "Channel {channel $0} saved"; + chansetup_removed = "Channel {channel $0} removed"; + chansetup_header = "Channel Network Password Settings"; + chansetup_line = "{channel $[15]0} %|$[10]1 $[10]2 $3"; + chansetup_footer = ""; + channel_move_notify = "{channel $0} is already joined in window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window"; + + # own_msg_private = "{ownprivmsg msg $0}$1"; + # own_msg_private_query = "{ownprivmsgnick {ownprivnick $[-9]2}}$1"; + # msg_private = "{privmsg $0 $1}$2"; + # msg_private_query = "{privmsgnick $[-9]0}$2"; + + no_msgs_got = " %p*%K | %W%KYou have not received a message from anyone yet"; + no_msgs_sent = " %p*%K | %W%KYou have not sent a message to anyone yet"; + query_stop = " %p*%K | %W%KA conversation with {nick $0} is ended."; + no_query = " %p*%K | %W%KNo query with {nick $0}"; + query_server_changed = " %y*%K | Query with {nick $0} %ychanged%K to server {server $1}"; + query_move_notify = " %p*%K | %W%KQuery with {nick $0} is already created to window $1, use \"/WINDOW ITEM MOVE $0\" to move it to this window"; + hilight_header = " %p*%K | %W%KHighlights:"; + hilight_line = "$[-4]0 $1 $2 $3$4$5"; + hilight_footer = ""; + hilight_not_found = " %r*%K | Highlight %rnot found%K: $0"; + hilight_removed = " %g*%K | Highlight %gremoved%K: $0"; + alias_added = " %g*%K | Alias $0 %gadded%K"; + alias_removed = " %g*%K | Alias $0 %gremoved%K"; + alias_not_found = " %r*%K | %rNo such%K alias: $0"; + aliaslist_header = " %p*%K | %W%KAliases:"; + aliaslist_line = "$[10]0 $1"; + aliaslist_footer = ""; + log_opened = " %g*%K | Log file {hilight $0} %gopened%K"; + log_closed = " %g*%K | Log file {hilight $0} %gclosed%K"; + log_create_failed = " %r*%K | %rCouldn't create%K log file {hilight $0}: $1"; + log_locked = " %r*%K | Log file {hilight $0} is %rlocked%K, probably by another running Irssi"; + log_not_open = " %r*%K | Log file {hilight $0} %rnot open%K"; + log_started = " %g*%K | %gStarted%K logging to file {hilight $0}"; + log_stopped = " %r*%K | %rStopped%K logging to file {hilight $0}"; + log_list_header = " %p*%K | %W%KLogs:"; + log_list = "%K$0 $1: $2 $3$4"; + log_list_footer = ""; + windowlog_file = " %g*%K | Window LOGFILE set to $0"; + windowlog_file_logging = " %r*%K | %rCan't change%K window's logfile while log is on"; + # no_away_msgs = " %r*%K | %rNo new%K messages in awaylog"; + no_away_msgs = ""; + # away_msgs = " %g*%K | {hilight $1} %gnew messages%K in awaylog:%K"; + away_msgs = ""; + module_header = "Module Type Submodules"; + module_line = "$[!20]0 $[7]1 $2"; + module_footer = ""; + module_already_loaded = " %r*%K | Module {hilight $0/$1} already loaded"; + module_not_loaded = " %r*%K | Module {hilight $0/$1} %ris not loaded%K"; + module_load_error = " %r*%K | %rError%K loading module {hilight $0/$1}: $2"; + module_invalid = " %r*%K | {hilight $0/$1} isn't Irssi module"; + module_loaded = " %g*%K | %gLoaded%K module {hilight $0/$1}"; + module_unloaded = " %r*%K | %rUnloaded%K module {hilight $0/$1}"; + command_unknown = " %r*%K | %rUnknown%K command: $0"; + command_ambiguous = " %r*%K | %rAmbiguous%K command: $0"; + option_unknown = " %r*%K | %rUnknown%K option: $0"; + option_ambiguous = " %r*%K | %rAmbiguous%K option: $0"; + option_missing_arg = " %r*%K | %rMissing%K required argument for: $0"; + not_enough_params = " %r*%K | %rNot enough%K parameters given"; + not_connected = " %r*%K | %rNot connected%K to server"; + not_joined = " %r*%K | %rNot joined%K to any channel"; + chan_not_found = " %r*%K | %rNot joined%K to such channel"; + chan_not_synced = " %r*%K | Channel %rnot fully synchronized%K yet, try again after a while"; + illegal_proto = " %r*%K | Command isn't designed for the chat protocol of the active server"; + not_good_idea = " %r*%K | Doing this is %rnot a good idea%K. Add -YES if you really mean it"; + theme_saved = " %g*%K | Theme %gsaved%K to $0"; + theme_save_failed = " %r*%K | %rError%K saving theme to $0: $1"; + theme_not_found = " %r*%K | Theme {hilight $0} %rnot found%K"; + theme_changed = " %y*%K | %yUsing%K now theme {hilight $0} ($1)"; + window_theme = " %g*%K | %gUsing%K theme {hilight $0} in this window"; + window_theme_default = " %r*%K | %rNo theme is set%K for this window"; + window_theme_changed = " %y*%K | %yUsing%K now theme {hilight $0} ($1) in this window"; + window_theme_removed = " %g*%K | %gRemoved%K theme from this window"; + format_title = "%:[{hilight $0}] - [{hilight $1}]%:"; + format_subtitle = "[{hilight $0}]"; + format_item = "$0 = $1"; + ignored = " %g*%K | %gIgnoring%K {hilight $1} from {nick $0}"; + unignored = " %g*%K | %gUnignored%K {nick $0}"; + ignore_not_found = " %g*%K | {nick $0} %gis not%K being ignored"; + ignore_no_ignores = " %p*%K | %W%KThere are no ignores"; + ignore_header = " %p*%K | %W%KIgnorance List:"; + ignore_line = "$[-4]0 $1: $2 $3 $4"; + ignore_footer = ""; + unknown_chat_protocol = " %r*%K | %rUnknown%K chat protocol: $0"; + unknown_chatnet = " %r*%K | %rUnknown%K chat network: $0 (create it with /IRCNET ADD)"; + not_toggle = " %r*%K | Value must be either ON, OFF or TOGGLE"; + perl_error = " %r*%K | Perl %rerror%K: $0"; + bind_key = "$[!20]0 $1 $2"; + bind_unknown_id = " %r*%K | %rUnknown%K bind action: $0"; + config_saved = " %g*%K | %gSaved%K configuration to file $0"; + config_reloaded = " %g*%K | %gReloaded%K configuration"; + config_modified = " %p*%K | %W%KConfiguration file was modified since irssi was last started - do you want to overwrite the possible changes?"; + glib_error = "{error GLib $0} $1"; + overwrite_config = " %p*%K | %W%KOverwrite config (%gy%K/%rN%K)?"; + set_title = "[{hilight $0}]"; + set_item = "$0 = $1"; + set_unknown = " %r*%K | %rUnknown%K setting $0"; + set_not_boolean = " %r*%K | Setting {hilight $0} isn't boolean, use /SET"; + translation_not_found = " %r*%K | %rError%K opening translation table file $0: $1"; + translation_file_error = " %r*%K | %rError%K parsing translation table file $0"; + + #privat: + + own_msg_private_query = "{ownprivmsgnick {ownprivnick $[-10]2}}%W$1"; + own_msg_private = "{ownprivmsg msg $0}%W$1"; + msg_private_query = "{privmsgnick {privnick $[-10]0}} %W$2"; + msg_private = "{privmsg $0 $1}%W$2"; + + # msn-styled: + # own_msg_private_query = "{ownprivnick $2}%w$1%:"; + # msg_private_query = "{privmsgnick $0}%w$2%:"; + + }; + "fe-common/irc/dcc" = { + dcc_list_header = "{line_start_irssi}{dcc DCC connections:}"; + dcc_list_footer = "{line_start_irssi}{dcc ];}"; + # own_dcc = "{dccownmsg dcc {dccownnick $1}}$2"; + # own_dcc_action = "{dccownaction_target $0 $1}$2"; + # own_dcc_action_query = "{dccownaction $0}$2"; + # own_dcc_ctcp = "{ownctcp ctcp $0}$1 $2"; + # dcc_msg = "{dccmsg dcc $0}$1"; + # action_dcc = "{dccaction $0}$1"; + # action_dcc_query = "{dccaction $0}$1"; + # own_dcc_query = "{ownmsgnick {dccownquerynick $0}}$2"; + # dcc_msg_query = "{privmsgnick $0}$1"; + dcc_ctcp = " %g*%K | {dcc >>> DCC CTCP {hilight $1} %greceived%K from {hilight $0}: $2}"; + dcc_chat = " %g*%K | {dcc DCC CHAT from {nick $0} [$1 port $2]}"; + dcc_chat_channel = " %g*%K | {dcc DCC CHAT from {nick $0} [$1 port $2] %grequested%K in channel {channel $3}}"; + dcc_chat_not_found = " %g*%K | {dcc No DCC CHAT %gconnection open%K to {nick $0}}"; + dcc_chat_connected = " %g*%K | {dcc DCC CHAT connection with {nick $0} [$1 port $2] %gestablished%K}"; + dcc_chat_disconnected = " %r*%K | {dcc DCC %rlost chat%K to {nick $0}}"; + dcc_send = " %g*%K | {dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes]}"; + dcc_send_channel = " %g*%K | {dcc DCC SEND from {nick $0} [$1 port $2]: $3 [$4 bytes] %grequested%K in channel {channel $5}}"; + dcc_send_exists = " %r*%K | {dcc DCC %ralready sending%K file {dccfile $0} for {nick $1}}"; + dcc_send_not_found = " %r*%K | {dcc DCC %rnot sending%K file {dccfile $1} to {nick $0}}"; + dcc_send_file_open_error = " %r*%K | {dcc DCC %rcan't open%K file {dccfile $0}: $1}"; + dcc_send_connected = " %g*%K | {dcc DCC %gsending%K file {dccfile $0} for {nick $1} [$2 port $3]}"; + dcc_send_complete = " %g*%K | {dcc DCC %gsent%K file {dccfile $0} [{hilight $1}kB] for {nick $2} in {hilight $3} secs [{hilight $4kB/s}]}"; + dcc_send_aborted = " %r*%K | {dcc DCC %raborted%K sending file {dccfile $0} for {nick $1}}"; + dcc_get_not_found = " %r*%K | {dcc DCC no file offered by {nick $0}}"; + dcc_get_connected = " %g*%K | {dcc DCC %greceiving%K file {dccfile $0} from {nick $1} [$2 port $3]}"; + dcc_get_complete = " %g*%K | {dcc DCC %greceived%K file {dccfile $0} [$1kB] from {nick $2} in {hilight $3} secs [$4kB/s]}"; + dcc_get_aborted = " %r*%K | {dcc DCC %raborted%K receiving file {dccfile $0} from {nick $1}}"; + dcc_unknown_ctcp = " %r*%K | {dcc DCC unknown ctcp {hilight $0} from {nick $1} [$2]}"; + dcc_unknown_reply = " %r*%K | {dcc DCC unknown reply {hilight $0} from {nick $1} [$2]}"; + dcc_unknown_type = " %r*%K | {dcc DCC unknown type {hilight $0}}"; + dcc_invalid_ctcp = " %r*%K | {dcc DCC received CTCP {hilight $0} with %rinvalid%K parameters from {nick $1}}"; + dcc_connect_error = " %r*%K | {dcc DCC %rcan't connect%K to {hilight $0} port {hilight $1}}"; + dcc_cant_create = " %r*%K | {dcc DCC %rcan't create%K file {dccfile $0}}"; + dcc_rejected = " %r*%K | {dcc DCC $0 was %rrejected%K by {nick $1} [{hilight $2}]}"; + dcc_request_send = " %g*%K | {dcc DCC $0 %grequest sent%K to {nick $1}: $2"; + dcc_close = "{dcc DCC $0 close for {nick $1} [{hilight $2}]}"; + dcc_lowport = "{dcc Warning: Port sent with DCC request is a lowport ({hilight $0, $1}) - this isn't normal. It is possible the address/port is faked (or maybe someone is just trying to bypass firewall)}"; + dcc_list_line_chat = "%WChat ->%K {dcc $0 $1}"; + dcc_list_line_file = "%WFile ->%K {dcc $0 $1 : $2k of $3k ($4%%) - $5kB/s - $6}"; + }; + "fe-text" = { + lastlog_too_long = " %p*%K | %W%K/LASTLOG would print $0 lines. If you really want to print all these lines use -force option."; + lastlog_count = "{hilight Lastlog}: $0 lines"; + lastlog_start = "{hilight Lastlog}:"; + lastlog_end = "{hilight End of Lastlog}"; + refnum_not_found = " %r*%K | Window number $0 %rnot found%K"; + window_too_small = " %r*%K | %rNot enough room%K to resize this window"; + cant_hide_last = " %r*%K | You %rcan't hide%K the last window"; + cant_hide_sticky_windows = " %r*%K | You %rcan't hide%K sticky windows (use /WINDOW STICK OFF)"; + cant_show_sticky_windows = " %r*%K | You %rcan't show%K sticky windows (use /WINDOW STICK OFF)"; + window_not_sticky = " %r*%K | Window %ris not%K sticky"; + window_set_sticky = " %g*%K | Window %gset%K sticky"; + window_unset_sticky = " %g*%K | Window %gis not%K sticky anymore"; + window_info_sticky = "Sticky : $0"; + window_scroll = " %g*%K | Window scroll mode is now $0"; + window_scroll_unknown = " %r*%K | %rUnknown%K scroll mode $0, must be ON, OFF or DEFAULT"; + }; + "fe-common/irc" = { + netsplit = " %r*%K | %r{netsplit netsplit}%K %|{server $0} <-> {server $1} %rquits%K: $2"; + netsplit_more = " %r*%K | %r{netsplit netsplit}%K %|{server $0} <-> {server $1} %rquits:%K $2 (+$3 more, use /NETSPLIT to show all of them)"; + netsplit_join = " %g*%K | %g{netjoin netsplit}%K - %|%gjoins:%K $0"; + netsplit_join_more = " %g*%K | %g{netjoin netsplit}%K - %|over, %gjoins:%K $0 (+$1 more)"; + no_netsplits = " %p*%K | %W%KThere are no netsplits"; + netsplits_header = "Nick Channel Server Splitted server"; + netsplits_line = "$[9]0 $[10]1 $[20]2 $3"; + netsplits_footer = ""; + ircnet_added = " %g*%K | Ircnet $0 %gsaved%K"; + ircnet_removed = " %g*%K | Ircnet $0 %gremoved%K"; + ircnet_not_found = " %r*%K | Ircnet $0 %rnot found%K"; + ircnet_header = " %p*%K | %W%KIrcnets:"; + ircnet_line = "$0: $1"; + ircnet_footer = ""; + setupserver_header = "Server Port Network Settings"; + setupserver_line = "%|$[!20]0 $[5]1 $[10]2 $3"; + setupserver_footer = ""; + joinerror_toomany = " %r*%K | Join %rfails%K: {channel $0} (You have joined to too many channels)"; + joinerror_full = " %r*%K | Join %rfails%K: {channel $0} (Channel is full)"; + joinerror_invite = " %r*%K | Join %rfails%K: {channel $0} (You must be invited)"; + joinerror_banned = " %r*%K | Join %rfails%K: {channel $0} (You are banned)"; + joinerror_bad_key = " %r*%K | Join %rfails%K: {channel $0} (Wrong channel key)"; + joinerror_bad_mask = " %r*%K | Join %rfails%K: {channel $0} (Bad channel mask)"; + joinerror_unavail = " %r*%K | Join %rfails%K: {channel $0} (Channel is temporarily unavailable)"; + joinerror_duplicate = " %r*%K | Channel {channel $0} already exists - %rcannot create%K it"; + channel_rejoin = " %r*%K | Channel {channel $0} is temporarily %runavailable%K. Setting up a rejoin, to not rejoin, use /rmrejoins."; + inviting = " %g*%K | %gInviting%K {nick $0} to {channel $1}"; + channel_created = " %p*%K | %W%K{channel $0} is created %_$1%_"; + url = " %p*%K | %W%KHome page for {channelhilight $0}: $1"; + topic = " %p*%K | %WTopic for channel {channel $0}: %_$1%_"; + no_topic = " %p*%K | %WNo topic for channel %_$0%_"; + topic_info = " %p*%K | %WTopic is set by {channick_hilight $0} {mode $1}"; + chanmode_change = " %y*%K | {nick $2} %ychanged mode%K on channel {channel $0} %w(%K{mode $1}%w)"; + server_chanmode_change = " %g*%K | {netsplit ServerMode}%K/{channelhilight $0}: {mode $1} by {nick $2}"; + channel_mode = " %y*%K | %yChannel settings%K: {mode $1}"; + bantype = " %y*%K | Ban type %ychanged%K to {channel $0}"; + no_bans = " %p*%K | %W%KNo bans in channel {channel $0}"; + banlist = "$0 - {channel $1}: ban {ban $2}"; + banlist_long = "$0 - {channel $1}: ban {ban $2} {comment by {nick $3}, $4 secs ago}"; + ebanlist = "{channel $0}: ban exception {ban $1}"; + ebanlist_long = "{channel $0}: ban exception {ban $1} {comment by {nick $2}, $3 secs ago}"; + no_invitelist = " %p*%K | %W%KInvite list is empty in channel {channel $0}"; + invitelist = "{channel $0}: invite {ban $1}"; + no_such_channel = " %r*%K | {channel $0}: %rNo such%K channel"; + channel_change = " %y*%K | %ychannel settings changed%K: %w(%K{mode $1}%w)"; + channel_synced = " %p*%K | %W%KJoin to {channel $0} was synced in {hilight $1} secs"; + usermode_change = " %y*%K | {nick $2} %ygave privileges%K on channel {channel $0} to %w(%K{mode $0}%w)"; + user_mode = " %p*%K | %W%KYour user mode is {mode $0}"; + # away = " %g*%K | You have been %gmarked%K as being away"; + # unaway = " %g*%K | You are %gno longer marked%K as being away"; + away = ""; + unaway = ""; + nick_away = " %r*%K | {nick $0} is %raway%K: $1"; + no_such_nick = " %r*%K | {nick $0}: %rNo such%K nick/channel"; + nick_in_use = " %r*%K | Nick {nick $0} is %ralready in use%K"; + nick_unavailable = " %r*%K | Nick {nick $0} is temporarily %runavailable%K"; + your_nick_owned = " %r*%K | Your nick is %rowned%K by {nick $3} {comment $1@$2}"; + whois = " %g*%K | %g{nick $0}%K ({nickhost $1@$2})%: %p*%K | ircname: $3"; + whowas = " %p*%K | %W%K{nick $0} {nickhost $1@$2}%: %p*%K | {whois ircname $3}"; + whois_idle = " %p*%K | %W%KIdle: %|since $1 days $2 hours $3 mins $4 secs"; + whois_idle_signon = " %p*%K | %W%KIdle: %|since $1 days $2 hours $3 mins $4 secs {comment Signed on: $5}"; + whois_server = " %p*%K | %W%KServer: %|$1 {comment $2}"; + whois_oper = " %p*%K | %W%KInfo: %|{hilight $1}"; + whois_registered = " %p*%K | %W%KInfo: %|has registered this nick"; + whois_help = " %p*%K | %W%KInfo: %|available for help"; + whois_modes = " %p*%K | %W%KModes: %|{mode $1}"; + whois_realhost = " %p*%K | %W%KHostname: %|{hilight $1-}"; + whois_usermode = " %p*%K | %W%KUsermode: %|{mode $1}"; + whois_channels = " %p*%K | %W%KChannels: %|{channel $1}"; + whois_away = " %p*%K | %W%KAway: %|$1"; + whois_special = " %p*%K | %W%KInfo: %|$1"; + whois_extra = " %p*%K | %W%KInfo: %|$1"; + end_of_whois = " %r*%K | %rEnd%K of WHOIS%K"; + end_of_whowas = " %p*%K | %W%KEnd of WHOWAS"; + whois_not_found = " %r*%K | There is %rno such%K nick {channick_hilight $0}"; + who = "{channelhilight $[-10]0} %|{nick $[!9]1} $[!3]2 $[!2]3 $4@$5 {comment {hilight $6}}"; + end_of_who = " %p*%K | %W%KEnd of /WHO list"; + own_notice = "%g>%K{ownnotice notice $0}%g< %w$1"; + # own_action = "{nick $[-11]0}%n $1"; + # own_action_target = "{ownaction_target $0 $2}$1"; + own_ctcp = "{ownctcp ctcp $0}$1 $2"; + notice_server = "%g>%K{servernotice $0}%g< %w$1"; + # notice_public = "%g>%K{notice $0{pubnotice_channel%g< $1}}$2"; + notice_private = "%g>%K{notice $0{pvtnotice_host $1}}%g< %w$2"; + # action_private = "{pvtaction $0}$2"; + # action_private_query = "{pvtaction_query $0}$2"; + # action_public = " {nick $[-11]0}%n $1"; + # action_public_channel = "{pubaction $0{msgchannel $1}}$2"; + ctcp_reply = " %g*%K | %gCTCP%K {hilight $0} reply from {channick_hilight $1}: $2"; + ctcp_reply_channel = " %g*%K | %gCTCP {hilight $0} reply%K from {channick_hilight $1} in channel {channel $3}: $2"; + ctcp_ping_reply = " %g*%K | %gCTCP {hilight PING} reply%K from {channick_hilight $0}: $1.$[-3.0]2 seconds"; + ctcp_requested = " %g*%K | %K{ctcp {hilight $0} {comment $1} %grequested%K {hilight $2} from {nick $3}}"; + ctcp_requested_unknown = ""; + online = " %p*%K | %W%KUsers online: {hilight $0}"; + pong = " %g*%K | PONG %greceived%K from $0: $1"; + wallops = "{wallop WALLOP {wallop_nick $0}} $1"; + action_wallops = "{wallop WALLOP {wallop_action $0}} $1"; + kill = " %r*%K | You were %r{error killed}%K by {nick $0} {nickhost $1} {reason $2} {comment Path: $3}"; + kill_server = " %r*%K | You were %r{error killed}%K by {server $0} {reason $1} {comment Path: $2}"; + error = " %r*%K | %r{error ERROR}%K $0"; + unknown_mode = " %r*%K | %rUnknown%K mode character $0"; + not_chanop = " %r*%K | You're %rnot channel operator%K in {channel $0}"; + silenced = " %g*%K | %gSilenced%K {nick $0}"; + unsilenced = " %g*%K | %gUnsilenced%K {nick $0}"; + silence_line = "{nick $0}: silence {ban $1}"; + ask_oper_pass = " %p*%K | %W%KOperator password:"; + }; + "fe-common/perl" = { + script_not_found = " %r*%K | Script {hilight $0} %rnot found%K"; + script_not_loaded = " %r*%K | Script {hilight $0} %ris not%K loaded"; + script_loaded = " %g*%K | %gLoaded%K script {hilight $0}"; + script_unloaded = " %r*%K | %rUnloaded%K script {hilight $0}"; + no_scripts_loaded = " %p*%K | %W%KNo scripts are loaded"; + script_list_header = " %p*%K | %W%KLoaded scripts:"; + script_list_line = "$[!15]0 $1"; + script_list_footer = ""; + script_error = "{error %r*%K %rError%K in script {hilight $0}:}"; + }; + # "fe-common/irc/notifylist" = { + # notify_join = "{nick $0} [$1@$2] [{hilight $3}] has joined to $4"; + # notify_part = "{nick $0} has left $4"; + # notify_away = "{nick $0} [$5] [$1@$2] [{hilight $3}] is now away: $4"; + # notify_unaway = "{nick $0} [$4] [$1@$2] [{hilight $3}] is now unaway"; + # notify_unidle = "{nick $0} [$5] [$1@$2] [{hilight $3}] just stopped idling"; + # notify_online = "On $0: {hilight $1}"; + # notify_offline = "Offline: $0"; + # notify_list = "$0: $1 $2 $3"; + # notify_list_empty = "The notify list is empty"; + # }; + "Irssi::Script::nickcolor" = { + pubmsg_hilight = "{pubmsghinick $0 $3 $[-9]1}$2"; + }; +}; diff --git a/termite/config b/termite/config new file mode 100644 index 0000000..118e3a3 --- /dev/null +++ b/termite/config @@ -0,0 +1,42 @@ +[options] +font = Inconsolata-g 10 + +[colors] + +# special +foreground = #daba8b +foreground_bold = #daba8b +cursor = #daba8b +background = #191716 + +# black +color0 = #0f0e0d +color8 = #383332 + +# red +color1 = #845336 +color9 = #8c4f4a + +# green +color2 = #57553c +color10 = #898471 + +# yellow +color3 = #a17e3e +color11 = #c8b491 + +# blue +color4 = #43454f +color12 = #65788f + +# magenta +color5 = #604848 +color13 = #755e4a + +# cyan +color6 = #5c6652 +color14 = #718062 + +# white +color7 = #a18b62 +color15 = #bc9d66 diff --git a/termite/termite.terminfo b/termite/termite.terminfo new file mode 100644 index 0000000..2cb2ded --- /dev/null +++ b/termite/termite.terminfo @@ -0,0 +1,54 @@ +# Reconstructed via infocmp from file: /usr/share/terminfo/x/xterm-termite +xterm-termite|VTE-based terminal, + am, bce, ccc, hs, km, mc5i, mir, msgr, npc, xenl, + colors#0x100, cols#80, it#8, lines#24, pairs#0x7fff, + acsc=++\,\,--..00``aaffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + bel=^G, bold=\E[1m, cbt=\E[Z, civis=\E[?25l, + clear=\E[H\E[2J, cnorm=\E[?12l\E[?25h, cr=\r, + csr=\E[%i%p1%d;%p2%dr, cub=\E[%p1%dD, cub1=^H, + cud=\E[%p1%dB, cud1=\n, cuf=\E[%p1%dC, cuf1=\E[C, + cup=\E[%i%p1%d;%p2%dH, cuu=\E[%p1%dA, cuu1=\E[A, + cvvis=\E[?12;25h, dch=\E[%p1%dP, dch1=\E[P, dim=\E[2m, + dl=\E[%p1%dM, dl1=\E[M, dsl=\E]2;\007, ech=\E[%p1%dX, + ed=\E[J, el=\E[K, el1=\E[1K, flash=\E[?5h$<100/>\E[?5l, + fsl=^G, home=\E[H, hpa=\E[%i%p1%dG, ht=^I, hts=\EH, + ich=\E[%p1%d@, il=\E[%p1%dL, il1=\E[L, ind=\n, + indn=\E[%p1%dS, + initc=\E]4;%p1%d;rgb\:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\\, + invis=\E[8m, is2=\E[!p\E[?3;4l\E[4l\E>, kDC=\E[3;2~, + kEND=\E[1;2F, kHOM=\E[1;2H, kIC=\E[2;2~, kLFT=\E[1;2D, + kNXT=\E[6;2~, kPRV=\E[5;2~, kRIT=\E[1;2C, kb2=\EOE, kbs=^?, + kcbt=\E[Z, kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, + kdch1=\E[3~, kend=\EOF, kent=\EOM, kf1=\EOP, kf10=\E[21~, + kf11=\E[23~, kf12=\E[24~, kf13=\E[1;2P, kf14=\E[1;2Q, + kf15=\E[1;2R, kf16=\E[1;2S, kf17=\E[15;2~, kf18=\E[17;2~, + kf19=\E[18;2~, kf2=\EOQ, kf20=\E[19;2~, kf21=\E[20;2~, + kf22=\E[21;2~, kf23=\E[23;2~, kf24=\E[24;2~, + kf25=\E[1;5P, kf26=\E[1;5Q, kf27=\E[1;5R, kf28=\E[1;5S, + kf29=\E[15;5~, kf3=\EOR, kf30=\E[17;5~, kf31=\E[18;5~, + kf32=\E[19;5~, kf33=\E[20;5~, kf34=\E[21;5~, + kf35=\E[23;5~, kf36=\E[24;5~, kf37=\E[1;6P, kf38=\E[1;6Q, + kf39=\E[1;6R, kf4=\EOS, kf40=\E[1;6S, kf41=\E[15;6~, + kf42=\E[17;6~, kf43=\E[18;6~, kf44=\E[19;6~, + kf45=\E[20;6~, kf46=\E[21;6~, kf47=\E[23;6~, + kf48=\E[24;6~, kf49=\E[1;3P, kf5=\E[15~, kf50=\E[1;3Q, + kf51=\E[1;3R, kf52=\E[1;3S, kf53=\E[15;3~, kf54=\E[17;3~, + kf55=\E[18;3~, kf56=\E[19;3~, kf57=\E[20;3~, + kf58=\E[21;3~, kf59=\E[23;3~, kf6=\E[17~, kf60=\E[24;3~, + kf61=\E[1;4P, kf62=\E[1;4Q, kf63=\E[1;4R, kf7=\E[18~, + kf8=\E[19~, kf9=\E[20~, khome=\EOH, kich1=\E[2~, + kind=\E[1;2B, kmous=\E[M, knp=\E[6~, kpp=\E[5~, + kri=\E[1;2A, mc0=\E[i, mc4=\E[4i, mc5=\E[5i, meml=\El, + memu=\Em, op=\E[39;49m, rc=\E8, rev=\E[7m, ri=\EM, + rin=\E[%p1%dT, ritm=\E[23m, rmacs=\E(B, rmam=\E[?7l, + rmcup=\E[?1049l, rmir=\E[4l, rmkx=\E[?1l\E>, + rmm=\E[?1034l, rmso=\E[27m, rmul=\E[24m, rs1=\Ec, + rs2=\E[!p\E[?3;4l\E[4l\E>, sc=\E7, + setab=\E[%?%p1%{8}%<%t4%p1%d%e%p1%{16}%<%t10%p1%{8}%-%d%e48;5;%p1%d%;m, + setaf=\E[%?%p1%{8}%<%t3%p1%d%e%p1%{16}%<%t9%p1%{8}%-%d%e38;5;%p1%d%;m, + sgr=%?%p9%t\E(0%e\E(B%;\E[0%?%p6%t;1%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m, + sgr0=\E(B\E[m, sitm=\E[3m, smacs=\E(0, smam=\E[?7h, + smcup=\E[?1049h, smir=\E[4h, smkx=\E[?1h\E=, + smm=\E[?1034h, smso=\E[7m, smul=\E[4m, tbc=\E[3g, + tsl=\E]2;, u6=\E[%i%d;%dR, u7=\E[6n, u8=\E[?1;2c, u9=\E[c, + vpa=\E[%i%p1%dd, diff --git a/vim/.vimrc b/vim/.vimrc new file mode 100644 index 0000000..079d8d7 --- /dev/null +++ b/vim/.vimrc @@ -0,0 +1,118 @@ +set nocompatible " be iMproved, required +filetype off " required +syntax on +set term=xterm-256color +set laststatus=2 +set nowrap +set nofoldenable +set number +set noshowmode +set title +set novisualbell +set shortmess=atI +set nostartofline +set autoindent +set smartindent +set expandtab +set smarttab +set shiftwidth=2 +set tabstop=4 +"set textwidth=80 +"set colorcolumn=81 +set backupcopy=yes + +filetype plugin indent on + + +" set the runtime path to include Vundle and initialize +set rtp+=~/.vim/bundle/Vundle.vim +call vundle#begin() +" alternatively, pass a path where Vundle should install plugins +"call vundle#begin('~/some/path/here') + +" let Vundle manage Vundle, required +Plugin 'VundleVim/Vundle.vim' +Plugin 'vim-airline/vim-airline' +Plugin 'vim-airline/vim-airline-themes' +Plugin 'Valloric/YouCompleteMe' +Plugin 'scrooloose/nerdtree' +Plugin 'scrooloose/syntastic' +Plugin 'mattn/emmet-vim' +Plugin 'editorconfig/editorconfig-vim' +Plugin 'mxw/vim-jsx' +Plugin 'Raimondi/delimitMate' +Plugin 'tomtom/tcomment_vim' +Plugin 'tpope/vim-surround' +Plugin 'tpope/vim-fugitive' +Plugin 'hail2u/vim-css3-syntax' +Plugin 'gregsexton/MatchTag' +Plugin 'alvan/vim-closetag' +Plugin 'pangloss/vim-javascript' +Plugin 'junegunn/goyo.vim' +Plugin 'ctrlpvim/ctrlp.vim' +" All of your Plugins must be added before the following line +call vundle#end() " required +filetype plugin indent on " required +" To ignore plugin indent changes, instead use: +"filetype plugin on +" +" Brief help +" :PluginList - lists configured plugins +" :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate +" :PluginSearch foo - searches for foo; append `!` to refresh local cache +" :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal +" +" see :h vundle for more details or wiki for FAQ +" Put your non-Plugin stuff after this line + +" Airline +let g:airline_solarized_bg='dark' +let g:airline_theme='tomorrow' +let g:airline_powerline_fonts=1 + +" Closetag +let g:closetag_filenames = '*.hbs,*.html' + +" CtrlP +let g:ctrlp_user_command = ['.git', 'cd %s && git ls-files -co --exclude-standard'] +let g:ctrlp_custom_ignore = 'node_modules\|git' + +" jsx +let g:jsx_ext_required = 0 + +" Nerdtree +map :NERDTree +map :NERDTreeToggle + +" Syntastic +let g:syntastic_javascript_checkers = ['eslint'] +let g:syntastic_mode_map = { + \'mode': 'active', + \'active_filetypes': [], + \'passive_filetypes': ['html'] } +let g:syntastic_always_populate_loc_list = 1 +let g:syntastic_error_symbol = '✗✗' +let g:syntastic_style_error_symbol = '✠✠' +let g:syntastic_warning_symbol = '∆∆' +let g:syntastic_style_warning_symbol = '≈≈' +let g:syntastic_check_on_open = 1 + +fun! SetJavascriptCheckers() + let localEslint = findfile('node_modules/.bin/eslint', '.;') + if localEslint != '' + let b:syntastic_javascript_eslint_exec = fnamemodify(localEslint, ':p') + endif +endf + +fun! SetCssCheckers() + let localStylelint = findfile('node_modules/.bin/stylelint', '.;') + if localStylelint != '' + let b:syntastic_css_stylelint_exec = fnamemodify(localStylelint, ':p') + endif +endf + +if !exists('autocommands_loaded') + let autocommands_loaded = 1 + autocmd FileType javascript :call SetJavascriptCheckers() + autocmd FileType scss :call SetCssCheckers() +endif diff --git a/vim/colors/wwdc16.vim b/vim/colors/wwdc16.vim new file mode 100644 index 0000000..b32cc20 --- /dev/null +++ b/vim/colors/wwdc16.vim @@ -0,0 +1,144 @@ +" Name: WWDC16 colorscheme +" Author: Lifepillar +" License: This file is placed in the public domain + +set background=dark +hi clear +if exists('syntax_on') + syntax reset +endif +let colors_name = 'wwdc16' + +if !has('gui_running') && get(g:, "wwdc16_term_trans_bg", 0) + hi Normal ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE +else + hi Normal ctermfg=15 ctermbg=0 cterm=NONE guifg=#ffffff guibg=#292c36 gui=NONE +endif + +hi ColorColumn ctermfg=NONE ctermbg=8 cterm=NONE guifg=NONE guibg=#333333 gui=NONE guisp=NONE +hi Conceal ctermfg=2 ctermbg=NONE cterm=NONE guifg=#64878f guibg=NONE gui=NONE guisp=NONE +hi Cursor ctermfg=15 ctermbg=4 cterm=NONE guifg=#ffffff guibg=#4670d8 gui=NONE guisp=NONE +hi! link lCursor Cursor +hi CursorIM ctermfg=NONE ctermbg=15 cterm=NONE guifg=NONE guibg=#ffffff gui=NONE guisp=NONE +hi CursorColumn ctermfg=NONE ctermbg=8 cterm=NONE guifg=NONE guibg=#333333 gui=NONE guisp=NONE +hi CursorLine ctermfg=NONE ctermbg=8 cterm=NONE guifg=NONE guibg=#333333 gui=NONE guisp=NONE +hi CursorLineNr ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi DiffAdd ctermfg=11 ctermbg=0 cterm=NONE,reverse guifg=#95c76f guibg=#292c36 gui=NONE,reverse guisp=NONE +hi DiffChange ctermfg=3 ctermbg=15 cterm=NONE,reverse guifg=#d28e5d guibg=#ffffff gui=NONE,reverse guisp=NONE +hi DiffDelete ctermfg=1 ctermbg=15 cterm=NONE,reverse guifg=#dc3c3c guibg=#ffffff gui=NONE,reverse guisp=NONE +hi DiffText ctermfg=10 ctermbg=15 cterm=NONE,bold,reverse guifg=#52bd58 guibg=#ffffff gui=NONE,bold,reverse guisp=NONE +hi Directory ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi EndOfBuffer ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi Error ctermfg=1 ctermbg=15 cterm=NONE,reverse guifg=#dc3c3c guibg=#ffffff gui=NONE,reverse guisp=NONE +hi ErrorMsg ctermfg=1 ctermbg=15 cterm=NONE,reverse guifg=#dc3c3c guibg=#ffffff gui=NONE,reverse guisp=NONE +hi FoldColumn ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi Folded ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi IncSearch ctermfg=3 ctermbg=15 cterm=NONE,reverse guifg=#d28e5d guibg=#ffffff gui=NONE,standout guisp=NONE +hi LineNr ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi MatchParen ctermfg=8 ctermbg=3 cterm=NONE,bold,reverse guifg=#333333 guibg=#d28e5d gui=NONE,bold,reverse guisp=NONE +hi ModeMsg ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi MoreMsg ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi NonText ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi Pmenu ctermfg=0 ctermbg=2 cterm=NONE guifg=#292c36 guibg=#64878f gui=NONE guisp=NONE +hi PmenuSbar ctermfg=9 ctermbg=8 cterm=NONE guifg=#666666 guibg=#333333 gui=NONE guisp=NONE +hi PmenuSel ctermfg=15 ctermbg=3 cterm=NONE guifg=#ffffff guibg=#d28e5d gui=NONE guisp=NONE +hi PmenuThumb ctermfg=9 ctermbg=3 cterm=NONE guifg=#666666 guibg=#d28e5d gui=NONE guisp=NONE +hi Question ctermfg=7 ctermbg=NONE cterm=NONE guifg=#999999 guibg=NONE gui=NONE guisp=NONE +hi Search ctermfg=3 ctermbg=15 cterm=NONE,reverse guifg=#d28e5d guibg=#ffffff gui=NONE,reverse guisp=NONE +hi SignColumn ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi SpecialKey ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi SpellBad ctermfg=NONE ctermbg=NONE cterm=NONE,underline guifg=NONE guibg=NONE gui=NONE,undercurl guisp=#b73999 +hi SpellCap ctermfg=13 ctermbg=NONE cterm=NONE,underline guifg=#8485ce guibg=NONE gui=NONE,undercurl guisp=#b73999 +hi SpellLocal ctermfg=13 ctermbg=NONE cterm=NONE,underline guifg=#8485ce guibg=NONE gui=NONE,undercurl guisp=#b73999 +hi SpellRare ctermfg=13 ctermbg=NONE cterm=NONE,underline guifg=#8485ce guibg=NONE gui=NONE,undercurl guisp=#b73999 +hi StatusLine ctermfg=2 ctermbg=15 cterm=NONE,reverse guifg=#64878f guibg=#ffffff gui=NONE,reverse guisp=NONE +hi StatusLineNC ctermfg=2 ctermbg=0 cterm=NONE,reverse guifg=#64878f guibg=#292c36 gui=NONE,reverse guisp=NONE +hi TabLine ctermfg=0 ctermbg=2 cterm=NONE guifg=#292c36 guibg=#64878f gui=NONE guisp=NONE +hi TabLineFill ctermfg=0 ctermbg=2 cterm=NONE guifg=#292c36 guibg=#64878f gui=NONE guisp=NONE +hi TabLineSel ctermfg=15 ctermbg=2 cterm=NONE guifg=#ffffff guibg=#64878f gui=NONE guisp=NONE +hi Title ctermfg=3 ctermbg=NONE cterm=NONE,bold guifg=#d28e5d guibg=NONE gui=NONE,bold guisp=NONE +hi VertSplit ctermfg=2 ctermbg=2 cterm=NONE guifg=#64878f guibg=#64878f gui=NONE guisp=NONE +hi Visual ctermfg=4 ctermbg=15 cterm=NONE,reverse guifg=#4670d8 guibg=#ffffff gui=NONE,reverse guisp=NONE +hi VisualNOS ctermfg=15 ctermbg=4 cterm=NONE guifg=#ffffff guibg=#4670d8 gui=NONE guisp=NONE +hi WarningMsg ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi WildMenu ctermfg=15 ctermbg=5 cterm=NONE guifg=#ffffff guibg=#b73999 gui=NONE guisp=NONE +hi Boolean ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi Character ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi Comment ctermfg=7 ctermbg=NONE cterm=NONE guifg=#999999 guibg=NONE gui=NONE,italic guisp=NONE +hi Constant ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi Debug ctermfg=5 ctermbg=NONE cterm=NONE guifg=#b73999 guibg=NONE gui=NONE guisp=NONE +hi Delimiter ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi Float ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi Function ctermfg=10 ctermbg=NONE cterm=NONE guifg=#52bd58 guibg=NONE gui=NONE guisp=NONE +hi Identifier ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi Ignore ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi Include ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi Keyword ctermfg=2 ctermbg=NONE cterm=NONE guifg=#64878f guibg=NONE gui=NONE guisp=NONE +hi Label ctermfg=10 ctermbg=NONE cterm=NONE guifg=#52bd58 guibg=NONE gui=NONE guisp=NONE +hi Number ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi Operator ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi PreProc ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi Special ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi SpecialChar ctermfg=5 ctermbg=NONE cterm=NONE guifg=#b73999 guibg=NONE gui=NONE guisp=NONE +hi SpecialComment ctermfg=5 ctermbg=NONE cterm=NONE guifg=#b73999 guibg=NONE gui=NONE guisp=NONE +hi Statement ctermfg=2 ctermbg=NONE cterm=NONE guifg=#64878f guibg=NONE gui=NONE guisp=NONE +hi StorageClass ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi String ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi Structure ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi Todo ctermfg=5 ctermbg=NONE cterm=NONE,bold guifg=#b73999 guibg=NONE gui=NONE,bold guisp=NONE +hi Type ctermfg=13 ctermbg=NONE cterm=NONE guifg=#8485ce guibg=NONE gui=NONE guisp=NONE +hi Underlined ctermfg=NONE ctermbg=NONE cterm=NONE,underline guifg=NONE guibg=NONE gui=NONE,underline guisp=NONE +hi WWDC16Black ctermfg=0 ctermbg=NONE cterm=NONE guifg=#292c36 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Red ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi WWDC16Forestgreen ctermfg=2 ctermbg=NONE cterm=NONE guifg=#64878f guibg=NONE gui=NONE guisp=NONE +hi WWDC16Orange ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi WWDC16Blue ctermfg=4 ctermbg=NONE cterm=NONE guifg=#4670d8 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Fusia ctermfg=5 ctermbg=NONE cterm=NONE guifg=#b73999 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Bluegreen ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Grey3 ctermfg=7 ctermbg=NONE cterm=NONE guifg=#999999 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Grey1 ctermfg=8 ctermbg=NONE cterm=NONE guifg=#333333 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Grey2 ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Green ctermfg=10 ctermbg=NONE cterm=NONE guifg=#52bd58 guibg=NONE gui=NONE guisp=NONE +hi WWDC16Mintgreen ctermfg=11 ctermbg=NONE cterm=NONE guifg=#95c76f guibg=NONE gui=NONE guisp=NONE +hi WWDC16Purple ctermfg=13 ctermbg=NONE cterm=NONE guifg=#8485ce guibg=NONE gui=NONE guisp=NONE +hi WWDC16White ctermfg=15 ctermbg=NONE cterm=NONE guifg=#ffffff guibg=NONE gui=NONE guisp=NONE +hi NormalMode ctermfg=9 ctermbg=15 cterm=NONE,reverse guifg=#666666 guibg=#ffffff gui=NONE,reverse guisp=NONE +hi InsertMode ctermfg=11 ctermbg=0 cterm=NONE,reverse guifg=#95c76f guibg=#292c36 gui=NONE,reverse guisp=NONE +hi ReplaceMode ctermfg=3 ctermbg=0 cterm=NONE,reverse guifg=#d28e5d guibg=#292c36 gui=NONE,reverse guisp=NONE +hi VisualMode ctermfg=4 ctermbg=15 cterm=NONE,reverse guifg=#4670d8 guibg=#ffffff gui=NONE,reverse guisp=NONE +hi CommandMode ctermfg=5 ctermbg=15 cterm=NONE,reverse guifg=#b73999 guibg=#ffffff gui=NONE,reverse guisp=NONE +if has('nvim') + hi! link TermCursor Cursor + hi TermCursorNC ctermfg=15 ctermbg=7 cterm=NONE guifg=#ffffff guibg=#999999 gui=NONE guisp=NONE + let g:terminal_color_0='#292c36' + let g:terminal_color_1='#dc3c3c' + let g:terminal_color_2='#64878f' + let g:terminal_color_3='#d28e5d' + let g:terminal_color_4='#4670d8' + let g:terminal_color_5='#b73999' + let g:terminal_color_6='#00aba5' + let g:terminal_color_7='#999999' + let g:terminal_color_8='#333333' + let g:terminal_color_9='#666666' + let g:terminal_color_10='#52bd58' + let g:terminal_color_11='#95c76f' + let g:terminal_color_12='#4670d8' + let g:terminal_color_13='#8485ce' + let g:terminal_color_14='#00aba5' + let g:terminal_color_15='#ffffff' +endif +hi vimMapModKey ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi vimMapMod ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi vimBracket ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi vimNotation ctermfg=6 ctermbg=NONE cterm=NONE guifg=#00aba5 guibg=NONE gui=NONE guisp=NONE +hi! link vimUserFunc Function +hi gitcommitComment ctermfg=9 ctermbg=NONE cterm=NONE guifg=#666666 guibg=NONE gui=NONE,italic guisp=NONE +hi markdownHeadingDelimiter ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +hi markdownURL ctermfg=13 ctermbg=NONE cterm=NONE guifg=#8485ce guibg=NONE gui=NONE guisp=NONE +hi! link javascriptBraces Delimiter +hi SyntasticErrorSign ctermfg=1 ctermbg=NONE cterm=NONE guifg=#dc3c3c guibg=NONE gui=NONE guisp=NONE +hi SyntasticWarningSign ctermfg=3 ctermbg=NONE cterm=NONE guifg=#d28e5d guibg=NONE gui=NONE guisp=NONE +if get(g:, "wwdc16_term_italics", 0) + hi Comment cterm=italic + hi gitcommitComment cterm=italic +endif diff --git a/zsh/.zshrc b/zsh/.zshrc new file mode 100644 index 0000000..d5f3476 --- /dev/null +++ b/zsh/.zshrc @@ -0,0 +1,91 @@ +export PATH=$HOME/.dotfiles/bin:$PATH + +# Path to your oh-my-zsh installation. + export ZSH=/home/dan/.oh-my-zsh + +# Set name of the theme to load. Optionally, if you set this to "random" +# it'll load a random theme each time that oh-my-zsh is loaded. +# See https://github.com/robbyrussell/oh-my-zsh/wiki/Themes +ZSH_THEME="muse" + +# Uncomment the following line to use case-sensitive completion. +# CASE_SENSITIVE="true" + +# Uncomment the following line to use hyphen-insensitive completion. Case +# sensitive completion must be off. _ and - will be interchangeable. +# HYPHEN_INSENSITIVE="true" + +# Uncomment the following line to disable bi-weekly auto-update checks. +# DISABLE_AUTO_UPDATE="true" + +# Uncomment the following line to change how often to auto-update (in days). +# export UPDATE_ZSH_DAYS=13 + +# Uncomment the following line to disable colors in ls. +# DISABLE_LS_COLORS="true" + +# Uncomment the following line to disable auto-setting terminal title. +# DISABLE_AUTO_TITLE="true" + +# Uncomment the following line to enable command auto-correction. +# ENABLE_CORRECTION="true" + +# Uncomment the following line to display red dots whilst waiting for completion. +# COMPLETION_WAITING_DOTS="true" + +# Uncomment the following line if you want to disable marking untracked files +# under VCS as dirty. This makes repository status check for large repositories +# much, much faster. +# DISABLE_UNTRACKED_FILES_DIRTY="true" + +# Uncomment the following line if you want to change the command execution time +# stamp shown in the history command output. +# The optional three formats: "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd" +# HIST_STAMPS="mm/dd/yyyy" + +# Would you like to use another custom folder than $ZSH/custom? +# ZSH_CUSTOM=/path/to/new-custom-folder + +# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*) +# Custom plugins may be added to ~/.oh-my-zsh/custom/plugins/ +# Example format: plugins=(rails git textmate ruby lighthouse) +# Add wisely, as too many plugins slow down shell startup. +plugins=(git) + +source $ZSH/oh-my-zsh.sh + +# User configuration + +# export MANPATH="/usr/local/man:$MANPATH" + +# You may need to manually set your language environment +# export LANG=en_US.UTF-8 + +# Preferred editor for local and remote sessions +# if [[ -n $SSH_CONNECTION ]]; then +# export EDITOR='vim' +# else +# export EDITOR='mvim' +# fi + +# Compilation flags +# export ARCHFLAGS="-arch x86_64" + +# ssh +# export SSH_KEY_PATH="~/.ssh/rsa_id" + +# Set personal aliases, overriding those provided by oh-my-zsh libs, +# plugins, and themes. Aliases can be placed here, though oh-my-zsh +# users are encouraged to define aliases within the ZSH_CUSTOM folder. +# For a full list of active aliases, run `alias`. +# +# Example aliases +# alias zshconfig="mate ~/.zshrc" +# alias ohmyzsh="mate ~/.oh-my-zsh" + +source ~/.dotfiles/zsh/aliases.zsh + +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion + diff --git a/zsh/aliases.zsh b/zsh/aliases.zsh new file mode 100644 index 0000000..0edc5c8 --- /dev/null +++ b/zsh/aliases.zsh @@ -0,0 +1,16 @@ +alias zshconfig="vim ~/.zshrc" +alias ohmyzsh="vim ~/.oh-my-zsh" +alias wttr="curl wttr.in/~Brentwood+UK" +alias phoenixfm="mpv 'http://hyades.shoutca.st:8555/stream'" +alias cl="find . -type f -name '$1' | xargs wc -l" +alias fif="find . -type f -print | xargs grep '$1'" +alias hcf="sudo shutdown -h now" +alias swh="tmux split-window -h" +alias swv="tmux split-window -v" +function rp() { + tmux resize-pane -$1 $2 +} +function android-backup() { + filename=Full_Backup_$(date +%Y-%m-%d_%T).ab + adb backup -f "${filename}" --twrp system cache data boot recovery --compress +}