From 1371fc91b6c838cff16a0903ea528c95003e425b Mon Sep 17 00:00:00 2001 From: laki Date: Thu, 29 Jan 2026 22:59:52 +0000 Subject: Initial commit for gtk2-md-editor --- insert_recursive_snippet.c | 160 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 insert_recursive_snippet.c (limited to 'insert_recursive_snippet.c') diff --git a/insert_recursive_snippet.c b/insert_recursive_snippet.c new file mode 100644 index 0000000..8c16651 --- /dev/null +++ b/insert_recursive_snippet.c @@ -0,0 +1,160 @@ + +static void insert_recursive(GtkTextBuffer *buffer, GtkTextIter *iter, const char *text, GSList *tags) { + if (!text || !*text) return; + + const char *p = text; + while (*p) { + // Strikethrough ~~ + if (strncmp(p, "~~", 2) == 0) { + const char *end = strstr(p + 2, "~~"); + if (end) { + char *inner = g_strndup(p + 2, end - p - 2); + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "strikethrough"); + GSList *new_tags = g_slist_prepend(g_slist_copy(tags), tag); + insert_recursive(buffer, iter, inner, new_tags); + g_slist_free(new_tags); + g_free(inner); + p = end + 2; continue; + } + } + // Bold+Italic *** + if (strncmp(p, "***", 3) == 0) { + const char *end = strstr(p + 3, "***"); + if (end) { + char *inner = g_strndup(p + 3, end - p - 3); + GtkTextTag *t1 = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "bold"); + GtkTextTag *t2 = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "italic"); + GSList *new_tags = g_slist_prepend(g_slist_prepend(g_slist_copy(tags), t1), t2); + insert_recursive(buffer, iter, inner, new_tags); + g_slist_free(new_tags); + g_free(inner); + p = end + 3; continue; + } + } + // Bold ** or __ + if (strncmp(p, "**", 2) == 0 || strncmp(p, "__", 2) == 0) { + const char *marker = strncmp(p, "**", 2) == 0 ? "**" : "__"; + const char *end = strstr(p + 2, marker); + if (end) { + char *inner = g_strndup(p + 2, end - p - 2); + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "bold"); + GSList *new_tags = g_slist_prepend(g_slist_copy(tags), tag); + insert_recursive(buffer, iter, inner, new_tags); + g_slist_free(new_tags); + g_free(inner); + p = end + 2; continue; + } + } + // Italic * or _ + if (*p == '*' || *p == '_') { + char marker[2] = {*p, 0}; + const char *end = strpbrk(p + 1, marker); + if (end && *end == *p) { + char *inner = g_strndup(p + 1, end - p - 1); + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "italic"); + GSList *new_tags = g_slist_prepend(g_slist_copy(tags), tag); + insert_recursive(buffer, iter, inner, new_tags); + g_slist_free(new_tags); + g_free(inner); + p = end + 1; continue; + } + } + // Code ` + if (*p == '`') { + const char *end = strchr(p + 1, '`'); + if (end) { + char *inner = g_strndup(p + 1, end - p - 1); + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "code"); + GSList *new_tags = g_slist_prepend(g_slist_copy(tags), tag); + // Code is not recursive + GtkTextIter start_ins = *iter; + gtk_text_buffer_insert(buffer, iter, inner, -1); + for (GSList *l = new_tags; l; l = l->next) { + gtk_text_buffer_apply_tag(buffer, (GtkTextTag*)l->data, &start_ins, iter); + } + g_slist_free(new_tags); + g_free(inner); + p = end + 1; continue; + } + } + // Image ![alt](url) + if (strncmp(p, "![", 2) == 0) { + const char *alt_end = strchr(p + 2, ']'); + if (alt_end && alt_end[1] == '(') { + const char *url_end = strchr(alt_end + 2, ')'); + if (url_end) { + char *path_start = (char*)alt_end + 2; + char *path = g_strndup(path_start, url_end - path_start); + if (strncmp(path, "http", 4) == 0) { + // Placeholder for remote + char *msg = g_strdup_printf("[Remote Image: %s]", path); + GtkTextIter start_ins = *iter; + gtk_text_buffer_insert(buffer, iter, msg, -1); + g_free(msg); + } else { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale(path, 600, -1, TRUE, NULL); + if (pixbuf) { + gtk_text_buffer_insert_pixbuf(buffer, iter, pixbuf); + g_object_unref(pixbuf); + } else { + char *msg = g_strdup_printf("[Image not found: %s]", path); + gtk_text_buffer_insert(buffer, iter, msg, -1); + g_free(msg); + } + } + g_free(path); + p = url_end + 1; continue; + } + } + } + // Link [text](url) + if (*p == '[') { + const char *txt_end = strchr(p + 1, ']'); + if (txt_end && txt_end[1] == '(') { + const char *url_end = strchr(txt_end + 2, ')'); + if (url_end) { + char *txt = g_strndup(p + 1, txt_end - p - 1); + char *url = g_strndup(txt_end + 2, url_end - txt_end - 2); + + GtkTextTag *url_tag = gtk_text_buffer_create_tag(buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_object_set_data_full(G_OBJECT(url_tag), "url", g_strdup(url), g_free); + + GSList *new_tags = g_slist_prepend(g_slist_copy(tags), url_tag); + insert_recursive(buffer, iter, txt, new_tags); + + g_slist_free(new_tags); + g_free(txt); g_free(url); + p = url_end + 1; continue; + } + } + } + // Auto-link http://... + if (strncmp(p, "http://", 7) == 0 || strncmp(p, "https://", 8) == 0) { + const char *end = p; + while (*end && !isspace(*end) && *end != ')' && *end != ']' && *end != '>') end++; + char *url = g_strndup(p, end - p); + + GtkTextTag *url_tag = gtk_text_buffer_create_tag(buffer, NULL, "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + g_object_set_data_full(G_OBJECT(url_tag), "url", g_strdup(url), g_free); + + GtkTextIter start_ins = *iter; + gtk_text_buffer_insert(buffer, iter, url, -1); + + // Apply background tags + url tag + for (GSList *l = tags; l; l = l->next) gtk_text_buffer_apply_tag(buffer, (GtkTextTag*)l->data, &start_ins, iter); + gtk_text_buffer_apply_tag(buffer, url_tag, &start_ins, iter); + + g_free(url); + p = end; continue; + } + + // Plain text + GtkTextIter start_ins = *iter; + char buf[2] = {*p, 0}; + gtk_text_buffer_insert(buffer, iter, buf, 1); + for (GSList *l = tags; l; l = l->next) { + gtk_text_buffer_apply_tag(buffer, (GtkTextTag*)l->data, &start_ins, iter); + } + p++; + } +} -- cgit v1.2.3-70-g09d2