summaryrefslogtreecommitdiffstats
path: root/src/fe-text/textbuffer.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/fe-text/textbuffer.c345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/fe-text/textbuffer.c b/src/fe-text/textbuffer.c
new file mode 100644
index 0000000..8a206f2
--- /dev/null
+++ b/src/fe-text/textbuffer.c
@@ -0,0 +1,345 @@
+/*
+ textbuffer.c : Text buffer handling
+
+ Copyright (C) 1999-2001 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.
+*/
+
+#define G_LOG_DOMAIN "TextBuffer"
+
+#include "module.h"
+#include <irssi/src/core/misc.h>
+#include <irssi/src/fe-common/core/formats.h>
+#include <irssi/src/core/utf8.h>
+#include <irssi/src/core/iregex.h>
+
+#include <irssi/src/fe-text/textbuffer-formats.h>
+#include <irssi/src/fe-text/textbuffer.h>
+
+#define TEXT_CHUNK_USABLE_SIZE (LINE_TEXT_CHUNK_SIZE-2-(int)sizeof(char*))
+
+TEXT_BUFFER_REC *textbuffer_create(WINDOW_REC *window)
+{
+ TEXT_BUFFER_REC *buffer;
+
+ buffer = g_slice_new0(TEXT_BUFFER_REC);
+ buffer->window = window;
+ buffer->last_eol = TRUE;
+ buffer->last_fg = -1;
+ buffer->last_bg = -1;
+ buffer->cur_text = g_string_sized_new(TEXT_CHUNK_USABLE_SIZE);
+ return buffer;
+}
+
+void textbuffer_destroy(TEXT_BUFFER_REC *buffer)
+{
+ GSList *tmp;
+
+ g_return_if_fail(buffer != NULL);
+
+ textbuffer_remove_all_lines(buffer);
+ g_string_free(buffer->cur_text, TRUE);
+ for (tmp = buffer->cur_info; tmp != NULL; tmp = tmp->next) {
+ LINE_INFO_REC *info = buffer->cur_info->data;
+ textbuffer_line_info_free1(info);
+ g_free(info);
+ }
+ g_slist_free(buffer->cur_info);
+
+ buffer->window = NULL;
+ g_slice_free(TEXT_BUFFER_REC, buffer);
+}
+
+void textbuffer_line_info_free1(LINE_INFO_REC *info)
+{
+ textbuffer_format_rec_free(info->format);
+ textbuffer_meta_rec_free(info->meta);
+ g_free(info->text);
+}
+
+static void text_chunk_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len)
+{
+ if (len == 0)
+ return;
+
+ /* g_string_append_len(buffer->cur_text, (const char *)data, len); */
+ g_string_append(buffer->cur_text, (const char *) data);
+}
+
+static LINE_REC *textbuffer_line_create(TEXT_BUFFER_REC *buffer)
+{
+ LINE_REC *rec;
+
+ rec = g_slice_new0(LINE_REC);
+ return rec;
+}
+
+static LINE_REC *textbuffer_line_insert(TEXT_BUFFER_REC *buffer,
+ LINE_REC *prev)
+{
+ LINE_REC *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 {
+ 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;
+}
+
+LINE_REC *textbuffer_line_last(TEXT_BUFFER_REC *buffer)
+{
+ return buffer->cur_line;
+}
+
+/* returns TRUE if `search' comes on or after `line' in the buffer */
+int textbuffer_line_exists_after(LINE_REC *line, LINE_REC *search)
+{
+ while (line != NULL) {
+ if (line == search)
+ return TRUE;
+ line = line->next;
+ }
+ return FALSE;
+}
+
+void textbuffer_line_add_colors(TEXT_BUFFER_REC *buffer, LINE_REC **line,
+ int fg, int bg, int flags)
+{
+ GString *out = g_string_new(NULL);
+ format_gui_flags(out, &buffer->last_fg, &buffer->last_bg, &buffer->last_flags, fg, bg,
+ flags);
+
+ if (*(out->str) != '\0') {
+ *line =
+ textbuffer_insert(buffer, *line, (unsigned char *) out->str, out->len, NULL);
+ }
+ g_string_free(out, TRUE);
+}
+
+LINE_REC *textbuffer_append(TEXT_BUFFER_REC *buffer,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ return textbuffer_insert(buffer, buffer->cur_line, data, len, info);
+}
+
+LINE_REC *textbuffer_insert(TEXT_BUFFER_REC *buffer, LINE_REC *insert_after,
+ const unsigned char *data, int len,
+ LINE_INFO_REC *info)
+{
+ LINE_REC *line;
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(data != NULL, NULL);
+
+ line = !buffer->last_eol ? insert_after :
+ textbuffer_line_insert(buffer, insert_after);
+
+ if (info != NULL)
+ memcpy(&line->info, info, sizeof(line->info));
+
+ text_chunk_append(buffer, data, len);
+
+ buffer->last_eol = len >= 2 &&
+ data[len-2] == 0 && data[len-1] == LINE_CMD_EOL;
+
+ if (buffer->last_eol) {
+ if (!line->info.format) {
+ line->info.text = g_strdup(buffer->cur_text->str);
+ g_string_truncate(buffer->cur_text, 0);
+ }
+
+ buffer->last_fg = -1;
+ buffer->last_bg = -1;
+ buffer->last_flags = 0;
+ }
+
+ return line;
+}
+
+void textbuffer_remove(TEXT_BUFFER_REC *buffer, LINE_REC *line)
+{
+ g_return_if_fail(buffer != NULL);
+ g_return_if_fail(line != NULL);
+
+ 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 = line->prev;
+ }
+
+ line->prev = line->next = NULL;
+
+ buffer->lines_count--;
+ textbuffer_line_info_free1(&line->info);
+ g_slice_free(LINE_REC, line);
+}
+
+/* Removes all lines from buffer */
+void textbuffer_remove_all_lines(TEXT_BUFFER_REC *buffer)
+{
+ LINE_REC *line;
+
+ g_return_if_fail(buffer != NULL);
+
+ while (buffer->first_line != NULL) {
+ line = buffer->first_line->next;
+ textbuffer_line_info_free1(&buffer->first_line->info);
+ g_slice_free(LINE_REC, buffer->first_line);
+ buffer->first_line = line;
+ }
+ buffer->lines_count = 0;
+
+ buffer->cur_line = NULL;
+ g_string_truncate(buffer->cur_text, 0);
+
+ buffer->last_eol = TRUE;
+}
+
+void textbuffer_line2text(TEXT_BUFFER_REC *buffer, LINE_REC *line, int coloring, GString *str)
+{
+ char *ptr, *tmp;
+
+ g_return_if_fail(line != NULL);
+ g_return_if_fail(str != NULL);
+
+ g_string_truncate(str, 0);
+
+ if ((ptr = textbuffer_line_get_text(buffer, line, coloring == COLORING_RAW)) != NULL) {
+ if (coloring == COLORING_STRIP) {
+ tmp = ptr;
+ ptr = strip_codes(tmp);
+ g_free(tmp);
+ } else if (coloring == COLORING_UNEXPAND) {
+ tmp = ptr;
+ ptr = format_string_unexpand(tmp, 0);
+ g_free(tmp);
+ }
+ g_string_append(str, ptr);
+ g_free(ptr);
+ }
+}
+
+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)
+{
+ Regex *preg;
+ LINE_REC *line, *pre_line;
+ GList *matches;
+ GString *str;
+ int i, match_after, line_matched;
+ char * (*match_func)(const char *, const char *);
+
+ g_return_val_if_fail(buffer != NULL, NULL);
+ g_return_val_if_fail(text != NULL, NULL);
+
+ preg = NULL;
+
+ if (regexp) {
+ preg = i_regex_new(text, case_sensitive ? 0 : G_REGEX_CASELESS, 0, NULL);
+
+ if (preg == NULL)
+ return NULL;
+ }
+
+ matches = NULL; match_after = 0;
+ str = g_string_new(NULL);
+
+ line = startline != NULL ? startline : buffer->first_line;
+
+ if (fullword)
+ match_func = case_sensitive ? strstr_full : stristr_full;
+ else
+ match_func = case_sensitive ? strstr : stristr;
+
+ for (; line != NULL; line = line->next) {
+ line_matched = (line->info.level & level) != 0 &&
+ (line->info.level & nolevel) == 0;
+
+ if (*text != '\0') {
+ textbuffer_line2text(buffer, line, 0, str);
+
+ if (line_matched) {
+ line_matched = regexp ?
+ i_regex_match(preg, str->str, 0, NULL)
+ : match_func(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_nth_data(matches, 0) == pre_line->prev ||
+ g_list_nth_data(matches, 1) == pre_line->prev)
+ break;
+ pre_line = pre_line->prev;
+ }
+
+ for (; pre_line != line; pre_line = pre_line->next)
+ matches = g_list_prepend(matches, pre_line);
+
+ match_after = after;
+ }
+
+ if (line_matched || match_after > 0) {
+ /* matched */
+ matches = g_list_prepend(matches, line);
+
+ if ((!line_matched && --match_after == 0) ||
+ (line_matched && match_after == 0 && before > 0))
+ matches = g_list_prepend(matches, NULL);
+ }
+ }
+
+ matches = g_list_reverse(matches);
+
+ if (preg != NULL)
+ i_regex_unref(preg);
+ g_string_free(str, TRUE);
+ return matches;
+}
+
+void textbuffer_init(void)
+{
+}
+
+void textbuffer_deinit(void)
+{
+}