Added SILC Thread Queue API
[runtime.git] / apps / irssi / src / fe-text / textbuffer.c
index f97f46c74144336cad34ae769f9e4d3c75b1c9fe..930e55ad6d205365adc909c808232f313243c5ef 100644 (file)
@@ -18,6 +18,8 @@
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
 
+#define        G_LOG_DOMAIN "TextBuffer"
+
 #include "module.h"
 #include "misc.h"
 #include "formats.h"
@@ -73,7 +75,7 @@ static TEXT_CHUNK_REC *text_chunk_find(TEXT_BUFFER_REC *buffer,
 static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
 {
        TEXT_CHUNK_REC *rec;
-       char *buf, *ptr, **pptr;
+       unsigned char *buf, *ptr, **pptr;
 
        rec = g_mem_chunk_alloc(text_chunk);
        rec->pos = 0;
@@ -90,7 +92,7 @@ static TEXT_CHUNK_REC *text_chunk_create(TEXT_BUFFER_REC *buffer)
                   breaks at least NetBSD/Alpha, so don't go "optimize"
                   it :) */
                ptr = rec->buffer; pptr = &ptr;
-               memcpy(buf, pptr, sizeof(char *));
+               memcpy(buf, pptr, sizeof(unsigned char *));
        } else {
                /* just to be safe */
                mark_temp_eol(rec);
@@ -111,15 +113,16 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 {
        TEXT_CHUNK_REC *chunk;
        const unsigned char *text;
-        unsigned char *tmp = NULL;
+        unsigned char cmd, *tmp = NULL;
 
        for (text = line->text;; text++) {
                if (*text != '\0')
                         continue;
 
                text++;
-               if (*text == LINE_CMD_CONTINUE || *text == LINE_CMD_EOL) {
-                       if (*text == LINE_CMD_CONTINUE)
+               cmd = *text;
+               if (cmd == LINE_CMD_CONTINUE || cmd == LINE_CMD_EOL) {
+                       if (cmd == LINE_CMD_CONTINUE)
                                memcpy(&tmp, text+1, sizeof(char *));
 
                        /* free the previous block */
@@ -131,16 +134,18 @@ static void text_chunk_line_free(TEXT_BUFFER_REC *buffer, LINE_REC *line)
                                        text_chunk_destroy(buffer, chunk);
                        }
 
-                       if (*text == LINE_CMD_EOL)
+                       if (cmd == LINE_CMD_EOL)
                                break;
 
                        text = tmp-1;
+               } else if (cmd == LINE_CMD_INDENT_FUNC) {
+                       text += sizeof(int (*) ());
                }
        }
 }
 
 static void text_chunk_append(TEXT_BUFFER_REC *buffer,
-                             const char *data, int len)
+                             const unsigned char *data, int len)
 {
         TEXT_CHUNK_REC *chunk;
        int left;
@@ -151,7 +156,8 @@ static void text_chunk_append(TEXT_BUFFER_REC *buffer,
         chunk = buffer->cur_text;
        while (chunk->pos + len >= TEXT_CHUNK_USABLE_SIZE) {
                left = TEXT_CHUNK_USABLE_SIZE - chunk->pos;
-               if (data[left-1] == 0) left--; /* don't split the commands */
+               if (left > 0 && data[left-1] == 0)
+                       left--; /* don't split the commands */
 
                memcpy(chunk->buffer + chunk->pos, data, left);
                chunk->pos += left;
@@ -187,14 +193,22 @@ static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
 {
        LINE_REC *line;
 
-        line = textbuffer_line_create(buffer);
-       if (prev == buffer->cur_line) {
-               buffer->cur_line = line;
-               buffer->lines = g_list_append(buffer->lines, buffer->cur_line);
+       line = textbuffer_line_create(buffer);
+       line->prev = prev;
+       if (prev == NULL) {
+               line->next = buffer->first_line;
+                if (buffer->first_line != NULL)
+                       buffer->first_line->prev = line;
+               buffer->first_line = line;
        } else {
-               buffer->lines = g_list_insert(buffer->lines, line,
-                                             g_list_index(buffer->lines, prev)+1);
+               line->next = prev->next;
+                if (line->next != NULL)
+                       line->next->prev = line;
+               prev->next = line;
        }
+
+       if (prev == buffer->cur_line)
+               buffer->cur_line = line;
         buffer->lines_count++;
 
         return line;
@@ -224,11 +238,34 @@ void textbuffer_line_unref_list(TEXT_BUFFER_REC *buffer, GList *list)
        g_return_if_fail(buffer != NULL);
 
        while (list != NULL) {
-                textbuffer_line_unref(buffer, list->data);
+                if (list->data != NULL)
+                       textbuffer_line_unref(buffer, list->data);
                 list = list->next;
        }
 }
 
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
+{
+       LINE_REC *line;
+
+       line = buffer->cur_line;
+       if (line != NULL) {
+               while (line->next != NULL)
+                       line = line->next;
+       }
+        return line;
+}
+
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
+{
+       while (line != NULL) {
+               if (line == search)
+                       return TRUE;
+                line = line->next;
+       }
+        return FALSE;
+}
+
 LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
                            const unsigned char *data, int len,
                            LINE_INFO_REC *info)
@@ -245,6 +282,9 @@ LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(data != NULL, NULL);
 
+       if (len == 0)
+                return insert_after;
+
        line = !buffer->last_eol ? insert_after :
                textbuffer_line_insert(buffer, insert_after);
 
@@ -264,13 +304,20 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
        g_return_if_fail(buffer != NULL);
        g_return_if_fail(line != NULL);
 
-       buffer->lines = g_list_remove(buffer->lines, line);
+       if (buffer->first_line == line)
+               buffer->first_line = line->next;
+       if (line->prev != NULL)
+               line->prev->next = line->next;
+       if (line->next != NULL)
+               line->next->prev = line->prev;
 
        if (buffer->cur_line == line) {
-               buffer->cur_line = buffer->lines == NULL ? NULL :
-                       g_list_last(buffer->lines)->data;
+               buffer->cur_line = line->next != NULL ?
+                       line->next : line->prev;
        }
 
+        line->prev = line->next = NULL;
+
        buffer->lines_count--;
         textbuffer_line_unref(buffer, line);
 }
@@ -279,40 +326,85 @@ void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
 void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
 {
        GSList *tmp;
+        LINE_REC *line;
 
        g_return_if_fail(buffer != NULL);
 
        for (tmp = buffer->text_chunks; tmp != NULL; tmp = tmp->next)
                 g_mem_chunk_free(text_chunk, tmp->data);
        g_slist_free(buffer->text_chunks);
-        buffer->text_chunks = NULL;
+       buffer->text_chunks = NULL;
 
-       g_list_free(buffer->lines);
-        buffer->lines = NULL;
+       while (buffer->first_line != NULL) {
+               line = buffer->first_line->next;
+               g_mem_chunk_free(line_chunk, buffer->first_line);
+                buffer->first_line = line;
+       }
+       buffer->lines_count = 0;
 
         buffer->cur_line = NULL;
-       buffer->lines_count = 0;
+        buffer->cur_text = NULL;
+
+       buffer->last_eol = TRUE;
+}
+
+static void set_color(GString *str, int cmd, int *last_fg, int *last_bg)
+{
+       if (cmd & LINE_COLOR_DEFAULT) {
+               g_string_sprintfa(str, "\004%c", FORMAT_STYLE_DEFAULTS);
+
+               /* need to reset the fg/bg color */
+               if (cmd & LINE_COLOR_BG) {
+                        *last_bg = -1;
+                       if (*last_fg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 *last_fg,
+                                                 FORMAT_COLOR_NOCHANGE);
+                       }
+               } else {
+                        *last_fg = -1;
+                       if (*last_bg != -1) {
+                               g_string_sprintfa(str, "\004%c%c",
+                                                 FORMAT_COLOR_NOCHANGE,
+                                                 *last_bg);
+                       }
+               }
+                return;
+       }
+
+       if ((cmd & LINE_COLOR_BG) == 0) {
+                /* change foreground color */
+                *last_fg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c", *last_fg,
+                                 FORMAT_COLOR_NOCHANGE);
+       } else {
+               /* change background color */
+                *last_bg = (cmd & 0x0f)+'0';
+               g_string_sprintfa(str, "\004%c%c",
+                                 FORMAT_COLOR_NOCHANGE, *last_bg);
+       }
 }
 
 void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 {
-        unsigned char cmd;
-       char *ptr, *tmp;
+        unsigned char cmd, *ptr, *tmp;
+        int last_fg, last_bg;
 
        g_return_if_fail(line != NULL);
        g_return_if_fail(str != NULL);
 
         g_string_truncate(str, 0);
 
+        last_fg = last_bg = -1;
        for (ptr = line->text;;) {
                if (*ptr != 0) {
-                       g_string_append_c(str, *ptr);
+                       g_string_append_c(str, (char) *ptr);
                         ptr++;
                        continue;
                }
 
                ptr++;
-                cmd = (unsigned char) *ptr;
+                cmd = *ptr;
                ptr++;
 
                if (cmd == LINE_CMD_EOL || cmd == LINE_CMD_FORMAT) {
@@ -322,7 +414,7 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if (cmd == LINE_CMD_CONTINUE) {
                         /* line continues in another address.. */
-                       memcpy(&tmp, ptr, sizeof(char *));
+                       memcpy(&tmp, ptr, sizeof(unsigned char *));
                        ptr = tmp;
                         continue;
                }
@@ -334,40 +426,39 @@ void textbuffer_line2text(LINE_REC *line, int coloring, GString *str)
 
                if ((cmd & 0x80) == 0) {
                        /* set color */
-                       g_string_sprintfa(str, "\004%c%c",
-                                         (cmd & 0x0f)+'0',
-                                         ((cmd & 0xf0) >> 4)+'0');
+                        set_color(str, cmd, &last_fg, &last_bg);
                } else switch (cmd) {
                case LINE_CMD_UNDERLINE:
                        g_string_append_c(str, 31);
                        break;
+               case LINE_CMD_REVERSE:
+                       g_string_append_c(str, 22);
+                       break;
                case LINE_CMD_COLOR0:
                        g_string_sprintfa(str, "\004%c%c",
                                          '0', FORMAT_COLOR_NOCHANGE);
                        break;
-               case LINE_CMD_COLOR8:
-                       g_string_sprintfa(str, "\004%c%c",
-                                         '8', FORMAT_COLOR_NOCHANGE);
-                       break;
-               case LINE_CMD_BLINK:
-                       g_string_sprintfa(str, "\004%c", FORMAT_STYLE_BLINK);
-                       break;
                case LINE_CMD_INDENT:
                        break;
+               case LINE_CMD_INDENT_FUNC:
+                        ptr += sizeof(void *);
+                       break;
                }
        }
 }
 
 GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
                            int level, int nolevel, const char *text,
+                           int before, int after,
                            int regexp, int fullword, int case_sensitive)
 {
 #ifdef HAVE_REGEX_H
        regex_t preg;
 #endif
-       GList *line, *tmp;
+        LINE_REC *line, *pre_line;
        GList *matches;
-        GString *str;
+       GString *str;
+        int i, match_after, line_matched;
 
        g_return_val_if_fail(buffer != NULL, NULL);
        g_return_val_if_fail(text != NULL, NULL);
@@ -383,216 +474,66 @@ GList *textbuffer_find_text(TEXT_BUFFER_REC *buffer, LINE_REC *startline,
 #endif
        }
 
-       matches = NULL;
+       matches = NULL; match_after = 0;
         str = g_string_new(NULL);
 
-        line = g_list_find(buffer->lines, startline);
-       if (line == NULL)
-               line = buffer->lines;
+       line = startline != NULL ? startline : buffer->first_line;
 
-       for (tmp = line; tmp != NULL; tmp = tmp->next) {
-               LINE_REC *rec = tmp->data;
-
-               if ((rec->info.level & level) == 0 ||
-                   (rec->info.level & nolevel) != 0)
+       for (; line != NULL; line = line->next) {
+               if ((line->info.level & level) == 0 ||
+                   (line->info.level & nolevel) != 0)
                         continue;
 
                if (*text == '\0') {
                         /* no search word, everything matches */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
                        continue;
                }
 
-                textbuffer_line2text(rec, FALSE, str);
+                textbuffer_line2text(line, FALSE, str);
 
-                if (
+               line_matched =
 #ifdef HAVE_REGEX_H
-                   regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
+                       regexp ? regexec(&preg, str->str, 0, NULL, 0) == 0 :
 #endif
-                   fullword ? strstr_full_case(str->str, text,
-                                               !case_sensitive) != NULL :
-                   case_sensitive ? strstr(str->str, text) != NULL :
-                                    stristr(str->str, text) != NULL) {
-                       /* matched */
-                        textbuffer_line_ref(rec);
-                       matches = g_list_append(matches, rec);
-               }
-       }
-#ifdef HAVE_REGEX_H
-       if (regexp) regfree(&preg);
-#endif
-        g_string_free(str, TRUE);
-       return matches;
-}
-
-#if 0 /* FIXME: saving formats is broken */
-static char *line_read_format(unsigned const char **text)
-{
-       GString *str;
-       char *ret;
-
-       str = g_string_new(NULL);
-       for (;;) {
-               if (**text == '\0') {
-                       if ((*text)[1] == LINE_CMD_EOL) {
-                               /* leave text at \0<eof> */
-                               break;
+                       fullword ? strstr_full_case(str->str, text, !case_sensitive) != NULL :
+                       case_sensitive ? strstr(str->str, text) != NULL :
+                       stristr(str->str, text) != NULL;
+               if (line_matched) {
+                        /* add the -before lines */
+                       pre_line = line;
+                       for (i = 0; i < before; i++) {
+                               if (pre_line->prev == NULL ||
+                                   g_list_find(matches, pre_line->prev) != NULL)
+                                       break;
+                                pre_line = pre_line->prev;
                        }
-                       if ((*text)[1] == LINE_CMD_FORMAT_CONT) {
-                               /* leave text at \0<format_cont> */
-                               break;
-                       }
-                       (*text)++;
 
-                       if (**text == LINE_CMD_FORMAT) {
-                               /* move text to start after \0<format> */
-                               (*text)++;
-                               break;
+                       for (; pre_line != line; pre_line = pre_line->next) {
+                               textbuffer_line_ref(pre_line);
+                               matches = g_list_append(matches, pre_line);
                        }
 
-                       if (**text == LINE_CMD_CONTINUE) {
-                               unsigned char *tmp;
-
-                               memcpy(&tmp, (*text)+1, sizeof(char *));
-                               *text = tmp;
-                               continue;
-                       } else if (**text & 0x80)
-                               (*text)++;
-                       continue;
+                       match_after = after;
                }
 
-               g_string_append_c(str, (char) **text);
-               (*text)++;
-       }
-
-       ret = str->str;
-       g_string_free(str, FALSE);
-       return ret;
-}
-
-static char *textbuffer_line_get_format(WINDOW_REC *window, LINE_REC *line,
-                                       GString *raw)
-{
-       const unsigned char *text;
-       char *module, *format_name, *args[MAX_FORMAT_PARAMS], *ret;
-       TEXT_DEST_REC dest;
-       int formatnum, argcount;
-
-       text = (const unsigned char *) line->text;
-
-       /* skip the beginning of the line until we find the format */
-       g_free(line_read_format(&text));
-       if (text[1] == LINE_CMD_FORMAT_CONT) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT_CONT);
-               return NULL;
-       }
-
-       /* read format information */
-        module = line_read_format(&text);
-       format_name = line_read_format(&text);
-
-       if (raw != NULL) {
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, module);
-
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_FORMAT);
-
-               g_string_append(raw, format_name);
-       }
+               if (line_matched || match_after > 0) {
+                       /* matched */
+                        textbuffer_line_ref(line);
+                       matches = g_list_append(matches, line);
 
-       formatnum = format_find_tag(module, format_name);
-       if (formatnum == -1)
-               ret = NULL;
-       else {
-                argcount = 0;
-                memset(args, 0, sizeof(args));
-               while (*text != '\0' || text[1] != LINE_CMD_EOL) {
-                       args[argcount] = line_read_format(&text);
-                       if (raw != NULL) {
-                               g_string_append_c(raw, '\0');
-                               g_string_append_c(raw,
-                                                 (char)LINE_CMD_FORMAT);
-
-                               g_string_append(raw, args[argcount]);
-                       }
-                       argcount++;
+                       if ((!line_matched && --match_after == 0) ||
+                           (line_matched && match_after == 0 && before > 0))
+                               matches = g_list_append(matches, NULL);
                }
-
-               /* get the format text */
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-               ret = format_get_text_theme_charargs(current_theme,
-                                                    module, &dest,
-                                                    formatnum, args);
-               while (argcount > 0)
-                       g_free(args[--argcount]);
-       }
-
-       g_free(module);
-       g_free(format_name);
-
-       return ret;
-}
-
-void textbuffer_reformat_line(WINDOW_REC *window, LINE_REC *line)
-{
-       GUI_WINDOW_REC *gui;
-       TEXT_DEST_REC dest;
-       GString *raw;
-       char *str, *tmp, *prestr, *linestart, *leveltag;
-
-       gui = WINDOW_GUI(window);
-
-       raw = g_string_new(NULL);
-       str = textbuffer_line_get_format(window, line, raw);
-
-        if (str == NULL && raw->len == 2 &&
-            raw->str[1] == (char)LINE_CMD_FORMAT_CONT) {
-                /* multiline format, format explained in one the
-                   following lines. remove this line. */
-                textbuffer_line_remove(window, line, FALSE);
-       } else if (str != NULL) {
-                /* FIXME: ugly ugly .. and this can't handle
-                   non-formatted lines.. */
-               g_string_append_c(raw, '\0');
-               g_string_append_c(raw, (char)LINE_CMD_EOL);
-
-                textbuffer_line_text_free(gui, line);
-
-                gui->temp_line = line;
-               gui->temp_line->text = gui->cur_text->buffer+gui->cur_text->pos;
-                gui->cur_text->lines++;
-               gui->eol_marked = FALSE;
-
-               format_create_dest(&dest, NULL, NULL, line->level, window);
-
-               linestart = format_get_line_start(current_theme, &dest, line->time);
-               leveltag = format_get_level_tag(current_theme, &dest);
-
-               prestr = g_strconcat(linestart == NULL ? "" : linestart,
-                                    leveltag, NULL);
-               g_free_not_null(linestart);
-               g_free_not_null(leveltag);
-
-               tmp = format_add_linestart(str, prestr);
-               g_free(str);
-               g_free(prestr);
-
-               format_send_to_gui(&dest, tmp);
-               g_free(tmp);
-
-               textbuffer_line_append(gui, raw->str, raw->len);
-
-               gui->eol_marked = TRUE;
-               gui->temp_line = NULL;
        }
-       g_string_free(raw, TRUE);
-}
+#ifdef HAVE_REGEX_H
+       if (regexp) regfree(&preg);
 #endif
+        g_string_free(str, TRUE);
+       return matches;
+}
 
 void textbuffer_init(void)
 {