Merged Irssi 0.8.2 from irssi.org cvs.
[silc.git] / apps / irssi / src / fe-text / gui-printtext.c
1 /*
2  gui-printtext.c : irssi
3
4     Copyright (C) 1999 Timo Sirainen
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 */
20
21 #include "module.h"
22 #include "signals.h"
23 #include "settings.h"
24
25 #include "formats.h"
26 #include "printtext.h"
27
28 #include "term.h"
29 #include "gui-printtext.h"
30 #include "gui-windows.h"
31
32 int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 };
33 static int scrollback_lines, scrollback_hours, scrollback_burst_remove;
34
35 static int last_fg, last_bg, last_flags;
36 static int next_xpos, next_ypos;
37
38 static GHashTable *indent_functions;
39 static INDENT_FUNC default_indent_func;
40
41 void gui_register_indent_func(const char *name, INDENT_FUNC func)
42 {
43         gpointer key, value;
44         GSList *list;
45
46         if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
47                 list = value;
48                 g_hash_table_remove(indent_functions, key);
49         } else {
50                 key = g_strdup(name);
51                 list = NULL;
52         }
53
54         list = g_slist_append(list, (void *) func);
55         g_hash_table_insert(indent_functions, key, list);
56 }
57
58 void gui_unregister_indent_func(const char *name, INDENT_FUNC func)
59 {
60         gpointer key, value;
61         GSList *list;
62
63         if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
64                 list = value;
65
66                 list = g_slist_remove(list, (void *) func);
67                 g_hash_table_remove(indent_functions, key);
68                 if (list == NULL)
69                         g_free(key);
70                 else
71                         g_hash_table_insert(indent_functions, key, list);
72         }
73
74         if (default_indent_func == func)
75                 gui_set_default_indent(NULL);
76
77         textbuffer_views_unregister_indent_func(func);
78 }
79
80 void gui_set_default_indent(const char *name)
81 {
82         GSList *list;
83
84         list = name == NULL ? NULL :
85                 g_hash_table_lookup(indent_functions, name);
86         default_indent_func = list == NULL ? NULL :
87                 (INDENT_FUNC) list->data;
88         gui_windows_reset_settings();
89 }
90
91 INDENT_FUNC get_default_indent_func(void)
92 {
93         return default_indent_func;
94 }
95
96 void gui_printtext(int xpos, int ypos, const char *str)
97 {
98         next_xpos = xpos;
99         next_ypos = ypos;
100
101         printtext_gui(str);
102
103         next_xpos = next_ypos = -1;
104 }
105
106 void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str)
107 {
108         GUI_WINDOW_REC *gui;
109
110         gui = WINDOW_GUI(dest->window);
111
112         gui->use_insert_after = TRUE;
113         gui->insert_after = prev;
114         format_send_to_gui(dest, str);
115         gui->use_insert_after = FALSE;
116 }
117
118 static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
119 {
120         LINE_REC *line;
121         time_t old_time;
122
123         old_time = time(NULL)-(scrollback_hours*3600)+1;
124         if (view->buffer->lines_count >=
125             scrollback_lines+scrollback_burst_remove) {
126                 /* remove lines by line count */
127                 while (view->buffer->lines_count > scrollback_lines) {
128                         line = view->buffer->first_line;
129                         if (line->info.time >= old_time ||
130                             scrollback_lines == 0) {
131                                 /* too new line, don't remove yet - also
132                                    if scrollback_lines is 0, we want to check
133                                    only scrollback_hours setting. */
134                                 break;
135                         }
136                         textbuffer_view_remove_line(view, line);
137                 }
138         }
139 }
140
141 static void get_colors(int flags, int *fg, int *bg, int *attr)
142 {
143         if (flags & GUI_PRINT_FLAG_MIRC_COLOR) {
144                 /* mirc colors - real range is 0..15, but after 16
145                    colors wrap to 0, 1, ... */
146                 if (*bg >= 0) *bg = mirc_colors[*bg % 16];
147                 if (*fg >= 0) *fg = mirc_colors[*fg % 16];
148                 if (settings_get_bool("mirc_blink_fix"))
149                         *bg &= ~0x08;
150         }
151
152         if (*fg < 0 || *fg > 15)
153                 *fg = -1;
154         if (*bg < 0 || *bg > 15)
155                 *bg = -1;
156
157         *attr = 0;
158         if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE;
159         if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD;
160         if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE;
161         if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK;
162 }
163
164 static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
165                             int fg, int bg, int flags)
166 {
167         unsigned char data[20];
168         int pos;
169
170         /* get the fg & bg command chars */
171         fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f;
172         bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f);
173         if (flags & GUI_PRINT_FLAG_BOLD)
174                 fg |= LINE_COLOR_BOLD;
175         if (flags & GUI_PRINT_FLAG_BLINK)
176                 bg |= LINE_COLOR_BLINK;
177
178         pos = 0;
179         if (fg != last_fg) {
180                 last_fg = fg;
181                 data[pos++] = 0;
182                 data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg;
183         }
184         if (bg != last_bg) {
185                 last_bg = bg;
186                 data[pos++] = 0;
187                 data[pos++] = bg;
188         }
189
190         if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) {
191                 data[pos++] = 0;
192                 data[pos++] = LINE_CMD_UNDERLINE;
193         }
194         if ((flags & GUI_PRINT_FLAG_REVERSE) != (last_flags & GUI_PRINT_FLAG_REVERSE)) {
195                 data[pos++] = 0;
196                 data[pos++] = LINE_CMD_REVERSE;
197         }
198         if (flags & GUI_PRINT_FLAG_INDENT) {
199                 data[pos++] = 0;
200                 data[pos++] = LINE_CMD_INDENT;
201         }
202
203         if (pos > 0)
204                 *line = textbuffer_insert(buffer, *line, data, pos, NULL);
205
206         last_flags = flags;
207 }
208
209 static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line,
210                                  const char *function)
211 {
212         GSList *list;
213         unsigned char data[1+sizeof(INDENT_FUNC)];
214
215         list = g_hash_table_lookup(indent_functions, function);
216         if (list != NULL) {
217                 data[0] = LINE_CMD_INDENT_FUNC;
218                 memcpy(data+1, list->data, sizeof(INDENT_FUNC));
219                 *line = textbuffer_insert(buffer, *line,
220                                           data, sizeof(data), NULL);
221         }
222 }
223
224 static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
225 {
226         static const unsigned char eol[] = { 0, LINE_CMD_EOL };
227
228         *line = textbuffer_insert(view->buffer, *line, eol, 2, NULL);
229         textbuffer_view_insert_line(view, *line);
230 }
231
232 static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
233                                void *bgcolor, void *pflags,
234                                char *str, void *level)
235 {
236         GUI_WINDOW_REC *gui;
237         TEXT_BUFFER_VIEW_REC *view;
238         LINE_REC *insert_after;
239         LINE_INFO_REC lineinfo;
240         int fg, bg, flags, attr;
241
242         flags = GPOINTER_TO_INT(pflags);
243         fg = GPOINTER_TO_INT(fgcolor);
244         bg = GPOINTER_TO_INT(bgcolor);
245         get_colors(flags, &fg, &bg, &attr);
246
247         if (window == NULL) {
248                 g_return_if_fail(next_xpos != -1);
249
250                 attr |= fg >= 0 ? fg : ATTR_RESETFG;
251                 attr |= bg >= 0 ? (bg << 4) : ATTR_RESETBG;
252                 term_set_color(root_window, attr);
253
254                 term_move(root_window, next_xpos, next_ypos);
255                 if (flags & GUI_PRINT_FLAG_CLRTOEOL)
256                         term_clrtoeol(root_window);
257                 term_addstr(root_window, str);
258                 next_xpos += strlen(str);
259                 return;
260         }
261
262         lineinfo.level = GPOINTER_TO_INT(level);
263         lineinfo.time = time(NULL);
264
265         gui = WINDOW_GUI(window);
266         view = gui->view;
267         insert_after = gui->use_insert_after ?
268                 gui->insert_after : view->buffer->cur_line;
269
270         if (flags & GUI_PRINT_FLAG_NEWLINE)
271                 view_add_eol(view, &insert_after);
272         line_add_colors(view->buffer, &insert_after, fg, bg, flags);
273
274         if (flags & GUI_PRINT_FLAG_INDENT_FUNC) {
275                 /* specify the indentation function */
276                 line_add_indent_func(view->buffer, &insert_after, str);
277         } else {
278                 insert_after = textbuffer_insert(view->buffer, insert_after,
279                                                  (unsigned char *) str,
280                                                  strlen(str), &lineinfo);
281         }
282         if (gui->use_insert_after)
283                 gui->insert_after = insert_after;
284 }
285
286 static void sig_gui_printtext_finished(WINDOW_REC *window)
287 {
288         TEXT_BUFFER_VIEW_REC *view;
289         LINE_REC *insert_after;
290
291         last_fg = last_bg = -1;
292         last_flags = 0;
293
294         view = WINDOW_GUI(window)->view;
295         insert_after = WINDOW_GUI(window)->use_insert_after ?
296                 WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
297
298         view_add_eol(view, &insert_after);
299         remove_old_lines(view);
300 }
301
302 static void read_settings(void)
303 {
304         scrollback_lines = settings_get_int("scrollback_lines");
305         scrollback_hours = settings_get_int("scrollback_hours");
306         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
307 }
308
309 void gui_printtext_init(void)
310 {
311         next_xpos = next_ypos = -1;
312         default_indent_func = NULL;
313         indent_functions = g_hash_table_new((GHashFunc) g_str_hash,
314                                             (GCompareFunc) g_str_equal);
315
316         settings_add_int("history", "scrollback_lines", 500);
317         settings_add_int("history", "scrollback_hours", 24);
318         settings_add_int("history", "scrollback_burst_remove", 10);
319
320         signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
321         signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
322         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
323
324         read_settings();
325 }
326
327 void gui_printtext_deinit(void)
328 {
329         g_hash_table_destroy(indent_functions);
330
331         signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
332         signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
333         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
334 }