Added SILC Thread Queue API
[crypto.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 #ifdef HAVE_CUIX
32 #include "cuix.h"
33 #endif
34
35 int mirc_colors[] = { 15, 0, 1, 2, 12, 4, 5, 6, 14, 10, 3, 11, 9, 13, 8, 7 };
36 static int scrollback_lines, scrollback_time, scrollback_burst_remove;
37
38 static int last_fg, last_bg, last_flags;
39 static int next_xpos, next_ypos;
40
41 static GHashTable *indent_functions;
42 static INDENT_FUNC default_indent_func;
43
44 void gui_register_indent_func(const char *name, INDENT_FUNC func)
45 {
46         gpointer key, value;
47         GSList *list;
48
49         if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
50                 list = value;
51                 g_hash_table_remove(indent_functions, key);
52         } else {
53                 key = g_strdup(name);
54                 list = NULL;
55         }
56
57         list = g_slist_append(list, (void *) func);
58         g_hash_table_insert(indent_functions, key, list);
59 }
60
61 void gui_unregister_indent_func(const char *name, INDENT_FUNC func)
62 {
63         gpointer key, value;
64         GSList *list;
65
66         if (g_hash_table_lookup_extended(indent_functions, name, &key, &value)) {
67                 list = value;
68
69                 list = g_slist_remove(list, (void *) func);
70                 g_hash_table_remove(indent_functions, key);
71                 if (list == NULL)
72                         g_free(key);
73                 else
74                         g_hash_table_insert(indent_functions, key, list);
75         }
76
77         if (default_indent_func == func)
78                 gui_set_default_indent(NULL);
79
80         textbuffer_views_unregister_indent_func(func);
81 }
82
83 void gui_set_default_indent(const char *name)
84 {
85         GSList *list;
86
87         list = name == NULL ? NULL :
88                 g_hash_table_lookup(indent_functions, name);
89         default_indent_func = list == NULL ? NULL :
90                 (INDENT_FUNC) list->data;
91         gui_windows_reset_settings();
92 }
93
94 INDENT_FUNC get_default_indent_func(void)
95 {
96         return default_indent_func;
97 }
98
99 void gui_printtext(int xpos, int ypos, const char *str)
100 {
101         next_xpos = xpos;
102         next_ypos = ypos;
103
104         printtext_gui(str);
105
106         next_xpos = next_ypos = -1;
107 }
108
109 void gui_printtext_after(TEXT_DEST_REC *dest, LINE_REC *prev, const char *str)
110 {
111         GUI_WINDOW_REC *gui;
112
113         gui = WINDOW_GUI(dest->window);
114
115         gui->use_insert_after = TRUE;
116         gui->insert_after = prev;
117         format_send_to_gui(dest, str);
118         gui->use_insert_after = FALSE;
119 }
120
121 static void remove_old_lines(TEXT_BUFFER_VIEW_REC *view)
122 {
123         LINE_REC *line;
124         time_t old_time;
125
126         old_time = time(NULL)-scrollback_time+1;
127         if (view->buffer->lines_count >=
128             scrollback_lines+scrollback_burst_remove) {
129                 /* remove lines by line count */
130                 while (view->buffer->lines_count > scrollback_lines) {
131                         line = view->buffer->first_line;
132                         if (line->info.time >= old_time ||
133                             scrollback_lines == 0) {
134                                 /* too new line, don't remove yet - also
135                                    if scrollback_lines is 0, we want to check
136                                    only scrollback_time setting. */
137                                 break;
138                         }
139                         textbuffer_view_remove_line(view, line);
140                 }
141         }
142 }
143
144 static void get_colors(int flags, int *fg, int *bg, int *attr)
145 {
146         if (flags & GUI_PRINT_FLAG_MIRC_COLOR) {
147                 /* mirc colors - real range is 0..15, but after 16
148                    colors wrap to 0, 1, ... */
149                 if (*bg >= 0) *bg = mirc_colors[*bg % 16];
150                 if (*fg >= 0) *fg = mirc_colors[*fg % 16];
151                 if (settings_get_bool("mirc_blink_fix"))
152                         *bg &= ~0x08;
153         }
154
155         if (*fg < 0 || *fg > 15)
156                 *fg = -1;
157         if (*bg < 0 || *bg > 15)
158                 *bg = -1;
159
160         *attr = 0;
161         if (flags & GUI_PRINT_FLAG_REVERSE) *attr |= ATTR_REVERSE;
162         if (flags & GUI_PRINT_FLAG_BOLD) *attr |= ATTR_BOLD;
163         if (flags & GUI_PRINT_FLAG_UNDERLINE) *attr |= ATTR_UNDERLINE;
164         if (flags & GUI_PRINT_FLAG_BLINK) *attr |= ATTR_BLINK;
165 }
166
167 static void line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
168                             int fg, int bg, int flags)
169 {
170         unsigned char data[20];
171         int pos;
172
173         /* get the fg & bg command chars */
174         fg = fg < 0 ? LINE_COLOR_DEFAULT : fg & 0x0f;
175         bg = LINE_COLOR_BG | (bg < 0 ? LINE_COLOR_DEFAULT : bg & 0x0f);
176         if (flags & GUI_PRINT_FLAG_BOLD)
177                 fg |= LINE_COLOR_BOLD;
178         if (flags & GUI_PRINT_FLAG_BLINK)
179                 bg |= LINE_COLOR_BLINK;
180
181         pos = 0;
182         if (fg != last_fg) {
183                 last_fg = fg;
184                 data[pos++] = 0;
185                 data[pos++] = fg == 0 ? LINE_CMD_COLOR0 : fg;
186         }
187         if (bg != last_bg) {
188                 last_bg = bg;
189                 data[pos++] = 0;
190                 data[pos++] = bg;
191         }
192
193         if ((flags & GUI_PRINT_FLAG_UNDERLINE) != (last_flags & GUI_PRINT_FLAG_UNDERLINE)) {
194                 data[pos++] = 0;
195                 data[pos++] = LINE_CMD_UNDERLINE;
196         }
197         if ((flags & GUI_PRINT_FLAG_REVERSE) != (last_flags & GUI_PRINT_FLAG_REVERSE)) {
198                 data[pos++] = 0;
199                 data[pos++] = LINE_CMD_REVERSE;
200         }
201         if (flags & GUI_PRINT_FLAG_INDENT) {
202                 data[pos++] = 0;
203                 data[pos++] = LINE_CMD_INDENT;
204         }
205
206         if (pos > 0)
207                 *line = textbuffer_insert(buffer, *line, data, pos, NULL);
208
209         last_flags = flags;
210 }
211
212 static void line_add_indent_func(TEXT_BUFFER_REC *buffer, LINE_REC **line,
213                                  const char *function)
214 {
215         GSList *list;
216         unsigned char data[1+sizeof(INDENT_FUNC)];
217
218         list = g_hash_table_lookup(indent_functions, function);
219         if (list != NULL) {
220                 data[0] = LINE_CMD_INDENT_FUNC;
221                 memcpy(data+1, list->data, sizeof(INDENT_FUNC));
222                 *line = textbuffer_insert(buffer, *line,
223                                           data, sizeof(data), NULL);
224         }
225 }
226
227 static void view_add_eol(TEXT_BUFFER_VIEW_REC *view, LINE_REC **line)
228 {
229         static const unsigned char eol[] = { 0, LINE_CMD_EOL };
230
231         *line = textbuffer_insert(view->buffer, *line, eol, 2, NULL);
232         textbuffer_view_insert_line(view, *line);
233 }
234
235 static void sig_gui_print_text(WINDOW_REC *window, void *fgcolor,
236                                void *bgcolor, void *pflags,
237                                char *str, TEXT_DEST_REC *dest)
238 {
239         GUI_WINDOW_REC *gui;
240         TEXT_BUFFER_VIEW_REC *view;
241         LINE_REC *insert_after;
242         LINE_INFO_REC lineinfo;
243         int fg, bg, flags, attr;
244
245         flags = GPOINTER_TO_INT(pflags);
246         fg = GPOINTER_TO_INT(fgcolor);
247         bg = GPOINTER_TO_INT(bgcolor);
248         get_colors(flags, &fg, &bg, &attr);
249
250         if (window == NULL) {
251                 g_return_if_fail(next_xpos != -1);
252
253                 attr |= fg >= 0 ? fg : ATTR_RESETFG;
254                 attr |= bg >= 0 ? (bg << 4) : ATTR_RESETBG;
255                 term_set_color(root_window, attr);
256
257                 term_move(root_window, next_xpos, next_ypos);
258                 if (flags & GUI_PRINT_FLAG_CLRTOEOL)
259                         term_clrtoeol(root_window);
260                 term_addstr(root_window, str);
261                 next_xpos += strlen(str); /* FIXME utf8 or big5 */
262                 return;
263         }
264
265         lineinfo.level = dest == NULL ? 0 : dest->level;
266         lineinfo.time = time(NULL);
267
268         gui = WINDOW_GUI(window);
269         view = gui->view;
270         insert_after = gui->use_insert_after ?
271                 gui->insert_after : view->buffer->cur_line;
272
273         if (flags & GUI_PRINT_FLAG_NEWLINE)
274                 view_add_eol(view, &insert_after);
275         line_add_colors(view->buffer, &insert_after, fg, bg, flags);
276
277         if (flags & GUI_PRINT_FLAG_INDENT_FUNC) {
278                 /* specify the indentation function */
279                 line_add_indent_func(view->buffer, &insert_after, str);
280         } else {
281                 insert_after = textbuffer_insert(view->buffer, insert_after,
282                                                  (unsigned char *) str,
283                                                  strlen(str), &lineinfo);
284         }
285         if (gui->use_insert_after)
286                 gui->insert_after = insert_after;
287 #ifdef HAVE_CUIX
288         cuix_refresh ();
289 #endif
290 }
291
292 static void sig_gui_printtext_finished(WINDOW_REC *window)
293 {
294         TEXT_BUFFER_VIEW_REC *view;
295         LINE_REC *insert_after;
296
297         last_fg = last_bg = -1;
298         last_flags = 0;
299
300         view = WINDOW_GUI(window)->view;
301         insert_after = WINDOW_GUI(window)->use_insert_after ?
302                 WINDOW_GUI(window)->insert_after : view->buffer->cur_line;
303
304         view_add_eol(view, &insert_after);
305         remove_old_lines(view);
306 }
307
308 static void read_settings(void)
309 {
310         scrollback_lines = settings_get_int("scrollback_lines");
311         scrollback_time = settings_get_time("scrollback_time")/1000;
312         scrollback_burst_remove = settings_get_int("scrollback_burst_remove");
313 }
314
315 void gui_printtext_init(void)
316 {
317         next_xpos = next_ypos = -1;
318         default_indent_func = NULL;
319         indent_functions = g_hash_table_new((GHashFunc) g_str_hash,
320                                             (GCompareFunc) g_str_equal);
321
322         settings_add_int("history", "scrollback_lines", 500);
323         settings_add_time("history", "scrollback_time", "1day");
324         settings_add_int("history", "scrollback_burst_remove", 10);
325
326         signal_add("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
327         signal_add("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
328         signal_add("setup changed", (SIGNAL_FUNC) read_settings);
329
330         read_settings();
331 }
332
333 void gui_printtext_deinit(void)
334 {
335         g_hash_table_destroy(indent_functions);
336
337         signal_remove("gui print text", (SIGNAL_FUNC) sig_gui_print_text);
338         signal_remove("gui print text finished", (SIGNAL_FUNC) sig_gui_printtext_finished);
339         signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
340 }