+Mon Mar 11 23:37:38 EET 2002 Pekka Riikonen <priikone@silcnet.org>
+
+ * Merged Irssi 0.8.2 from irssi.org CVS.
+
Sun Mar 10 23:34:48 CET 2002 Johnny Mnemonic <johnny@themnemonic.org>
* If silc_debug is TRUE, also output standard logging messages
o Configuration file additions:
+ o Add support for multipe PublicKey params in connections
+ sections. For now, add this support only in Client and not
+ in ServerConnection nor RouterConnection.
+
o Add version handling, to allow, disallow certain versions to
connect.
refresh_screen
yank_from_cutbuffer - "Undelete" line
transpose_characters - Swap current and previous character
+ escape_char - Insert the next character exactly as-is to input line
insert_text - Insert data to entry line, data may contain $variables.
stop_irc - Send SIGSTOP to client (^Z)
@SYNTAX:connect@
-4, -6: specify explicitly whether to use IPv4 or IPv6 address
- -silcnet: the specified network
+ -silcnet: the SILCNet
-host: the host
+ -!: don't autojoin channels
This command makes irssi to connect to specified server.
Current connections are kept and a new one is created.
@SYNTAX:ignore@
-regexp: <pattern> is a regular expression
- -word: <pattern> must match to full words
+ -full: <pattern> must match to full words
-pattern: <pattern> must match to the message's text
-replies: Ignore replies to nick in channels. For example
- "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone
- from Finland, but also anyone sending message
- "tofinnishnick: blahblah".
- -except: *DON'T* ignore
+ "/IGNORE -replies *!*@*.fi PUBLIC" ignores everyone
+ from Finland, but also anyone sending message
+ "tofinnishnick: blahblah".
+ -except: *DON'T* ignore - overrides an existing ignore.
-channels: Ignore only in channels
<mask>: Either a nick mask or list of channels
- <levels>: List of levels to ignore
+ <levels>: List of levels to ignore. You can use -<level> to remove levels
+ from ignore.
<^levels>: List of levels to NOT ignore
- (/ignore -except nick notices = /ignore nick ^notices)
+ (/ignore -except nick notices = /ignore nick ^notices)
-/IGNORE without any arguments displays list of ignores.
+/IGNORE without any arguments displays list of ignores. If you want to remove
+some levels of the ignore, use /IGNORE <mask> -<level> -<level2> etc
The best match always wins, so you can have:
/IGNORE * CTCPS
/IGNORE -except *!*@host.org CTCPS
+Examples:
+
+ /IGNORE #channel ALL -PUBLIC -ACTIONS - ignore all but public/actions
+ /IGNORE #channel -JOINS - don't ignore joins anymore
+ /IGNORE -replies *!user@*.host.org ALL - ignore user and all replies
+
+Some suggestions for ignoring annoying public aways:
+ /IGNORE -regexp -pattern "is (away|gone|back)" * ACTIONS
+ /IGNORE *zzz* NICKS
+ /IGNORE *afk* NICKS
+ /IGNORE *away* NICKS
+
+For regular expressions, see `man 7 regex`.
+
See also: UNIGNORE
-noauto: Don't connect to server at startup
-silcnet: Specify what network this server belongs to
-host: Specify what host name to use, if you have multiple
+ -!: don't autojoin channels
-cmdspeed: Same as /SET cmd_queue_speed, see section 3.1
-cmdmax: Same as /SET cmd_max_at_once, see section 3.1
-port: This is pretty much like the port argument later,
Server::command(cmd)
Window::command(cmd)
Windowitem::command(cmd)
- Send a command `cmd' (in current channel). This will work just as if you
- had typed `cmd' in command line, so you'll need to use /COMMANDS or the
- text will be sent to the channel.
-
- Just like above, except different calling method.
+ Send a command `cmd' (in current channel). The '/' char isn't needed.
*** Themes
Remove nick from nicklist.
Nick
-Channel::nick_find(mask)
+Channel::nick_find(nick)
Find nick from nicklist.
+Nick
+Channel::nick_find_mask(mask)
+ Find nick mask from nicklist, wildcards allowed.
+
Channel::nicks(channel)
Return a list of all nicks in channel.
<ol>
<li><a href="#c1">For all the lazy people</a></li>
+ <ul>
+ <li>This window management is just weird, I want it exactly like ircII</li>
+ </ul>
<li><a href="#c2">Basic user interface usage</a>
<ul>
<li>Split windows work in weird way</li>
</ul></li>
<li><a href="#c10">Proxies and IRC bouncers</a></li>
<li><a href="#c11">Irssi's settings</a></li>
+<li><a href="#c12">Statusbar</a>
+ <ul>
+ <li>I loaded a statusbar script but it's not visible anywhere!</li>
+ </ul></li>
</ol>
<h3><a id="c1">1. For all the lazy people</a></h3>
<p>These settings should give you pretty good defaults (the ones I use):</p>
+<p>If colors don't work, and you know you're not going to use some
+weird non-VT compatible terminal (you most probably aren't), just
+say:</p>
+
+<pre>
+ /SET term_force_colors ON
+</pre>
+
<p>I don't like automatic query windows, I don't like status window, I do
like msgs window where all messages go:</p>
/SET reuse_unused_windows ON
</pre>
+<p>Here's the settings that make irssi work exactly like ircII in window
+management (send me a note if you can think of more):</p>
+
+<pre>
+ /SET autocreate_own_query OFF
+ /SET autocreate_query_level NONE
+ /SET use_status_window OFF
+ /SET use_msgs_window OFF
+ /SET reuse_unused_windows ON
+ /SET windows_auto_renumber OFF
+
+ /SET autostick_split_windows OFF
+ /SET autoclose_windows OFF
+ /SET print_active_channel ON
+</pre>
+
<p>And example how to add servers:</p>
<p>(openprojects network, identify with nickserv and wait for 2 seconds before
/SET autocreate_windows OFF
</pre>
+<p>And if you keep all channels in one window, you most probably want
+the channel name printed in each line:</p>
+
+<pre>
+ /SET print_active_channel ON
+</pre>
+
<p>If you want to group only some channels or queries in one window,
use</p>
<h3><a id="c10">10. Proxies and IRC bouncers</a></h3>
-<p>Irssi supports connecting to IRC servers via a proxy. All proxies have
-these settings in common:</p>
+<p>Irssi supports connecting to IRC servers via a proxy. All server
+connections are then made through it, and if you've set up everything
+properly, you don't need to do any /QUOTE SERVER commands manually.</p>
+
+<p>Here's an example: You have your bouncer (lets say, BNC or BNC-like)
+listening in irc.bouncer.org port 5000. You want to use it to connect
+to servers irc.dalnet and irc.efnet.org. First you'd need to setup the
+bouncer:</p>
+
+<pre>
+ /SET use_proxy ON
+ /SET proxy_address irc.bouncer.org
+ /SET proxy_port 5000
+
+ /SET proxy_password YOUR_BNC_PASSWORD_HERE
+ /SET -clear proxy_string
+ /SET proxy_string_after conn %s %d
+</pre>
+
+<p>Then you'll need to add the server connections. These are done
+exactly as if you'd want to connect directly to them. Nothing special
+about them:</p>
+
+<pre>
+ /SERVER ADD -auto -ircnet dalnet irc.dal.net
+ /SERVER ADD -auto -ircnet efnet irc.efnet.org
+</pre>
+
+<p>With the proxy /SETs however, irssi now connects to those servers
+through your BNC. All server connections are made through them so you
+can just forget that your bouncer even exists.</p>
+
+<p>If you don't want to use the proxy for some reason, there's -noproxy
+option which you can give to /SERVER and /SERVER ADD commands.</p>
+
+<p><strong>Proxy specific settings:</strong></p>
+
+<p>All proxies have these settings in common:</p>
<pre>
/SET use_proxy ON
<pre>
/SET proxy_password your_pass
/SET -clear proxy_string
- /SET proxy_string conn %s %d
+ /SET proxy_string_after conn %s %d
</pre>
<p><strong>dircproxy</strong></p>
<p>The server name and port you give isn't used anywhere, so you can
put anything you want in there.</p>
+<p><strong>psyBNC</strong></p>
+
+<p>psyBNC has internal support for multiple servers. However, it could
+be a bit annoying to use, and some people just use different users for
+connecting to different servers. You can manage this in a bit same way
+as with dircproxy, by creating fake connections:</p>
+
+<pre>
+ /SET -clear proxy_password
+ /SET -clear proxy_string
+
+ /IRCNET ADD -user ircnetuser ircnet
+ /SERVER ADD -auto -ircnet ircnet fake.ircnet 6667 ircpass
+ /IRCNET ADD -user opnuser opn
+ /SERVER ADD -auto -ircnet opn fake.opn 6667 opnpass
+</pre>
+
+<p>So, you'll specify the usernames with /IRCNET ADD command, and the
+user's password with /SERVER ADD.</p>
+
<p><strong>Irssi proxy</strong></p>
<p>Irssi contains it's own proxy which you can build giving
or queries, list them here. For example "#boringchannel =bot1 =bot2".
If any highlighted text or message for you appears in that window, this
setting is ignored and the activity is shown.</dd>
+</dl>
<p><strong>Nick completion</strong></p>
<dt>/SET completion_char :</dt>
<dd>Completion character to use.</dd>
</dl>
+
+<h3><a id="c12">12. Statusbar</a></h3>
+
+<p><strong>/STATUSBAR</strong> displays a list of statusbars:</p>
+
+<pre>
+Name Type Placement Position Visible
+window window bottom 0 always
+window_inact window bottom 1 inactive
+prompt root bottom 100 always
+topic root top 1 always
+</pre>
+
+<p><strong>/STATUSBAR <name></strong> prints the statusbar
+settings and it's items. <strong>/STATUSBAR <name>
+ENABLE|DISABLE</strong> enables/disables the statusbar.
+<strong>/STATUSBAR <name> RESET</strong> resets the statusbar to
+it's default settings, or if the statusbar was created by you, it will be
+removed.</p>
+
+<p>Type can be window or root, meaning if the statusbar should be
+created for each split window, or just once. Placement can be top or
+bottom. Position is a number, the higher the value the lower in screen
+it is. Visible can be always, active or inactive. Active/inactive is
+useful only with split windows, one split window is active and the rest
+are inactive. These settings can be changed with:</p>
+
+<ul>
+<li>STATUSBAR <name> TYPE window|root</li>
+<li>STATUSBAR <name> PLACEMENT top|bottom</li>
+<li>STATUSBAR <name> POSITION <num></li>
+<li>STATUSBAR <name> VISIBLE always|active|inactive</li>
+</ul>
+
+<p>When loading a new statusbar scripts, you'll need to also specify
+where you want to show it. Statusbar items can be modified with:</p>
+
+<ul>
+<li>10:52 STATUSBAR <name> ADD [-before | -after <item>] [-priority #] [-alignment left|right] <item></li>
+<li>10:52 STATUSBAR <name> REMOVE <item></li>
+</ul>
+
+<p>The item name with statusbar scripts is usually same as the script's
+name. Script's documentation should tell if this isn't the case. So, to
+add mail.pl before the window activity item (see the list with
+/STATUSBAR window), use: <strong>/STATUSBAR window ADD -before act
+mail</strong>.</p>
g_return_if_fail(IS_SERVER(server));
- if (server->connrec->chatnet == NULL)
+ if (server->connrec->chatnet == NULL ||
+ server->session_reconnect)
return;
rec = chatnet_find(server->connrec->chatnet);
int irssi_gui;
int irssi_init_finished;
+int reload_config;
static char *irssi_dir, *irssi_config_file;
static GSList *dialog_type_queue, *dialog_text_queue;
return irssi_config_file;
}
+static void sig_reload_config(int signo)
+{
+ reload_config = TRUE;
+}
+
static void read_settings(void)
{
#ifndef WIN32
static int signals[] = {
- SIGHUP, SIGINT, SIGQUIT, SIGTERM,
+ SIGINT, SIGQUIT, SIGTERM,
SIGALRM, SIGUSR1, SIGUSR2
};
static char *signames[] = {
- "hup", "int", "quit", "term",
+ "int", "quit", "term",
"alrm", "usr1", "usr2"
};
sigemptyset (&act.sa_mask);
act.sa_flags = 0;
+ /* reload config on SIGHUP */
+ act.sa_handler = sig_reload_config;
+ sigaction(SIGHUP, &act, NULL);
+
for (n = 0; n < sizeof(signals)/sizeof(signals[0]); n++) {
act.sa_handler = find_substr(ignores, signames[n]) ?
SIG_IGN : SIG_DFL;
extern int irssi_gui;
extern int irssi_init_finished; /* TRUE after "irssi init finished" signal is sent */
+extern int reload_config; /* TRUE after received SIGHUP. */
void core_init_paths(int argc, char *argv[]);
#else
hp = gethostbyname(addr);
if (hp == NULL)
- return -1;
- //return h_errno;
+ return h_errno;
ip4->family = AF_INET;
memcpy(&ip4->ip, hp->h_addr, 4);
int is_ipv6_address(const char *host)
{
while (*host != '\0') {
- if (*host != ':' && !isxdigit(*host))
+ if (*host != ':' && !i_isxdigit(*host))
return 0;
host++;
}
MODULE_DATA_INIT(query);
query->type = module_get_uniq_id_str("WINDOW ITEM TYPE", "QUERY");
query->destroy = (void (*) (WI_ITEM_REC *)) query_destroy;
+ query->last_unread_msg = time(NULL);
+
if (query->server_tag != NULL) {
query->server = server_find_tag(query->server_tag);
if (query->server != NULL) {
char *address;
char *server_tag;
+time_t last_unread_msg;
+
unsigned int unwanted:1; /* TRUE if the other side closed or
some error occured (DCC chats!) */
unsigned int destroying:1;
server->connect_pipe[1] = NULL;
/* figure out if we should use IPv4 or v6 address */
- ip = iprec.error != 0 ? NULL : iprec.ip6.family == 0 ||
- (server->connrec->family == AF_INET && iprec.ip4.family != 0) ?
- &iprec.ip4 : &iprec.ip6;
- if (iprec.ip4.family != 0 && server->connrec->family == 0 &&
- !settings_get_bool("resolve_prefer_ipv6"))
- ip = &iprec.ip4;
+ if (iprec.error != 0) {
+ /* error */
+ ip = NULL;
+ } else if (server->connrec->family == AF_INET) {
+ /* force IPv4 connection */
+ ip = iprec.ip4.family == 0 ? NULL : &iprec.ip4;
+ } else if (server->connrec->family == AF_INET6) {
+ /* force IPv6 connection */
+ ip = iprec.ip6.family == 0 ? NULL : &iprec.ip6;
+ } else {
+ /* pick the one that was found, or if both do it like
+ /SET resolve_prefer_ipv6 says. */
+ ip = iprec.ip6.family != 0 &&
+ settings_get_bool("resolve_prefer_ipv6") ?
+ &iprec.ip6 : &iprec.ip4;
+ }
conn = server->connrec;
port = conn->proxy != NULL ? conn->proxy_port : conn->port;
if (handle == NULL) {
/* failed */
- if (iprec.error != 0 && net_hosterror_notfound(iprec.error)) {
+ if (ip == NULL && (iprec.error == 0 ||
+ net_hosterror_notfound(iprec.error))) {
/* IP wasn't found for the host, don't try to reconnect
back to this server */
server->dns_error = TRUE;
}
- if (iprec.error == 0) {
+ if (ip != NULL) {
/* connect() failed */
errormsg = g_strerror(errno);
+ } else if (iprec.error == 0) {
+ /* forced IPv4 or IPv6 address but it wasn't found */
+ errormsg = server->connrec->family == AF_INET ?
+ "IPv4 address not found for host" :
+ "IPv6 address not found for host";
} else {
/* gethostbyname() failed */
errormsg = iprec.errorstr != NULL ? iprec.errorstr :
config_node_set_str(config, node, "name", channel->name);
config_node_set_str(config, node, "topic", channel->topic);
+ config_node_set_str(config, node, "topic_by", channel->topic_by);
+ config_node_set_int(config, node, "topic_time", channel->topic_time);
config_node_set_str(config, node, "key", channel->key);
signal_emit("session save channel", 3, channel, config, node);
channel = CHAT_PROTOCOL(server)->channel_create(server, name, TRUE);
channel->topic = g_strdup(config_node_get_str(node, "topic", NULL));
+ channel->topic_by = g_strdup(config_node_get_str(node, "topic_by", NULL));
+ channel->topic_time = config_node_get_int(node, "topic_time", 0);
channel->key = g_strdup(config_node_get_str(node, "key", NULL));
channel->session_rejoin = TRUE;
joindata = chanrec->get_join_data(chanrec);
window_bind_add(window_item_window(chanrec),
chanrec->server->tag, chanrec->name);
- channel_destroy(chanrec);
+
+ /* FIXME: kludgy kludgy... and it relies on channel not
+ being destroyed immediately.. */
+ signal_emit("command part", 3, data, server, item);
+ chanrec->left = TRUE;
+ channel_destroy(chanrec);
server->channels_join(server, joindata, FALSE);
g_free(joindata);
recvlen = read(f, tmpbuf, sizeof(tmpbuf));
ret = line_split(tmpbuf, recvlen, &str, &buffer);
- if (ret > 0)
- printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP, "%s", str);
+ if (ret > 0) {
+ printtext(NULL, NULL, MSGLEVEL_CLIENTCRAP |
+ MSGLEVEL_NEVER, "%s", str);
+ }
} while (ret > 0);
line_split_free(buffer);
#include "signals.h"
#include "levels.h"
#include "ignore.h"
+#include "servers.h"
static void sig_message_public(SERVER_REC *server, const char *msg,
const char *nick, const char *address,
const char *nick, const char *kicker,
const char *address, const char *reason)
{
- if (ignore_check(server, kicker, address,
+ /* never ignore if you were kicked */
+ if (g_strcasecmp(nick, server->nick) != 0 &&
+ ignore_check(server, kicker, address,
channel, reason, MSGLEVEL_KICKS))
signal_stop();
}
cmd_params_free(free_arg);
}
-static int window_has_query(WINDOW_REC *window)
+static void window_reset_query_timestamps(WINDOW_REC *window)
{
GSList *tmp;
- g_return_val_if_fail(window != NULL, FALSE);
+ if (window == NULL)
+ return;
for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
- if (IS_QUERY(tmp->data))
- return TRUE;
- }
+ QUERY_REC *query = QUERY(tmp->data);
- return FALSE;
+ if (query != NULL)
+ query->last_unread_msg = time(NULL);
+ }
}
static void sig_window_changed(WINDOW_REC *window, WINDOW_REC *old_window)
{
- if (query_auto_close <= 0)
- return;
-
- /* reset the window's last_line timestamp so that query doesn't get
- closed immediately after switched to the window, or after changed
- to some other window from it */
- if (window != NULL && window_has_query(window))
- window->last_line = time(NULL);
- if (old_window != NULL && window_has_query(old_window))
- old_window->last_line = time(NULL);
+ /* reset the queries last_unread_msg so query doesn't get closed
+ immediately after switched to the window, or after changed to
+ some other window from it */
+ window_reset_query_timestamps(window);
+ window_reset_query_timestamps(old_window);
}
static int sig_query_autoclose(void)
next = tmp->next;
window = window_item_window((WI_ITEM_REC *) rec);
- if (window != active_win && rec->data_level == 0 &&
- now-window->last_line > query_auto_close)
+ if (window != active_win && rec->data_level < DATA_LEVEL_MSG &&
+ now-rec->last_unread_msg > query_auto_close)
query_destroy(rec);
}
return 1;
static void sig_message_private(SERVER_REC *server, const char *msg,
const char *nick, const char *address)
{
+ QUERY_REC *query;
+
/* create query window if needed */
- privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
+ query = privmsg_get_query(server, nick, FALSE, MSGLEVEL_MSGS);
+
+ /* reset the query's last_unread_msg timestamp */
+ if (query != NULL)
+ query->last_unread_msg = time(NULL);
}
static void read_settings(void)
return window->name;
}
+#define WINDOW_LEVEL_MATCH(window, server, level) \
+ (((window)->level & level) && \
+ (server == NULL || (window)->active_server == server))
+
WINDOW_REC *window_find_level(void *server, int level)
{
- WINDOW_REC *match;
GSList *tmp;
- match = NULL;
+ /* prefer active window if possible */
+ if (WINDOW_LEVEL_MATCH(active_win, server, level))
+ return active_win;
+
for (tmp = windows; tmp != NULL; tmp = tmp->next) {
WINDOW_REC *rec = tmp->data;
- if ((server == NULL || rec->active_server == server) &&
- (rec->level & level)) {
- if (server == NULL || rec->active_server == server)
- return rec;
- match = rec;
- }
+ if (WINDOW_LEVEL_MATCH(rec, server, level))
+ return rec;
}
- return match;
+ return NULL;
}
WINDOW_REC *window_find_closest(void *server, const char *name, int level)
return ret;
}
+char *format_add_lineend(const char *text, const char *linestart)
+{
+ GString *str;
+ char *ret;
+
+ if (linestart == NULL)
+ return g_strdup(text);
+
+ if (strchr(text, '\n') == NULL)
+ return g_strconcat(text, linestart, NULL);
+
+ str = g_string_new(NULL);
+ while (*text != '\0') {
+ if (*text == '\n')
+ g_string_append(str, linestart);
+ g_string_append_c(str, *text);
+ text++;
+ }
+ g_string_append(str, linestart);
+
+ ret = str->str;
+ g_string_free(str, FALSE);
+ return ret;
+}
+
#define LINE_START_IRSSI_LEVEL \
(MSGLEVEL_CLIENTERROR | MSGLEVEL_CLIENTNOTICE)
flags |= GUI_PRINT_FLAG_REVERSE;
break;
default:
- if (num >= 30 && num <= 37)
+ if (num >= 30 && num <= 37) {
+ if (fg == -1) fg = 0;
fg = (fg & 0xf8) | ansitab[num-30];
+ }
if (num >= 40 && num <= 47) {
if (bg == -1) bg = 0;
bg = (bg & 0xf8) | ansitab[num-40];
}
}
- if (*p == 27 && p[1] != '\0')
+ if (*p == 27 && p[1] != '\0') {
+ p++;
p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
+ }
if (!IS_COLOR_CODE(*p))
*out++ = *p;
/* send a fully parsed text string for GUI to print */
void format_send_to_gui(TEXT_DEST_REC *dest, const char *text)
{
+ THEME_REC *theme;
char *dup, *str, *ptr, type;
int fgcolor, bgcolor;
int flags;
+ theme = dest->window != NULL && dest->window->theme != NULL ?
+ dest->window->theme : current_theme;
+
dup = str = g_strdup(text);
- flags = 0; fgcolor = -1; bgcolor = -1;
+ flags = 0; fgcolor = theme->default_color; bgcolor = -1;
while (*str != '\0') {
type = '\0';
for (ptr = str; *ptr != '\0'; ptr++) {
break;
}
case FORMAT_STYLE_DEFAULTS:
- fgcolor = bgcolor = -1;
+ fgcolor = theme->default_color;
+ bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT;
break;
case FORMAT_STYLE_CLRTOEOL:
break;
case 15:
/* remove all styling */
- fgcolor = bgcolor = -1;
+ fgcolor = theme->default_color;
+ bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT;
break;
case 22:
case 27:
/* ansi color code */
ptr = (char *)
- get_ansi_color(dest->window == NULL || dest->window->theme == NULL ?
- current_theme : dest->window->theme,
- ptr,
+ get_ansi_color(theme, ptr,
hide_text_style ? NULL : &fgcolor,
hide_text_style ? NULL : &bgcolor,
hide_text_style ? NULL : &flags);
TEXT_DEST_REC *dest, int formatnum,
char **args);
-/* add `linestart' to start of each line in `text'. `text' may contain
+/* add `linestart' to start/end of each line in `text'. `text' may contain
multiple lines separated with \n. */
char *format_add_linestart(const char *text, const char *linestart);
+char *format_add_lineend(const char *text, const char *linestart);
/* return the "-!- " text at the start of the line */
char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest);
command_id = strchr(settings_get_str("cmdchars"), *id) != NULL;
if (command_id) {
/* using shortcut to command id */
- keydata = g_strconcat(id, " ", keydata, NULL);
+ keydata = g_strconcat(id+1, " ", keydata, NULL);
id = "command";
}
static void print_line(TEXT_DEST_REC *dest, const char *text)
{
+ THEME_REC *theme;
char *str, *tmp, *stripped;
g_return_if_fail(dest != NULL);
g_return_if_fail(text != NULL);
- tmp = format_get_level_tag(window_get_theme(dest->window), dest);
- str = format_add_linestart(text, tmp);
+ theme = window_get_theme(dest->window);
+ tmp = format_get_level_tag(theme, dest);
+ str = !theme->info_eol ? format_add_linestart(text, tmp) :
+ format_add_lineend(text, tmp);
g_free_not_null(tmp);
/* send both the formatted + stripped (for logging etc.) */
static void sig_print_text(TEXT_DEST_REC *dest, const char *text)
{
+ THEME_REC *theme;
char *str, *tmp;
g_return_if_fail(dest != NULL);
/* add timestamp/server tag here - if it's done in print_line()
it would be written to log files too */
- tmp = format_get_line_start(window_get_theme(dest->window),
- dest, time(NULL));
- str = format_add_linestart(text, tmp);
+ theme = window_get_theme(dest->window);
+ tmp = format_get_line_start(theme, dest, time(NULL));
+ str = !theme->info_eol ? format_add_linestart(text, tmp) :
+ format_add_lineend(text, tmp);
+
g_free_not_null(tmp);
format_send_to_gui(dest, str);
}
index = (flags & EXPAND_FLAG_IGNORE_REPLACES) ? -1 :
- theme->replace_keys[(int) chr];
+ theme->replace_keys[(int) (unsigned char) chr];
if (index == -1)
g_string_append_c(str, chr);
else {
if (node->key != NULL && node->value != NULL) {
for (p = node->key; *p != '\0'; p++)
- theme->replace_keys[(int) *p] = index;
+ theme->replace_keys[(int) (unsigned char) *p] = index;
theme->replace_values =
g_slist_append(theme->replace_values,
theme->default_color =
config_get_int(config, NULL, "default_color", -1);
+ theme->info_eol = config_get_bool(config, NULL, "info_eol", FALSE);
+
/* FIXME: remove after 0.7.99 */
if (theme->default_color == 0 &&
config_get_int(config, NULL, "default_real_color", -1) != -1)
words = 0;
do {
+ ptr++;
+
words++;
ptr = strchr(ptr, ' ');
} while (ptr != NULL);
int default_color; /* default color to use with text with default
background. default is -1 which means the
default color set by terminal */
+ unsigned int info_eol:1; /* show the timestamp/servertag at the
+ end of the line, not at beginning */
+
GHashTable *modules;
int replace_keys[256]; /* index to replace_values for each char */
return;
entry->text_alloc = nearest_power(entry->text_alloc+grow_size);
- entry->text = g_realloc(entry->text, entry->text_alloc);
+ entry->text = g_realloc(entry->text,
+ sizeof(unichar) * entry->text_alloc);
}
GUI_ENTRY_REC *gui_entry_create(int xpos, int ypos, int width, int utf8)
g_return_val_if_fail(entry != NULL, NULL);
+ if (entry->cutbuffer == NULL)
+ return NULL;
+
buf = g_malloc(entry->cutbuffer_len*6 + 1);
if (entry->utf8)
utf16_to_utf8(entry->cutbuffer, buf);
return buf;
}
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size)
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer)
{
g_return_if_fail(entry != NULL);
if (entry->pos < size)
return;
- /* put erased text to cutbuffer */
- if (entry->cutbuffer == NULL || entry->cutbuffer_len < size) {
- g_free(entry->cutbuffer);
- entry->cutbuffer = g_new(unichar, size+1);
- }
+ if (update_cutbuffer) {
+ /* put erased text to cutbuffer */
+ if (entry->cutbuffer == NULL || entry->cutbuffer_len < size) {
+ g_free(entry->cutbuffer);
+ entry->cutbuffer = g_new(unichar, size+1);
+ }
- entry->cutbuffer_len = size;
- entry->cutbuffer[size] = '\0';
- memcpy(entry->cutbuffer, entry->text + entry->pos - size,
- size * sizeof(unichar));
+ entry->cutbuffer_len = size;
+ entry->cutbuffer[size] = '\0';
+ memcpy(entry->cutbuffer, entry->text + entry->pos - size,
+ size * sizeof(unichar));
+ }
if (size == 0) {
/* we just wanted to clear the cutbuffer */
}
if (to > 0) to++;
- gui_entry_erase(entry, entry->pos-to);
+ gui_entry_erase(entry, entry->pos-to, TRUE);
}
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space)
size = to-entry->pos;
entry->pos = to;
- gui_entry_erase(entry, size);
+ gui_entry_erase(entry, size, TRUE);
}
void gui_entry_transpose_chars(GUI_ENTRY_REC *entry)
void gui_entry_insert_char(GUI_ENTRY_REC *entry, unichar chr);
char *gui_entry_get_cutbuffer(GUI_ENTRY_REC *entry);
-void gui_entry_erase(GUI_ENTRY_REC *entry, int size);
+void gui_entry_erase(GUI_ENTRY_REC *entry, int size, int update_cutbuffer);
void gui_entry_erase_word(GUI_ENTRY_REC *entry, int to_space);
void gui_entry_erase_next_word(GUI_ENTRY_REC *entry, int to_space);
colors wrap to 0, 1, ... */
if (*bg >= 0) *bg = mirc_colors[*bg % 16];
if (*fg >= 0) *fg = mirc_colors[*fg % 16];
+ if (settings_get_bool("mirc_blink_fix"))
+ *bg &= ~0x08;
}
if (*fg < 0 || *fg > 15)
- *fg = current_theme->default_color;
+ *fg = -1;
if (*bg < 0 || *bg > 15)
*bg = -1;
static KEYBOARD_REC *keyboard;
static ENTRY_REDIRECT_REC *redir;
+static int escape_next_key;
static int readtag;
static time_t idle_time;
str[utf16_char_to_utf8(key, str)] = '\0';
}
- if (!key_pressed(keyboard, str)) {
- /* key wasn't used for anything, print it */
+ if (escape_next_key || !key_pressed(keyboard, str)) {
+ /* key wasn't used for anything, print it */
+ escape_next_key = FALSE;
gui_entry_insert_char(active_entry, key);
}
}
char *str, *add_history;
str = gui_entry_get_text(active_entry);
- if (str == NULL || *str == '\0') {
+ if (str == NULL || (*str == '\0' && redir == NULL)) {
g_free(str);
return;
}
static void key_erase_line(void)
{
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len);
+ gui_entry_erase(active_entry, active_entry->text_len, TRUE);
}
static void key_erase_to_beg_of_line(void)
int pos;
pos = gui_entry_get_pos(active_entry);
- gui_entry_erase(active_entry, pos);
+ gui_entry_erase(active_entry, pos, TRUE);
}
static void key_erase_to_end_of_line(void)
pos = gui_entry_get_pos(active_entry);
gui_entry_set_pos(active_entry, active_entry->text_len);
- gui_entry_erase(active_entry, active_entry->text_len - pos);
+ gui_entry_erase(active_entry, active_entry->text_len - pos, TRUE);
}
static void key_yank_from_cutbuffer(void)
char *cutbuffer;
cutbuffer = gui_entry_get_cutbuffer(active_entry);
- if (cutbuffer != NULL)
+ if (cutbuffer != NULL) {
gui_entry_insert_text(active_entry, cutbuffer);
+ g_free(cutbuffer);
+ }
}
static void key_transpose_characters(void)
{
if (gui_entry_get_pos(active_entry) < active_entry->text_len) {
gui_entry_move_pos(active_entry, 1);
- gui_entry_erase(active_entry, 1);
+ gui_entry_erase(active_entry, 1, FALSE);
}
}
static void key_backspace(void)
{
- gui_entry_erase(active_entry, 1);
+ gui_entry_erase(active_entry, 1, FALSE);
}
static void key_delete_previous_word(void)
}
}
+static void key_escape(void)
+{
+ escape_next_key = TRUE;
+}
+
static void key_insert_text(const char *data)
{
char *str;
char *key, data[MAX_INT_STRLEN];
int n;
+ escape_next_key = FALSE;
redir = NULL;
idle_time = time(NULL);
input_listen_init(STDIN_FILENO);
key_bind("scroll_end", "End of the window", "", NULL, (SIGNAL_FUNC) key_scroll_end);
/* inserting special input characters to line.. */
+ key_bind("escape_char", "Escape the next keypress", NULL, NULL, (SIGNAL_FUNC) key_escape);
key_bind("insert_text", "Append text to line", NULL, NULL, (SIGNAL_FUNC) key_insert_text);
/* autoreplaces */
key_unbind("scroll_start", (SIGNAL_FUNC) key_scroll_start);
key_unbind("scroll_end", (SIGNAL_FUNC) key_scroll_end);
+ key_unbind("escape_char", (SIGNAL_FUNC) key_escape);
key_unbind("insert_text", (SIGNAL_FUNC) key_insert_text);
key_unbind("change_window", (SIGNAL_FUNC) key_change_window);
key_unbind("stop_irc", (SIGNAL_FUNC) key_sig_stop);
window_reparent(active_win, rec);
}
-static void windows_print_sticky(MAIN_WINDOW_REC *win)
+static void windows_print_sticky(WINDOW_REC *win)
{
+ MAIN_WINDOW_REC *mainwin;
GSList *tmp, *list;
GString *str;
+ mainwin = WINDOW_MAIN(win);
+
/* convert to string */
str = g_string_new(NULL);
- list = get_sticky_windows_sorted(win);
+ list = get_sticky_windows_sorted(mainwin);
for (tmp = list; tmp != NULL; tmp = tmp->next) {
WINDOW_REC *rec = tmp->data;
g_string_truncate(str, str->len-2);
g_slist_free(list);
- printformat_window(win->active, MSGLEVEL_CLIENTCRAP,
+ printformat_window(win, MSGLEVEL_CLIENTCRAP,
TXT_WINDOW_INFO_STICKY, str->str);
g_string_free(str, TRUE);
}
static void sig_window_print_info(WINDOW_REC *win)
{
+ GUI_WINDOW_REC *gui;
+
+ gui = WINDOW_GUI(win);
+ if (gui->use_scroll) {
+ printformat_window(win, MSGLEVEL_CLIENTCRAP,
+ TXT_WINDOW_INFO_SCROLL,
+ gui->scroll ? "yes" : "no");
+ }
+
if (WINDOW_MAIN(win)->sticky_windows)
- windows_print_sticky(WINDOW_MAIN(win));
+ windows_print_sticky(win);
}
void mainwindows_init(void)
{ "window_set_sticky", "Window set sticky", 0 },
{ "window_unset_sticky", "Window is not sticky anymore", 0 },
{ "window_info_sticky", "Sticky : $0", 1, { 0 } },
+ { "window_info_scroll", "Scroll : $0", 1, { 0 } },
{ "window_scroll", "Window scroll mode is now $0", 1, { 0 } },
{ "window_scroll_unknown", "Unknown scroll mode $0, must be ON, OFF or DEFAULT", 1, { 0 } },
TXT_WINDOW_NOT_STICKY,
TXT_WINDOW_SET_STICKY,
TXT_WINDOW_UNSET_STICKY,
- TXT_WINDOW_INFO_STICKY,
+ TXT_WINDOW_INFO_STICKY,
+ TXT_WINDOW_INFO_SCROLL,
TXT_WINDOW_SCROLL,
TXT_WINDOW_SCROLL_UNKNOWN,
statusbar_item_register("window_empty", NULL, item_window_empty);
statusbar_item_register("prompt", NULL, item_window_active);
statusbar_item_register("prompt_empty", NULL, item_window_empty);
+ statusbar_item_register("topic", NULL, item_window_active);
+ statusbar_item_register("topic_empty", NULL, item_window_empty);
statusbar_item_register("lag", NULL, item_lag);
statusbar_item_register("act", NULL, item_act);
statusbar_item_register("more", NULL, item_more);
return value;
}
+static char *reverse_controls(const char *str)
+{
+ GString *out;
+ char *ret;
+
+ out = g_string_new(NULL);
+
+ while (*str != '\0') {
+ if ((unsigned char) *str < 32 ||
+ (term_type == TERM_TYPE_8BIT &&
+ (unsigned char) (*str & 0x7f) < 32)) {
+ /* control char */
+ g_string_sprintfa(out, "%%8%c%%8",
+ 'A'-1 + (*str & 0x7f));
+ } else {
+ g_string_append_c(out, *str);
+ }
+
+ str++;
+ }
+
+ ret = out->str;
+ g_string_free(out, FALSE);
+ return ret;
+}
+
void statusbar_item_default_handler(SBAR_ITEM_REC *item, int get_size_only,
const char *str, const char *data,
int escape_vars)
tmpstr = strip_codes(tmpstr2);
g_free(tmpstr2);
+ /* show all control chars reversed */
+ tmpstr2 = reverse_controls(tmpstr);
+ g_free(tmpstr);
+
+ tmpstr = tmpstr2;
if (get_size_only) {
item->min_size = item->max_size = format_get_length(tmpstr);
} else {
if (term_detached) return;
if (vcmove) term_move_real();
- term_printed_text(1);
+
+ /* With UTF-8, move cursor only if this char is either single-byte
+ (8. bit on) or beginning of multibyte (7+8 bits on) */
+ if (term_type != TERM_TYPE_UTF8 ||
+ (chr & 0x80) == 0 || (chr & 0x40) == 0) {
+ term_printed_text(1);
+ }
+
if (vcy != term_height || vcx != 0)
putc(chr, window->term->out);
}
static void term_addch_utf8(TERM_WINDOW *window, unichar chr)
{
- unsigned char buf[10];
+ char buf[10];
int i, len;
len = utf16_char_to_utf8(chr, buf);
if (i >= term_inbuf_pos)
term_inbuf_pos = 0;
- else {
+ else if (i > 0) {
memmove(term_inbuf+i, term_inbuf, term_inbuf_pos-i);
- term_inbuf_pos = i;
+ term_inbuf_pos -= i;
}
}
settings_add_bool("lookandfeel", "colors", TRUE);
settings_add_bool("lookandfeel", "term_force_colors", FALSE);
settings_add_bool("lookandfeel", "term_auto_detach", FALSE);
+ settings_add_bool("lookandfeel", "mirc_blink_fix", FALSE);
settings_add_str("lookandfeel", "term_type", "8bit");
force_colors = FALSE;
package Irssi;
use strict;
-use vars qw($VERSION @ISA @EXPORT @EXPORT_OK);
+use vars qw($VERSION $in_irssi @ISA @EXPORT @EXPORT_OK);
sub VERSION {
my $version = $_[1];
@EXPORT_OK = grep { /[a-z]/ && defined *{$_}{CODE} } keys %Irssi::;
}
+sub in_irssi {
+ return $in_irssi;
+}
+
$VERSION = "0.9";
require Exporter;
);
@EXPORT_OK = qw();
-bootstrap Irssi $VERSION if (!Irssi::Core::is_static());
+my $static = 0;
+
+eval {
+ $static = Irssi::Core::is_static();
+};
+$in_irssi = $@ ? 0 : 1;
-@Irssi::Channel::ISA = qw(Irssi::Windowitem);
-@Irssi::Query::ISA = qw(Irssi::Windowitem);
+if (!in_irssi()) {
+ print "Warning: This script should be run inside irssi\n";
+} else {
+ bootstrap Irssi $VERSION if (!$static);
-Irssi::init();
+ @Irssi::Channel::ISA = qw(Irssi::Windowitem);
+ @Irssi::Query::ISA = qw(Irssi::Windowitem);
-Irssi::EXPORT_ALL();
+ Irssi::init();
+
+ Irssi::EXPORT_ALL();
+}
1;
perl_window_item_fill_hash(hv, (WI_ITEM_REC *) channel);
+ if (channel->ownnick != NULL)
+ hv_store(hv, "ownnick", 7, iobject_bless(channel->ownnick), 0);
+
hv_store(hv, "topic", 5, new_pv(channel->topic), 0);
hv_store(hv, "topic_by", 8, new_pv(channel->topic_by), 0);
hv_store(hv, "topic_time", 10, newSViv(channel->topic_time), 0);
* SYNOPSIS
*
* bool silc_idcache_add(SilcIDCache cache, char *name, void *id,
- * void *context, int expire);
+ * void *context, int expire, SilcIDCacheEntry *ret);
*
* DESCRIPTION
*