forked from PsychoticNinja/irssi
default_bold_color to default_real_color. Activity list printed first item with white always. Color redrawed itself every second. git-svn-id: http://svn.irssi.org/repos/irssi/trunk@1381 dbcabf3a-b0e7-0310-adc4-f8d773084564
628 lines
16 KiB
C
628 lines
16 KiB
C
/*
|
|
gui-printtext.c : irssi
|
|
|
|
Copyright (C) 1999 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "module.h"
|
|
#include "signals.h"
|
|
#include "commands.h"
|
|
#include "settings.h"
|
|
|
|
#include "fe-windows.h"
|
|
#include "formats.h"
|
|
#include "printtext.h"
|
|
#include "themes.h"
|
|
|
|
#include "screen.h"
|
|
#include "gui-windows.h"
|
|
|
|
#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
|
|
|
|
int mirc_colors[] = { 15, 0, 1, 2, 12, 6, 5, 4, 14, 10, 3, 11, 9, 13, 8, 7 };
|
|
static int scrollback_lines, scrollback_hours;
|
|
|
|
static int scrollback_save_formats;
|
|
static GString *format;
|
|
|
|
static int next_xpos, next_ypos;
|
|
|
|
#define mark_temp_eol(text) \
|
|
memcpy((text)->buffer + (text)->pos, "\0\200", 2);
|
|
|
|
static LINE_REC *create_line(GUI_WINDOW_REC *gui, int level)
|
|
{
|
|
LINE_REC *rec;
|
|
|
|
g_return_val_if_fail(gui != NULL, NULL);
|
|
g_return_val_if_fail(gui->cur_text != NULL, NULL);
|
|
|
|
rec = g_mem_chunk_alloc(gui->line_chunk);
|
|
rec->text = gui->cur_text->buffer+gui->cur_text->pos;
|
|
rec->level = GPOINTER_TO_INT(level);
|
|
rec->time = time(NULL);
|
|
|
|
mark_temp_eol(gui->cur_text);
|
|
gui->cur_text->lines++;
|
|
|
|
gui->last_color = -1;
|
|
gui->last_flags = 0;
|
|
|
|
if (gui->temp_line != NULL) {
|
|
int pos = g_list_index(gui->lines, gui->temp_line);
|
|
gui->lines = g_list_insert(gui->lines, rec, pos+1);
|
|
gui->temp_line = rec;
|
|
} else {
|
|
gui->cur_line = rec;
|
|
gui->lines = g_list_append(gui->lines, rec);
|
|
if (gui->startline == NULL) {
|
|
/* first line */
|
|
gui->startline = gui->lines;
|
|
gui->bottom_startline = gui->lines;
|
|
}
|
|
}
|
|
return rec;
|
|
}
|
|
|
|
static TEXT_CHUNK_REC *create_text_chunk(GUI_WINDOW_REC *gui)
|
|
{
|
|
TEXT_CHUNK_REC *rec;
|
|
char *buffer, *ptr;
|
|
|
|
g_return_val_if_fail(gui != NULL, NULL);
|
|
|
|
rec = g_new(TEXT_CHUNK_REC, 1);
|
|
rec->pos = 0;
|
|
rec->lines = 0;
|
|
|
|
if (gui->cur_line != NULL && gui->cur_line->text != NULL) {
|
|
/* create a link to new block from the old block */
|
|
buffer = gui->cur_text->buffer + gui->cur_text->pos;
|
|
*buffer++ = 0; *buffer++ = (char) LINE_CMD_CONTINUE;
|
|
|
|
ptr = rec->buffer;
|
|
memcpy(buffer, &ptr, sizeof(char *));
|
|
} else {
|
|
/* just to be safe */
|
|
mark_temp_eol(rec);
|
|
}
|
|
|
|
gui->cur_text = rec;
|
|
gui->text_chunks = g_slist_append(gui->text_chunks, rec);
|
|
return rec;
|
|
}
|
|
|
|
static void text_chunk_free(GUI_WINDOW_REC *gui, TEXT_CHUNK_REC *chunk)
|
|
{
|
|
g_return_if_fail(gui != NULL);
|
|
g_return_if_fail(chunk != NULL);
|
|
|
|
gui->text_chunks = g_slist_remove(gui->text_chunks, chunk);
|
|
g_free(chunk);
|
|
}
|
|
|
|
static TEXT_CHUNK_REC *text_chunk_find(GUI_WINDOW_REC *gui, const char *data)
|
|
{
|
|
GSList *tmp;
|
|
|
|
for (tmp = gui->text_chunks; tmp != NULL; tmp = tmp->next) {
|
|
TEXT_CHUNK_REC *rec = tmp->data;
|
|
|
|
if (data >= rec->buffer &&
|
|
data < rec->buffer+sizeof(rec->buffer))
|
|
return rec;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void gui_window_line_text_free(GUI_WINDOW_REC *gui, LINE_REC *line)
|
|
{
|
|
TEXT_CHUNK_REC *chunk;
|
|
const char *text;
|
|
|
|
text = line->text;
|
|
for (;;) {
|
|
if (*text == '\0') {
|
|
text++;
|
|
if ((unsigned char) *text == LINE_CMD_EOL)
|
|
break;
|
|
|
|
if ((unsigned char) *text == LINE_CMD_CONTINUE) {
|
|
char *tmp;
|
|
|
|
memcpy(&tmp, text+1, sizeof(char *));
|
|
|
|
/* free the previous block */
|
|
chunk = text_chunk_find(gui, text);
|
|
if (--chunk->lines == 0)
|
|
text_chunk_free(gui, chunk);
|
|
|
|
text = tmp;
|
|
continue;
|
|
}
|
|
if ((unsigned char) *text & 0x80)
|
|
text++;
|
|
continue;
|
|
}
|
|
|
|
text++;
|
|
}
|
|
|
|
/* free the last block */
|
|
chunk = text_chunk_find(gui, text);
|
|
if (--chunk->lines == 0) {
|
|
if (gui->cur_text == chunk)
|
|
chunk->pos = 0;
|
|
else
|
|
text_chunk_free(gui, chunk);
|
|
}
|
|
}
|
|
|
|
void gui_window_line_remove(WINDOW_REC *window, LINE_REC *line, int redraw)
|
|
{
|
|
GUI_WINDOW_REC *gui;
|
|
GList *last;
|
|
int screenchange;
|
|
|
|
g_return_if_fail(window != NULL);
|
|
g_return_if_fail(line != NULL);
|
|
|
|
gui = WINDOW_GUI(window);
|
|
|
|
if (gui->lines->next == NULL) {
|
|
/* last line in window */
|
|
gui_window_clear(window);
|
|
return;
|
|
}
|
|
|
|
screenchange = g_list_find(gui->startline, line) != NULL;
|
|
if (screenchange) gui->ypos -= gui_window_get_linecount(gui, line);
|
|
|
|
gui_window_cache_remove(gui, line);
|
|
gui_window_line_text_free(gui, line);
|
|
if (gui->lastlog_last_check != NULL &&
|
|
gui->lastlog_last_check->data == line)
|
|
gui->lastlog_last_check = NULL;
|
|
if (gui->lastlog_last_away != NULL &&
|
|
gui->lastlog_last_away->data == line)
|
|
gui->lastlog_last_away = NULL;
|
|
|
|
last = g_list_last(gui->bottom_startline);
|
|
if (last->data == line) {
|
|
/* removing last line */
|
|
gui->last_subline =
|
|
gui_window_get_linecount(gui, last->prev->data)-1;
|
|
}
|
|
|
|
if (gui->bottom_startline->data == line) {
|
|
/* bottom line removed */
|
|
if (gui->bottom_startline->next != NULL) {
|
|
gui->bottom_startline = gui->bottom_startline->next;
|
|
gui->bottom_subline = 0;
|
|
} else {
|
|
gui->bottom_startline = gui->bottom_startline->prev;
|
|
gui->bottom_subline = gui->last_subline+1;
|
|
}
|
|
}
|
|
|
|
if (gui->startline->data == line) {
|
|
/* first line in screen removed */
|
|
if (gui->startline->next != NULL) {
|
|
gui->startline = gui->startline->next;
|
|
gui->subline = 0;
|
|
} else {
|
|
gui->startline = gui->startline->prev;
|
|
gui->subline = gui->last_subline+1;
|
|
gui->ypos = -1;
|
|
gui->empty_linecount = gui->parent->lines;
|
|
gui->bottom = TRUE;
|
|
}
|
|
}
|
|
|
|
window->lines--;
|
|
g_mem_chunk_free(gui->line_chunk, line);
|
|
gui->lines = g_list_remove(gui->lines, line);
|
|
|
|
if (window->lines == 0)
|
|
gui_window_clear(window);
|
|
|
|
if (redraw && screenchange && is_window_visible(window))
|
|
gui_window_redraw(window);
|
|
}
|
|
|
|
void gui_printtext(int xpos, int ypos, const char *str)
|
|
{
|
|
next_xpos = xpos;
|
|
next_ypos = ypos;
|
|
|
|
printtext_gui(str);
|
|
|
|
next_xpos = next_ypos = -1;
|
|
}
|
|
|
|
static void remove_old_lines(WINDOW_REC *window)
|
|
{
|
|
GUI_WINDOW_REC *gui;
|
|
LINE_REC *line;
|
|
time_t old_time;
|
|
|
|
gui = WINDOW_GUI(window);
|
|
|
|
old_time = time(NULL)-(scrollback_hours*3600)+1;
|
|
if (scrollback_lines > 0) {
|
|
/* remove lines by line count */
|
|
while (window->lines > scrollback_lines) {
|
|
line = gui->lines->data;
|
|
if (line->time >= old_time) {
|
|
/* too new line, don't remove yet */
|
|
break;
|
|
}
|
|
gui_window_line_remove(window, line, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void get_colors(int flags, int *fg, int *bg)
|
|
{
|
|
if (flags & PRINTFLAG_MIRC_COLOR) {
|
|
/* mirc colors - real range is 0..15, but after 16
|
|
colors wrap to 0, 1, ... */
|
|
*bg = *bg < 0 ? 0 : mirc_colors[*bg % 16];
|
|
if (*fg > 0) *fg = mirc_colors[*fg % 16];
|
|
} else {
|
|
/* default colors */
|
|
*bg = *bg < 0 || *bg > 15 ? 0 : *bg;
|
|
if (*fg > 8) *fg &= ~8;
|
|
}
|
|
|
|
if (*fg < 0 || *fg > 15) {
|
|
*fg = *bg == 0 ? current_theme->default_color :
|
|
current_theme->default_real_color;
|
|
}
|
|
|
|
if (flags & PRINTFLAG_REVERSE) {
|
|
int tmp;
|
|
|
|
tmp = *fg; *fg = *bg; *bg = tmp;
|
|
}
|
|
|
|
if (*fg == 8) *fg |= ATTR_COLOR8;
|
|
if (flags & PRINTFLAG_BOLD) {
|
|
if (*fg == 0) *fg = current_theme->default_real_color;
|
|
*fg |= 8;
|
|
}
|
|
if (flags & PRINTFLAG_UNDERLINE) *fg |= ATTR_UNDERLINE;
|
|
if (flags & PRINTFLAG_BLINK) *bg |= 0x80;
|
|
}
|
|
|
|
static void linebuf_add(GUI_WINDOW_REC *gui, const char *str, int len)
|
|
{
|
|
int left;
|
|
|
|
if (len == 0) return;
|
|
|
|
while (gui->cur_text->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
|
|
left = TEXT_CHUNK_USABLE_SIZE - gui->cur_text->pos;
|
|
if (str[left-1] == 0) left--; /* don't split the commands */
|
|
|
|
memcpy(gui->cur_text->buffer + gui->cur_text->pos, str, left);
|
|
gui->cur_text->pos += left;
|
|
|
|
create_text_chunk(gui);
|
|
gui->cur_text->lines++;
|
|
len -= left; str += left;
|
|
}
|
|
|
|
memcpy(gui->cur_text->buffer + gui->cur_text->pos, str, len);
|
|
gui->cur_text->pos += len;
|
|
}
|
|
|
|
void gui_window_line_append(GUI_WINDOW_REC *gui, const char *str, int len)
|
|
{
|
|
linebuf_add(gui, str, len);
|
|
mark_temp_eol(gui->cur_text);
|
|
}
|
|
|
|
static void line_add_colors(GUI_WINDOW_REC *gui, int fg, int bg, int flags)
|
|
{
|
|
unsigned char buffer[12];
|
|
int color, pos;
|
|
|
|
/* color should never have last bit on or it would be treated as a
|
|
command! */
|
|
color = (fg & 0x0f) | ((bg & 0x07) << 4);
|
|
pos = 0;
|
|
|
|
if (((fg & ATTR_COLOR8) == 0 && (fg|(bg << 4)) != gui->last_color) ||
|
|
((fg & ATTR_COLOR8) && (fg & 0xf0) != (gui->last_color & 0xf0))) {
|
|
buffer[pos++] = 0;
|
|
buffer[pos++] = color == 0 ? LINE_CMD_COLOR0 : color;
|
|
}
|
|
|
|
if ((flags & PRINTFLAG_UNDERLINE) != (gui->last_flags & PRINTFLAG_UNDERLINE)) {
|
|
buffer[pos++] = 0;
|
|
buffer[pos++] = LINE_CMD_UNDERLINE;
|
|
}
|
|
if (fg & ATTR_COLOR8) {
|
|
buffer[pos++] = 0;
|
|
buffer[pos++] = LINE_CMD_COLOR8;
|
|
}
|
|
if (bg & 0x08) {
|
|
buffer[pos++] = 0;
|
|
buffer[pos++] = LINE_CMD_BLINK;
|
|
}
|
|
if (flags & PRINTFLAG_INDENT) {
|
|
buffer[pos++] = 0;
|
|
buffer[pos++] = LINE_CMD_INDENT;
|
|
}
|
|
|
|
linebuf_add(gui, (char *) buffer, pos);
|
|
|
|
gui->last_flags = flags;
|
|
gui->last_color = fg | (bg << 4);
|
|
}
|
|
|
|
static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
|
|
void *bgcolor, void *pflags,
|
|
char *str, void *level)
|
|
{
|
|
GUI_WINDOW_REC *gui;
|
|
LINE_REC *line;
|
|
int fg, bg, flags, new_lines, n, visible, ypos, subline;
|
|
|
|
flags = GPOINTER_TO_INT(pflags);
|
|
fg = GPOINTER_TO_INT(fgcolor);
|
|
bg = GPOINTER_TO_INT(bgcolor);
|
|
get_colors(flags, &fg, &bg);
|
|
|
|
if (window == NULL && next_xpos != -1) {
|
|
wmove(stdscr, next_ypos, next_xpos);
|
|
set_color(stdscr, fg | (bg << 4));
|
|
addstr(str);
|
|
next_xpos += strlen(str);
|
|
return;
|
|
}
|
|
|
|
g_return_if_fail(window != NULL);
|
|
|
|
gui = WINDOW_GUI(window);
|
|
visible = is_window_visible(window) && gui->bottom;
|
|
|
|
if (gui->cur_text == NULL)
|
|
create_text_chunk(gui);
|
|
|
|
/* newline can be only at the start of the line.. */
|
|
if (flags & PRINTFLAG_NEWLINE) {
|
|
remove_old_lines(window);
|
|
if (!gui->eol_marked) {
|
|
if (format->len > 0 || gui->temp_line != NULL) {
|
|
/* mark format continuing to next line */
|
|
char tmp[2] = { 0, (char)LINE_CMD_FORMAT_CONT };
|
|
linebuf_add(gui, tmp, 2);
|
|
}
|
|
linebuf_add(gui, "\0\200", 2); /* mark EOL */
|
|
}
|
|
gui->eol_marked = FALSE;
|
|
|
|
line = create_line(gui, 0);
|
|
if (gui->temp_line == NULL ||
|
|
g_list_find(gui->startline, gui->temp_line) != NULL)
|
|
gui_window_newline(gui, visible);
|
|
|
|
gui->last_subline = 0;
|
|
} else {
|
|
line = gui->temp_line != NULL ? gui->temp_line :
|
|
gui->cur_line != NULL ? gui->cur_line :
|
|
create_line(gui, 0);
|
|
if (line->level == 0) line->level = GPOINTER_TO_INT(level);
|
|
}
|
|
|
|
line_add_colors(gui, fg, bg, flags);
|
|
linebuf_add(gui, str, strlen(str));
|
|
mark_temp_eol(gui->cur_text);
|
|
|
|
gui_window_cache_remove(gui, line);
|
|
|
|
if (gui->temp_line != NULL) {
|
|
/* updating existing line - don't even
|
|
try to print it to screen */
|
|
return;
|
|
}
|
|
|
|
new_lines = gui_window_get_linecount(gui, line)-1 - gui->last_subline;
|
|
|
|
for (n = 0; n < new_lines; n++)
|
|
gui_window_newline(gui, visible);
|
|
|
|
if (visible) {
|
|
/* draw the line to screen. */
|
|
ypos = gui->ypos-new_lines;
|
|
if (new_lines > 0) {
|
|
#ifdef USE_CURSES_WINDOWS
|
|
set_color(gui->parent->curses_win, 0);
|
|
wmove(gui->parent->curses_win, ypos, 0);
|
|
wclrtoeol(gui->parent->curses_win);
|
|
#else
|
|
set_color(stdscr, 0);
|
|
move(ypos + gui->parent->first_line, 0);
|
|
wclrtoeol(stdscr);
|
|
#endif
|
|
}
|
|
|
|
if (ypos >= 0)
|
|
subline = gui->last_subline;
|
|
else {
|
|
/* *LONG* line - longer than screen height */
|
|
subline = -ypos+gui->last_subline;
|
|
ypos = 0;
|
|
}
|
|
gui_window_line_draw(gui, line, ypos, subline, -1);
|
|
}
|
|
|
|
gui->last_subline += new_lines;
|
|
}
|
|
|
|
static void window_clear_screen(GUI_WINDOW_REC *gui)
|
|
{
|
|
#ifdef USE_CURSES_WINDOWS
|
|
wclear(gui->parent->curses_win);
|
|
screen_refresh(gui->parent->curses_win);
|
|
#else
|
|
int n;
|
|
|
|
for (n = gui->parent->first_line; n < gui->parent->last_line; n++) {
|
|
move(n, 0);
|
|
clrtoeol();
|
|
}
|
|
screen_refresh(NULL);
|
|
#endif
|
|
}
|
|
|
|
static void window_clear(WINDOW_REC *window)
|
|
{
|
|
GUI_WINDOW_REC *gui = WINDOW_GUI(window);
|
|
|
|
if (is_window_visible(window))
|
|
window_clear_screen(gui);
|
|
|
|
gui->ypos = -1;
|
|
gui->bottom_startline = gui->startline = g_list_last(gui->lines);
|
|
gui->bottom_subline = gui->subline = gui->last_subline+1;
|
|
gui->empty_linecount = gui->parent->lines;
|
|
gui->bottom = TRUE;
|
|
}
|
|
|
|
/* SYNTAX: CLEAR */
|
|
static void cmd_clear(const char *data)
|
|
{
|
|
GHashTable *optlist;
|
|
void *free_arg;
|
|
|
|
g_return_if_fail(data != NULL);
|
|
|
|
if (!cmd_get_params(data, &free_arg, PARAM_FLAG_OPTIONS,
|
|
"clear", &optlist)) return;
|
|
|
|
if (g_hash_table_lookup(optlist, "all") != NULL)
|
|
g_slist_foreach(windows, (GFunc) window_clear, NULL);
|
|
else
|
|
window_clear(active_win);
|
|
|
|
cmd_params_free(free_arg);
|
|
}
|
|
|
|
static void sig_printtext_finished(WINDOW_REC *window)
|
|
{
|
|
GUI_WINDOW_REC *gui;
|
|
|
|
gui = WINDOW_GUI(window);
|
|
if (gui->cur_line == NULL)
|
|
return;
|
|
|
|
if (format->len > 0) {
|
|
/* save format of the line */
|
|
linebuf_add(gui, format->str, format->len);
|
|
|
|
g_string_truncate(format, 0);
|
|
}
|
|
|
|
linebuf_add(gui, "\0\200", 2); /* mark EOL */
|
|
gui->eol_marked = TRUE;
|
|
|
|
if (is_window_visible(window)) {
|
|
#ifdef USE_CURSES_WINDOWS
|
|
screen_refresh(gui->parent->curses_win);
|
|
#else
|
|
screen_refresh(NULL);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static void sig_print_format(THEME_REC *theme, const char *module,
|
|
TEXT_DEST_REC *dest, void *formatnump,
|
|
char **args)
|
|
{
|
|
FORMAT_REC *formats;
|
|
int formatnum, n;
|
|
|
|
if (!scrollback_save_formats)
|
|
return;
|
|
|
|
formatnum = GPOINTER_TO_INT(formatnump);
|
|
formats = g_hash_table_lookup(default_formats, module);
|
|
|
|
/* <module><format_name><arg...> */
|
|
g_string_truncate(format, 0);
|
|
|
|
g_string_append_c(format, '\0');
|
|
g_string_append_c(format, (char)LINE_CMD_FORMAT);
|
|
|
|
g_string_append(format, module);
|
|
|
|
g_string_append_c(format, '\0');
|
|
g_string_append_c(format, (char)LINE_CMD_FORMAT);
|
|
|
|
g_string_append(format, formats[formatnum].tag);
|
|
|
|
for (n = 0; n < formats[formatnum].params; n++) {
|
|
g_string_append_c(format, '\0');
|
|
g_string_append_c(format, (char)LINE_CMD_FORMAT);
|
|
|
|
g_string_append(format, args[n]);
|
|
}
|
|
}
|
|
|
|
static void read_settings(void)
|
|
{
|
|
scrollback_lines = settings_get_int("scrollback_lines");
|
|
scrollback_hours = settings_get_int("scrollback_hours");
|
|
scrollback_save_formats = settings_get_bool("scrollback_save_formats");
|
|
}
|
|
|
|
void gui_printtext_init(void)
|
|
{
|
|
next_xpos = next_ypos = -1;
|
|
format = g_string_new(NULL);
|
|
|
|
settings_add_int("history", "scrollback_lines", 500);
|
|
settings_add_int("history", "scrollback_hours", 24);
|
|
settings_add_bool("history", "scrollback_save_formats", FALSE);
|
|
|
|
signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
|
|
signal_add("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
|
|
signal_add("print format", (SIGNAL_FUNC) sig_print_format);
|
|
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
|
|
signal_add("beep", (SIGNAL_FUNC) beep);
|
|
command_bind("clear", NULL, (SIGNAL_FUNC) cmd_clear);
|
|
command_set_options("clear", "all");
|
|
|
|
read_settings();
|
|
}
|
|
|
|
void gui_printtext_deinit(void)
|
|
{
|
|
g_string_free(format, TRUE);
|
|
|
|
signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
|
|
signal_remove("print text finished", (SIGNAL_FUNC) sig_printtext_finished);
|
|
signal_remove("print format", (SIGNAL_FUNC) sig_print_format);
|
|
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
|
|
signal_remove("beep", (SIGNAL_FUNC) beep);
|
|
command_unbind("clear", (SIGNAL_FUNC) cmd_clear);
|
|
}
|