1
0
mirror of https://github.com/irssi/irssi.git synced 2025-02-02 15:08:01 -05:00
irssi/src/fe-common/core/formats.c
Ailin Nemui 96a292d40e Finish 256 colour support for Irssi
256 colour patch is cleaned up and the remaining cases are made work,
this includes especially Theme support, which was not implemented
before. Changes not related to colours were reverted again, making a
review of the two patches against master easier to follow.

As a byproduct of the Hex-colour code parser, the 24bit colours are
also implemented. Actually using them in the terminal is guarded by a
compile time switch (as well as a run time switch), as it breaks the
existing colour protocol and requires additional storage.

To make a seamless usage, down-conversion is provided for 8 and 16
colours.

Diverging from Tom's approach, the colour protocol is reverted back to
the original one. Unfortunately, the changes required in the Theme
engine will break the API.

For more details, please refer to the patch documentation at either
http://irssi-docs.wikispaces.com/Notes-256-Colour or
https://github.com/shabble/irssi-docs/wiki/Notes-256-Colour
2014-06-30 02:41:34 +02:00

1404 lines
31 KiB
C

/*
formats.c : irssi
Copyright (C) 1999-2000 Timo Sirainen
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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "module.h"
#include "module-formats.h"
#include "signals.h"
#include "special-vars.h"
#include "settings.h"
#include "levels.h"
#include "servers.h"
#include "fe-windows.h"
#include "window-items.h"
#include "formats.h"
#include "themes.h"
#include "recode.h"
#include "utf8.h"
static const char *format_backs = "04261537";
static const char *format_fores = "kbgcrmyw";
static const char *format_boldfores = "KBGCRMYW";
static int signal_gui_print_text;
static int hide_text_style, hide_server_tags, hide_colors;
static int timestamp_level;
static int timestamp_timeout;
int format_find_tag(const char *module, const char *tag)
{
FORMAT_REC *formats;
int n;
formats = g_hash_table_lookup(default_formats, module);
if (formats == NULL)
return -1;
for (n = 0; formats[n].def != NULL; n++) {
if (formats[n].tag != NULL &&
g_ascii_strcasecmp(formats[n].tag, tag) == 0)
return n;
}
return -1;
}
static void format_expand_code(const char **format, GString *out, int *flags)
{
int set;
if (flags == NULL) {
/* flags are being ignored - skip the code */
while (**format != ']')
(*format)++;
return;
}
set = TRUE;
(*format)++;
while (**format != ']' && **format != '\0') {
if (**format == '+')
set = TRUE;
else if (**format == '-')
set = FALSE;
else switch (**format) {
case 's':
case 'S':
*flags |= !set ? PRINT_FLAG_UNSET_LINE_START :
**format == 's' ? PRINT_FLAG_SET_LINE_START :
PRINT_FLAG_SET_LINE_START_IRSSI;
break;
case 't':
*flags |= set ? PRINT_FLAG_SET_TIMESTAMP :
PRINT_FLAG_UNSET_TIMESTAMP;
break;
case 'T':
*flags |= set ? PRINT_FLAG_SET_SERVERTAG :
PRINT_FLAG_UNSET_SERVERTAG;
break;
}
(*format)++;
}
}
void format_ext_color(GString *out, int bg, int color)
{
g_string_append_c(out, 4);
if (bg && color < 0x10)
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
if (color < 0x10)
g_string_append_c(out, color+'0');
else {
if (color < 0x60)
g_string_append_c(out, bg ? FORMAT_COLOR_EXT1_BG
: FORMAT_COLOR_EXT1);
else if (color < 0xb0)
g_string_append_c(out, bg ? FORMAT_COLOR_EXT2_BG
: FORMAT_COLOR_EXT2);
else
g_string_append_c(out, bg ? FORMAT_COLOR_EXT3_BG
: FORMAT_COLOR_EXT3);
g_string_append_c(out, FORMAT_COLOR_NOCHANGE + ((color-0x10)%0x50));
}
if (!bg && color < 0x10)
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
}
#ifdef TERM_TRUECOLOR
void unformat_24bit_color(char **ptr, int off, int *fgcolor, int *bgcolor, int *flags)
{
unsigned int color;
unsigned char rgbx[4];
unsigned int i;
for (i = 0; i < 4; ++i) {
rgbx[i] = (*ptr)[i + off];
}
rgbx[3] -= 0x20;
*ptr += 4;
for (i = 0; i < 3; ++i) {
if (rgbx[3] & (0x10 << i))
rgbx[i] -= 0x20;
}
color = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
if (rgbx[3] & 0x1) {
*bgcolor = color;
*flags |= GUI_PRINT_FLAG_COLOR_24_BG;
}
else {
*fgcolor = color;
*flags |= GUI_PRINT_FLAG_COLOR_24_FG;
}
}
#endif
void format_24bit_color(GString *out, int bg, unsigned int color)
{
unsigned char rgb[] = { color >> 16, color >> 8, color };
#ifdef TERM_TRUECOLOR
unsigned char x = bg ? 0x1 : 0;
unsigned int i;
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_COLOR_24);
for (i = 0; i < 3; ++i) {
if (rgb[i] > 0x20)
g_string_append_c(out, rgb[i]);
else {
g_string_append_c(out, 0x20 + rgb[i]);
x |= 0x10 << i;
}
}
g_string_append_c(out, 0x20 + x);
#else /* !TERM_TRUECOLOR */
format_ext_color(out, bg, color_24bit_256(rgb));
#endif /* TERM_TRUECOLOR */
}
int format_expand_styles(GString *out, const char **format, int *flags)
{
int retval = 1;
char *p, fmt;
/* storage for numerical parsing code for %x/X formats. */
int tmp;
unsigned int tmp2;
fmt = **format;
switch (fmt) {
case '{':
case '}':
case '%':
/* escaped char */
g_string_append_c(out, fmt);
break;
case 'U':
/* Underline on/off */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_UNDERLINE);
break;
case '9':
case '_':
/* bold on/off */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_BOLD);
break;
case '8':
/* reverse */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_REVERSE);
break;
case ':':
/* Newline */
g_string_append_c(out, '\n');
break;
case '|':
/* Indent here */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_INDENT);
break;
case 'F':
/* blink */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_BLINK);
break;
case 'n':
case 'N':
/* default color */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_DEFAULTS);
break;
case '>':
/* clear to end of line */
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_CLRTOEOL);
break;
case '#':
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_STYLE_MONOSPACE);
break;
case '[':
/* code */
format_expand_code(format, out, flags);
break;
case 'x':
case 'X':
if ((*format)[1] < '0' || (*format)[1] > '7')
break;
tmp = 16 + ((*format)[1]-'0'-1)*36;
if (tmp > 231) {
if (!isalpha((*format)[2]))
break;
tmp += (*format)[2] >= 'a' ? (*format)[2] - 'a' : (*format)[2] - 'A';
if (tmp > 255)
break;
}
else if (tmp > 0) {
if (!isalnum((*format)[2]))
break;
if ((*format)[2] >= 'a')
tmp += 10 + (*format)[2] - 'a';
else if ((*format)[2] >= 'A')
tmp += 10 + (*format)[2] - 'A';
else
tmp += (*format)[2] - '0';
}
else {
if (!isxdigit((*format)[2]))
break;
tmp = g_ascii_xdigit_value((*format)[2]);
}
retval += 2;
format_ext_color(out, fmt == 'x', tmp);
break;
case 'z':
case 'Z':
tmp2 = 0;
for (tmp = 1; tmp < 7; ++tmp) {
if (!isxdigit((*format)[tmp])) {
tmp2 = UINT_MAX;
break;
}
tmp2 <<= 4;
tmp2 |= g_ascii_xdigit_value((*format)[tmp]);
}
if (tmp2 == UINT_MAX)
break;
retval += 6;
format_24bit_color(out, fmt == 'z', tmp2);
break;
default:
/* check if it's a background color */
p = strchr(format_backs, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
g_string_append_c(out, (char) ((int) (p-format_backs)+'0'));
break;
}
/* check if it's a foreground color */
if (fmt == 'p') fmt = 'm';
p = strchr(format_fores, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, (char) ((int) (p-format_fores)+'0'));
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
break;
}
/* check if it's a bold foreground color */
if (fmt == 'P') fmt = 'M';
p = strchr(format_boldfores, fmt);
if (p != NULL) {
g_string_append_c(out, 4);
g_string_append_c(out, (char) (8+(int) (p-format_boldfores)+'0'));
g_string_append_c(out, FORMAT_COLOR_NOCHANGE);
break;
}
return FALSE;
}
return retval;
}
void format_read_arglist(va_list va, FORMAT_REC *format,
char **arglist, int arglist_size,
char *buffer, int buffer_size)
{
int num, len, bufpos;
g_return_if_fail(format->params < arglist_size);
bufpos = 0;
arglist[format->params] = NULL;
for (num = 0; num < format->params; num++) {
switch (format->paramtypes[num]) {
case FORMAT_STRING:
arglist[num] = (char *) va_arg(va, char *);
if (arglist[num] == NULL)
arglist[num] = "";
break;
case FORMAT_INT: {
int d = (int) va_arg(va, int);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%d", d);
bufpos += len+1;
break;
}
case FORMAT_LONG: {
long l = (long) va_arg(va, long);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%ld", l);
bufpos += len+1;
break;
}
case FORMAT_FLOAT: {
double f = (double) va_arg(va, double);
if (bufpos >= buffer_size) {
arglist[num] = "";
break;
}
arglist[num] = buffer+bufpos;
len = g_snprintf(buffer+bufpos, buffer_size-bufpos,
"%0.2f", f);
bufpos += len+1;
break;
}
}
}
}
void format_create_dest(TEXT_DEST_REC *dest,
void *server, const char *target,
int level, WINDOW_REC *window)
{
format_create_dest_tag(dest, server, NULL, target, level, window);
}
void format_create_dest_tag(TEXT_DEST_REC *dest, void *server,
const char *server_tag, const char *target,
int level, WINDOW_REC *window)
{
memset(dest, 0, sizeof(TEXT_DEST_REC));
dest->server = server;
dest->server_tag = server != NULL ? SERVER(server)->tag : server_tag;
dest->target = target;
dest->level = level;
dest->window = window != NULL ? window :
window_find_closest(server, target, level);
}
static int advance (char const **str, gboolean utf8)
{
if (utf8) {
gunichar c;
c = g_utf8_get_char(*str);
*str = g_utf8_next_char(*str);
return unichar_isprint(c) ? mk_wcwidth(c) : 1;
} else {
*str += 1;
return 1;
}
}
/* Return length of text part in string (ie. without % codes) */
int format_get_length(const char *str)
{
GString *tmp;
int len;
gboolean utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
tmp = g_string_new(NULL);
len = 0;
while (*str != '\0') {
if (*str == '%' && str[1] != '\0') {
str++;
if (*str != '%') {
adv = format_expand_styles(tmp, &str, NULL);
str += adv;
if (adv)
continue;
}
/* %% or unknown %code, written as-is */
if (*str != '%')
len++;
}
len += advance(&str, utf8);
}
g_string_free(tmp, TRUE);
return len;
}
/* Return how many characters in `str' must be skipped before `len'
characters of text is skipped. Like strip_real_length(), except this
handles %codes. */
int format_real_length(const char *str, int len)
{
GString *tmp;
const char *start;
const char *oldstr;
gboolean utf8;
int adv = 0;
g_return_val_if_fail(str != NULL, 0);
g_return_val_if_fail(len >= 0, 0);
utf8 = is_utf8() && g_utf8_validate(str, -1, NULL);
start = str;
tmp = g_string_new(NULL);
while (*str != '\0' && len > 0) {
if (*str == '%' && str[1] != '\0') {
str++;
if (*str != '%') {
adv = format_expand_styles(tmp, &str, NULL);
str += adv;
if (adv)
continue;
}
/* %% or unknown %code, written as-is */
if (*str != '%') {
if (--len == 0)
break;
}
}
oldstr = str;
len -= advance(&str, utf8);
if (len < 0)
str = oldstr;
}
g_string_free(tmp, TRUE);
return (int) (str-start);
}
char *format_string_expand(const char *text, int *flags)
{
GString *out;
char code, *ret;
int adv;
g_return_val_if_fail(text != NULL, NULL);
out = g_string_new(NULL);
if (flags != NULL) *flags = 0;
code = 0;
while (*text != '\0') {
if (code == '%') {
/* color code */
adv = format_expand_styles(out, &text, flags);
if (!adv) {
g_string_append_c(out, '%');
g_string_append_c(out, '%');
g_string_append_c(out, *text);
} else {
text += adv - 1;
}
code = 0;
} else {
if (*text == '%')
code = *text;
else
g_string_append_c(out, *text);
}
text++;
}
ret = out->str;
g_string_free(out, FALSE);
return ret;
}
static char *format_get_text_args(TEXT_DEST_REC *dest,
const char *text, char **arglist)
{
GString *out;
char code, *ret;
int need_free;
int adv;
out = g_string_new(NULL);
code = 0;
while (*text != '\0') {
if (code == '%') {
/* color code */
adv = format_expand_styles(out, &text, &dest->flags);
if (!adv) {
g_string_append_c(out, '%');
g_string_append_c(out, '%');
g_string_append_c(out, *text);
} else {
text += adv - 1;
}
code = 0;
} else if (code == '$') {
/* argument */
char *ret;
ret = parse_special((char **) &text, dest->server,
dest->target == NULL ? NULL :
window_item_find(dest->server, dest->target),
arglist, &need_free, NULL, 0);
if (ret != NULL) {
/* string shouldn't end with \003 or it could
mess up the next one or two characters */
int diff;
int len = strlen(ret);
while (len > 0 && ret[len-1] == 3) len--;
diff = strlen(ret)-len;
g_string_append(out, ret);
if (diff > 0)
g_string_truncate(out, out->len-diff);
if (need_free) g_free(ret);
}
code = 0;
} else {
if (*text == '%' || *text == '$')
code = *text;
else
g_string_append_c(out, *text);
}
text++;
}
ret = out->str;
g_string_free(out, FALSE);
return ret;
}
char *format_get_text_theme(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum, ...)
{
va_list va;
char *str;
if (theme == NULL)
theme = window_get_theme(dest->window);
va_start(va, formatnum);
str = format_get_text_theme_args(theme, module, dest, formatnum, va);
va_end(va);
return str;
}
char *format_get_text_theme_args(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum,
va_list va)
{
char *arglist[MAX_FORMAT_PARAMS];
char buffer[DEFAULT_FORMAT_ARGLIST_SIZE];
FORMAT_REC *formats;
formats = g_hash_table_lookup(default_formats, module);
format_read_arglist(va, &formats[formatnum],
arglist, sizeof(arglist)/sizeof(char *),
buffer, sizeof(buffer));
return format_get_text_theme_charargs(theme, module, dest,
formatnum, arglist);
}
char *format_get_text_theme_charargs(THEME_REC *theme, const char *module,
TEXT_DEST_REC *dest, int formatnum,
char **args)
{
MODULE_THEME_REC *module_theme;
char *text;
module_theme = g_hash_table_lookup(theme->modules, module);
if (module_theme == NULL)
return NULL;
text = module_theme->expanded_formats[formatnum];
return format_get_text_args(dest, text, args);
}
char *format_get_text(const char *module, WINDOW_REC *window,
void *server, const char *target,
int formatnum, ...)
{
TEXT_DEST_REC dest;
THEME_REC *theme;
va_list va;
char *str;
format_create_dest(&dest, server, target, 0, window);
theme = window_get_theme(dest.window);
va_start(va, formatnum);
str = format_get_text_theme_args(theme, module, &dest, formatnum, va);
va_end(va);
return str;
}
/* add `linestart' to start of each line in `text'. `text' may contain
multiple lines separated with \n. */
char *format_add_linestart(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(linestart, text, NULL);
str = g_string_new(linestart);
while (*text != '\0') {
g_string_append_c(str, *text);
if (*text == '\n')
g_string_append(str, linestart);
text++;
}
ret = str->str;
g_string_free(str, FALSE);
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)
#define NOT_LINE_START_LEVEL \
(MSGLEVEL_NEVER | MSGLEVEL_LASTLOG | MSGLEVEL_CLIENTCRAP | \
MSGLEVEL_MSGS | MSGLEVEL_PUBLIC | MSGLEVEL_DCC | MSGLEVEL_DCCMSGS | \
MSGLEVEL_ACTIONS | MSGLEVEL_NOTICES | MSGLEVEL_SNOTES | MSGLEVEL_CTCPS)
/* return the "-!- " text at the start of the line */
char *format_get_level_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
{
int format;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_LINE_START)
return NULL;
if (dest->flags & PRINT_FLAG_SET_LINE_START)
format = TXT_LINE_START;
else if (dest->flags & PRINT_FLAG_SET_LINE_START_IRSSI)
format = TXT_LINE_START_IRSSI;
else {
/* use defaults */
if (dest->level & LINE_START_IRSSI_LEVEL)
format = TXT_LINE_START_IRSSI;
else if ((dest->level & NOT_LINE_START_LEVEL) == 0)
format = TXT_LINE_START;
else
return NULL;
}
return format_get_text_theme(theme, MODULE_NAME, dest, format);
}
static char *get_timestamp(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
{
char *format, str[256];
struct tm *tm;
int diff;
if ((timestamp_level & dest->level) == 0)
return NULL;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_TIMESTAMP)
return NULL;
if ((dest->flags & PRINT_FLAG_SET_TIMESTAMP) == 0 &&
(dest->level & (MSGLEVEL_NEVER|MSGLEVEL_LASTLOG)) != 0)
return NULL;
if (timestamp_timeout > 0) {
diff = t - dest->window->last_timestamp;
dest->window->last_timestamp = t;
if (diff < timestamp_timeout)
return NULL;
}
tm = localtime(&t);
format = format_get_text_theme(theme, MODULE_NAME, dest,
TXT_TIMESTAMP);
if (strftime(str, sizeof(str), format, tm) <= 0)
str[0] = '\0';
g_free(format);
return g_strdup(str);
}
static char *get_server_tag(THEME_REC *theme, TEXT_DEST_REC *dest)
{
int count = 0;
if (dest->server_tag == NULL || hide_server_tags)
return NULL;
/* check for flags if we want to override defaults */
if (dest->flags & PRINT_FLAG_UNSET_SERVERTAG)
return NULL;
if ((dest->flags & PRINT_FLAG_SET_SERVERTAG) == 0) {
if (dest->window->active != NULL &&
dest->window->active->server == dest->server)
return NULL;
if (servers != NULL) {
count++;
if (servers->next != NULL)
count++;
}
if (count < 2 && lookup_servers != NULL) {
count++;
if (lookup_servers->next != NULL)
count++;
}
if (count < 2)
return NULL;
}
return format_get_text_theme(theme, MODULE_NAME, dest,
TXT_SERVERTAG, dest->server_tag);
}
char *format_get_line_start(THEME_REC *theme, TEXT_DEST_REC *dest, time_t t)
{
char *timestamp, *servertag;
char *linestart;
timestamp = get_timestamp(theme, dest, t);
servertag = get_server_tag(theme, dest);
if (timestamp == NULL && servertag == NULL)
return NULL;
linestart = g_strconcat(timestamp != NULL ? timestamp : "",
servertag, NULL);
g_free_not_null(timestamp);
g_free_not_null(servertag);
return linestart;
}
void format_newline(WINDOW_REC *window)
{
g_return_if_fail(window != NULL);
signal_emit_id(signal_gui_print_text, 6, window,
GINT_TO_POINTER(-1), GINT_TO_POINTER(-1),
GINT_TO_POINTER(GUI_PRINT_FLAG_NEWLINE),
"", NULL);
}
#ifndef TERM_TRUECOLOR
inline static int color_24bit_256_int(unsigned int color)
{
unsigned char rgb[] = { color >> 16, color >> 8, color };
return color_24bit_256(rgb);
}
#endif /* !TERM_TRUECOLOR */
/* parse ANSI color string */
static const char *get_ansi_color(THEME_REC *theme, const char *str,
int *fg_ret, int *bg_ret, int *flags_ret)
{
static char ansitab[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
const char *start;
int fg, bg, flags, num, i;
unsigned int num2;
if (*str != '[')
return str;
start = str++;
fg = fg_ret == NULL || *fg_ret < 0 ? theme->default_color : *fg_ret;
bg = bg_ret == NULL || *bg_ret < 0 ? -1 : *bg_ret;
flags = flags_ret == NULL ? 0 : *flags_ret;
num = 0;
for (;; str++) {
if (*str == '\0') return start;
if (i_isdigit(*str)) {
num = num*10 + (*str-'0');
continue;
}
if (*str != ';' && *str != 'm')
return start;
switch (num) {
case 0:
/* reset colors back to default */
fg = theme->default_color;
bg = -1;
flags &= ~(GUI_PRINT_FLAG_COLOR_24_FG | GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT);
break;
case 1:
/* hilight */
flags |= GUI_PRINT_FLAG_BOLD;
break;
case 22:
/* normal */
flags &= ~GUI_PRINT_FLAG_BOLD;
break;
case 4:
/* underline */
flags |= GUI_PRINT_FLAG_UNDERLINE;
break;
case 24:
/* not underline */
flags &= ~GUI_PRINT_FLAG_UNDERLINE;
break;
case 5:
/* blink */
flags |= GUI_PRINT_FLAG_BLINK;
break;
case 25:
/* steady */
flags &= ~GUI_PRINT_FLAG_BLINK;
break;
case 7:
/* reverse */
flags |= GUI_PRINT_FLAG_REVERSE;
break;
case 27:
/* positive */
flags &= ~GUI_PRINT_FLAG_REVERSE;
break;
case 39:
/* reset fg */
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = theme->default_color;
break;
case 49:
/* reset bg */
bg = -1;
flags &= ~(GUI_PRINT_FLAG_COLOR_24_BG | GUI_PRINT_FLAG_INDENT);
break;
case 38:
case 48:
/* ANSI indexed color or RGB color */
if (*str != ';') break;
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
switch (num2) {
case 2:
/* RGB */
num2 = 0;
for (i = 0; i < 3; ++i) {
num2 <<= 8;
if (*str != ';' && *str != ':') {
i = -1;
break;
}
str++;
for (; i_isdigit(*str); str++)
num2 = (num2&~0xff) |
(((num2&0xff) * 10 + (*str-'0'))&0xff);
}
if (i == -1) break;
#ifdef TERM_TRUECOLOR
if (num == 38) {
flags |= GUI_PRINT_FLAG_COLOR_24_FG;
fg = num2;
} else if (num == 48) {
flags |= GUI_PRINT_FLAG_COLOR_24_BG;
bg = num2;
}
#else /* !TERM_TRUECOLOR */
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = color_24bit_256_int(num2);
} else if (num == 48) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = color_24bit_256_int(num2);
}
#endif
break;
case 5:
/* indexed */
if (*str != ';') break;
str++;
for (num2 = 0; i_isdigit(*str); str++)
num2 = num2*10 + (*str-'0');
if (num == 38) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = num2;
} else if (num == 48) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = num2;
}
break;
}
break;
default:
if (num >= 30 && num <= 37) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = ansitab[num-30];
} else if (num >= 40 && num <= 47) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = ansitab[num-40];
} else if (num >= 90 && num <= 97) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fg = 8 + ansitab[num-90];
} else if (num >= 100 && num <= 107) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bg = 8 + ansitab[num-100];
}
break;
}
num = 0;
if (*str == 'm') {
if (fg_ret != NULL) *fg_ret = fg;
if (bg_ret != NULL) *bg_ret = bg;
if (flags_ret != NULL) *flags_ret = flags;
str++;
break;
}
}
return str;
}
/* parse MIRC color string */
static void get_mirc_color(const char **str, int *fg_ret, int *bg_ret)
{
int fg, bg;
fg = fg_ret == NULL ? -1 : *fg_ret;
bg = bg_ret == NULL ? -1 : *bg_ret;
if (!i_isdigit(**str) && **str != ',') {
fg = -1;
bg = -1;
} else {
/* foreground color */
if (**str != ',') {
fg = **str-'0';
(*str)++;
if (i_isdigit(**str)) {
fg = fg*10 + (**str-'0');
(*str)++;
}
}
if (**str == ',') {
/* background color */
if (!i_isdigit((*str)[1]))
bg = -1;
else {
(*str)++;
bg = **str-'0';
(*str)++;
if (i_isdigit(**str)) {
bg = bg*10 + (**str-'0');
(*str)++;
}
}
}
}
if (fg_ret) *fg_ret = fg;
if (bg_ret) *bg_ret = bg;
}
#define IS_COLOR_CODE(c) \
((c) == 2 || (c) == 3 || (c) == 4 || (c) == 6 || (c) == 7 || \
(c) == 15 || (c) == 22 || (c) == 27 || (c) == 31)
/* Return how many characters in `str' must be skipped before `len'
characters of text is skipped. */
int strip_real_length(const char *str, int len,
int *last_color_pos, int *last_color_len)
{
const char *start = str;
if (last_color_pos != NULL)
*last_color_pos = -1;
if (last_color_len != NULL)
*last_color_len = -1;
while (*str != '\0') {
if (*str == 3) {
const char *mircstart = str;
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
str++;
get_mirc_color(&str, NULL, NULL);
if (last_color_len != NULL)
*last_color_len = (int) (str-mircstart);
} else if (*str == 4 && str[1] != '\0') {
#ifdef TERM_TRUECOLOR
if (str[1] == FORMAT_COLOR_24 && str[2] != '\0') {
if (str[3] == '\0') str++;
else if (str[4] == '\0') str += 2;
else if (str[5] == '\0') str += 3;
else {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 6;
str+=4;
}
} else
#endif
if (str[1] < FORMAT_STYLE_SPECIAL && str[2] != '\0') {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 3;
str++;
} else if (str[1] == FORMAT_STYLE_DEFAULTS) {
if (last_color_pos != NULL)
*last_color_pos = (int) (str-start);
if (last_color_len != NULL)
*last_color_len = 2;
}
str += 2;
} else {
if (!IS_COLOR_CODE(*str)) {
if (len-- == 0)
break;
}
str++;
}
}
return (int) (str-start);
}
char *strip_codes(const char *input)
{
const char *p;
char *str, *out;
out = str = g_strdup(input);
for (p = input; *p != '\0'; p++) {
if (*p == 3) {
p++;
/* mirc color */
get_mirc_color(&p, NULL, NULL);
p--;
continue;
}
if (*p == 4 && p[1] != '\0') {
if (p[1] >= FORMAT_STYLE_SPECIAL) {
p++;
continue;
}
/* irssi color */
if (p[2] != '\0') {
#ifdef TERM_TRUECOLOR
if (p[1] == FORMAT_COLOR_24) {
if (p[3] == '\0') p += 2;
else if (p[4] == '\0') p += 3;
else if (p[5] == '\0') p += 4;
else p += 5;
} else
#endif /* TERM_TRUECOLOR */
p += 2;
continue;
}
}
if (*p == 27 && p[1] != '\0') {
p++;
p = get_ansi_color(current_theme, p, NULL, NULL, NULL);
p--;
} else if (!IS_COLOR_CODE(*p))
*out++ = *p;
}
*out = '\0';
return str;
}
/* 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 = window_get_theme(dest->window);
dup = str = g_strdup(text);
flags = 0; fgcolor = theme->default_color; bgcolor = -1;
while (*str != '\0') {
type = '\0';
for (ptr = str; *ptr != '\0'; ptr++) {
if (IS_COLOR_CODE(*ptr) || *ptr == '\n') {
type = *ptr;
*ptr++ = '\0';
break;
}
}
if (type == 7) {
/* bell */
if (settings_get_bool("bell_beeps"))
signal_emit("beep", 0);
} else if (type == 4 && *ptr == FORMAT_STYLE_CLRTOEOL) {
/* clear to end of line */
flags |= GUI_PRINT_FLAG_CLRTOEOL;
}
if (*str != '\0' || (flags & GUI_PRINT_FLAG_CLRTOEOL)) {
/* send the text to gui handler */
signal_emit_id(signal_gui_print_text, 6, dest->window,
GINT_TO_POINTER(fgcolor),
GINT_TO_POINTER(bgcolor),
GINT_TO_POINTER(flags), str,
dest);
flags &= ~(GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_CLRTOEOL);
}
if (type == '\n') {
format_newline(dest->window);
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
}
if (*ptr == '\0')
break;
switch (type)
{
case 2:
/* bold */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_BOLD;
break;
case 3:
/* MIRC color */
get_mirc_color((const char **) &ptr,
hide_colors ? NULL : &fgcolor,
hide_colors ? NULL : &bgcolor);
if (!hide_colors)
flags |= GUI_PRINT_FLAG_MIRC_COLOR;
break;
case 4:
/* user specific colors */
flags &= ~GUI_PRINT_FLAG_MIRC_COLOR;
switch (*ptr) {
case FORMAT_STYLE_BLINK:
flags ^= GUI_PRINT_FLAG_BLINK;
break;
case FORMAT_STYLE_UNDERLINE:
flags ^= GUI_PRINT_FLAG_UNDERLINE;
break;
case FORMAT_STYLE_BOLD:
flags ^= GUI_PRINT_FLAG_BOLD;
break;
case FORMAT_STYLE_REVERSE:
flags ^= GUI_PRINT_FLAG_REVERSE;
break;
case FORMAT_STYLE_MONOSPACE:
flags ^= GUI_PRINT_FLAG_MONOSPACE;
break;
case FORMAT_STYLE_INDENT:
flags |= GUI_PRINT_FLAG_INDENT;
break;
case FORMAT_STYLE_DEFAULTS:
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
break;
case FORMAT_STYLE_CLRTOEOL:
break;
case FORMAT_COLOR_EXT1:
fgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT1_BG:
bgcolor = 0x10 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
case FORMAT_COLOR_EXT2:
fgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT2_BG:
bgcolor = 0x60 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
case FORMAT_COLOR_EXT3:
fgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
break;
case FORMAT_COLOR_EXT3_BG:
bgcolor = 0xb0 + *++ptr - FORMAT_COLOR_NOCHANGE;
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
break;
#ifdef TERM_TRUECOLOR
case FORMAT_COLOR_24:
unformat_24bit_color(&ptr, 1, &fgcolor, &bgcolor, &flags);
break;
#endif
default:
if (*ptr != FORMAT_COLOR_NOCHANGE) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_FG;
fgcolor = *ptr==(char)0xff ? -1 : (unsigned char) *ptr-'0';
}
if (ptr[1] == '\0')
break;
ptr++;
if (*ptr != FORMAT_COLOR_NOCHANGE) {
flags &= ~GUI_PRINT_FLAG_COLOR_24_BG;
bgcolor = *ptr==(char)0xff ? -1 : *ptr-'0';
}
}
ptr++;
break;
case 6:
/* blink */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_BLINK;
break;
case 15:
/* remove all styling */
fgcolor = theme->default_color;
bgcolor = -1;
flags &= GUI_PRINT_FLAG_INDENT|GUI_PRINT_FLAG_MONOSPACE;
break;
case 22:
/* reverse */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_REVERSE;
break;
case 31:
/* underline */
if (!hide_text_style)
flags ^= GUI_PRINT_FLAG_UNDERLINE;
break;
case 27:
/* ansi color code */
ptr = (char *)
get_ansi_color(theme, ptr,
hide_colors ? NULL : &fgcolor,
hide_colors ? NULL : &bgcolor,
hide_colors ? NULL : &flags);
break;
}
str = ptr;
}
g_free(dup);
}
static void read_settings(void)
{
timestamp_level = settings_get_bool("timestamps") ? MSGLEVEL_ALL : 0;
if (timestamp_level > 0)
timestamp_level = settings_get_level("timestamp_level");
timestamp_timeout = settings_get_time("timestamp_timeout")/1000;
hide_server_tags = settings_get_bool("hide_server_tags");
hide_text_style = settings_get_bool("hide_text_style");
hide_colors = hide_text_style || settings_get_bool("hide_colors");
}
void formats_init(void)
{
signal_gui_print_text = signal_get_uniq_id("gui print text");
read_settings();
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
void formats_deinit(void)
{
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
}