gtk2-md-editor/insert_recursive_snippet.c

161 lines
7.3 KiB
C

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++;
}
}