From 1371fc91b6c838cff16a0903ea528c95003e425b Mon Sep 17 00:00:00 2001 From: laki Date: Thu, 29 Jan 2026 22:59:52 +0000 Subject: [PATCH] Initial commit for gtk2-md-editor --- Makefile | 28 ++ control | 9 + insert_recursive_snippet.c | 160 +++++++ main.c | 904 +++++++++++++++++++++++++++++++++++++ md_render.c | 881 ++++++++++++++++++++++++++++++++++++ md_render.h | 19 + mdviewer | Bin 0 -> 187832 bytes mdviewer.desktop | 10 + sample.md | 70 +++ 9 files changed, 2081 insertions(+) create mode 100644 Makefile create mode 100644 control create mode 100644 insert_recursive_snippet.c create mode 100644 main.c create mode 100644 md_render.c create mode 100644 md_render.h create mode 100755 mdviewer create mode 100755 mdviewer.desktop create mode 100644 sample.md diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..68fa045 --- /dev/null +++ b/Makefile @@ -0,0 +1,28 @@ +CC = gcc +CFLAGS = `pkg-config --cflags gtk+-2.0` -Wall -Wextra -g +LIBS = `pkg-config --libs gtk+-2.0` + +OBJ = main.o md_render.o +TARGET = mdviewer + +all: $(TARGET) + +$(TARGET): $(OBJ) + $(CC) -o $@ $^ $(LIBS) + +%.o: %.c + $(CC) $(CFLAGS) -c $< -o $@ + +deb: $(TARGET) + mkdir -p pkg/DEBIAN + mkdir -p pkg/usr/bin + mkdir -p pkg/usr/share/applications + mkdir -p pkg/usr/share/icons/hicolor/48x48/apps + cp $(TARGET) pkg/usr/bin/ + cp mdviewer.desktop pkg/usr/share/applications/ + cp control pkg/DEBIAN/ + convert /home/laki/.gemini/antigravity/brain/b3043472-8254-4c94-b2c0-7c8bf8ae13da/mdviewer_icon_1769114997833.png -resize 48x48 pkg/usr/share/icons/hicolor/48x48/apps/mdviewer.png + dpkg-deb --build pkg mdviewer.deb + +clean: + rm -rf $(OBJ) $(TARGET) pkg mdviewer.deb diff --git a/control b/control new file mode 100644 index 0000000..54d7b0b --- /dev/null +++ b/control @@ -0,0 +1,9 @@ +Package: mdviewer +Version: 1.0 +Section: utils +Priority: optional +Architecture: amd64 +Depends: libgtk2.0-0 +Maintainer: laki +Description: Simple GTK2 Markdown Editor and Viewer + A lightweight application to edit and view Markdown files with live preview and dark mode support. 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++; + } +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..403a2e9 --- /dev/null +++ b/main.c @@ -0,0 +1,904 @@ +#include +#include +#include +#include +#include +#include +#include "md_render.h" + +GtkWidget *text_view_editor; +GtkWidget *text_view_viewer; +GtkTextBuffer *buffer_editor; +GtkTextBuffer *buffer_viewer; +void update_line_numbers(); + +GtkWidget *statusbar; +GtkWidget *toc_sidebar; +GtkListStore *toc_store; +char *current_filename = NULL; +int dark_mode = 1; +int line_wrap = 1; +int auto_save_enabled = 0; +int show_toc = 1; +GList *recent_files = NULL; +guint status_context_id; +GtkWidget *line_num_view; +GtkTextBuffer *buffer_line_nums; +GtkWidget *find_dialog = NULL; +GtkWidget *find_entry, *replace_entry; +guint debounce_id = 0; + +typedef struct { + GList *undo_stack; + GList *redo_stack; + int max_depth; +} UndoManager; + +UndoManager *undo_manager; + +void undo_push(const char *text); +void undo_perform(); +void redo_perform(); +void undo_free(); + +void update_preview(); +static void on_editor_changed(GtkTextBuffer *buffer, gpointer user_data); + +void load_config(); +void save_config(); +void add_recent_file(const char *filename); +void update_recent_menu(); +GtkWidget *recent_menu; + +enum { + COL_TEXT, + COL_LINE, + NUM_COLS +}; + +void update_ui_colors() { + GdkColor bg, fg; + if (dark_mode) { + gdk_color_parse("#1e1e1e", &bg); + gdk_color_parse("#ffffff", &fg); + } else { + gdk_color_parse("#ffffff", &bg); + gdk_color_parse("#24292e", &fg); + } + gtk_widget_modify_base(text_view_editor, GTK_STATE_NORMAL, &bg); + gtk_widget_modify_text(text_view_editor, GTK_STATE_NORMAL, &fg); + gtk_widget_modify_base(text_view_viewer, GTK_STATE_NORMAL, &bg); + gtk_widget_modify_text(text_view_viewer, GTK_STATE_NORMAL, &fg); +} + +void save_config() { + char *config_dir = g_build_filename(g_get_user_config_dir(), "mdviewer", NULL); + g_mkdir_with_parents(config_dir, 0755); + char *config_path = g_build_filename(config_dir, "config", NULL); + FILE *f = fopen(config_path, "w"); + if (f) { + fprintf(f, "dark_mode=%d\n", dark_mode); + fprintf(f, "line_wrap=%d\n", line_wrap); + fprintf(f, "auto_save=%d\n", auto_save_enabled); + fprintf(f, "show_toc=%d\n", show_toc); + fclose(f); + } + + char *recent_path = g_build_filename(config_dir, "recent", NULL); + f = fopen(recent_path, "w"); + if (f) { + for (GList *l = recent_files; l; l = l->next) { + fprintf(f, "%s\n", (char*)l->data); + } + fclose(f); + } + g_free(config_path); + g_free(recent_path); + g_free(config_dir); +} + +void load_config() { + char *config_path = g_build_filename(g_get_user_config_dir(), "mdviewer", "config", NULL); + FILE *f = fopen(config_path, "r"); + if (f) { + char line[128]; + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "dark_mode=", 10) == 0) dark_mode = atoi(line + 10); + else if (strncmp(line, "line_wrap=", 10) == 0) line_wrap = atoi(line + 10); + else if (strncmp(line, "auto_save=", 10) == 0) auto_save_enabled = atoi(line + 10); + else if (strncmp(line, "show_toc=", 9) == 0) show_toc = atoi(line + 9); + } + fclose(f); + } + g_free(config_path); + + char *recent_path = g_build_filename(g_get_user_config_dir(), "mdviewer", "recent", NULL); + f = fopen(recent_path, "r"); + if (f) { + char line[1024]; + while (fgets(line, sizeof(line), f)) { + line[strcspn(line, "\n")] = 0; + if (strlen(line) > 0) + recent_files = g_list_append(recent_files, g_strdup(line)); + } + fclose(f); + } + g_free(recent_path); +} + +void load_file(const char *filename); + +static void on_recent_activate(GtkMenuItem *item, gpointer user_data) { + load_file((char*)user_data); +} + +void update_recent_menu() { + if (!recent_menu) return; + GList *children = gtk_container_get_children(GTK_CONTAINER(recent_menu)); + for (GList *l = children; l; l = l->next) gtk_widget_destroy(GTK_WIDGET(l->data)); + g_list_free(children); + + for (GList *l = recent_files; l; l = l->next) { + GtkWidget *item = gtk_menu_item_new_with_label((char*)l->data); + g_signal_connect(item, "activate", G_CALLBACK(on_recent_activate), l->data); + gtk_menu_shell_append(GTK_MENU_SHELL(recent_menu), item); + } + gtk_widget_show_all(recent_menu); +} + +void undo_push(const char *text) { + if (undo_manager->undo_stack && strcmp((char*)undo_manager->undo_stack->data, text) == 0) return; + + undo_manager->undo_stack = g_list_prepend(undo_manager->undo_stack, g_strdup(text)); + if (g_list_length(undo_manager->undo_stack) > (guint)undo_manager->max_depth) { + GList *last = g_list_last(undo_manager->undo_stack); + g_free(last->data); + undo_manager->undo_stack = g_list_remove_link(undo_manager->undo_stack, last); + g_list_free_1(last); + } + + // Clear redo stack on new change + g_list_free_full(undo_manager->redo_stack, g_free); + undo_manager->redo_stack = NULL; +} + +void undo_perform() { + if (!undo_manager->undo_stack || !undo_manager->undo_stack->next) return; + + // Current state to redo + undo_manager->redo_stack = g_list_prepend(undo_manager->redo_stack, undo_manager->undo_stack->data); + undo_manager->undo_stack = g_list_remove_link(undo_manager->undo_stack, undo_manager->undo_stack); + + char *text = (char*)undo_manager->undo_stack->data; + g_signal_handlers_block_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + gtk_text_buffer_set_text(buffer_editor, text, -1); + g_signal_handlers_unblock_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + update_preview(); +} + +void redo_perform() { + if (!undo_manager->redo_stack) return; + + char *text = (char*)undo_manager->redo_stack->data; + undo_manager->undo_stack = g_list_prepend(undo_manager->undo_stack, text); + undo_manager->redo_stack = g_list_remove_link(undo_manager->redo_stack, undo_manager->redo_stack); + + g_signal_handlers_block_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + gtk_text_buffer_set_text(buffer_editor, text, -1); + g_signal_handlers_unblock_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + update_preview(); +} + +void undo_free() { + g_list_free_full(undo_manager->undo_stack, g_free); + g_list_free_full(undo_manager->redo_stack, g_free); + g_free(undo_manager); +} + +void add_recent_file(const char *filename) { + for (GList *l = recent_files; l; l = l->next) { + if (strcmp((char*)l->data, filename) == 0) { + recent_files = g_list_remove_link(recent_files, l); + g_free(l->data); + g_list_free_1(l); + break; + } + } + recent_files = g_list_prepend(recent_files, g_strdup(filename)); + if (g_list_length(recent_files) > 10) { + GList *last = g_list_last(recent_files); + g_free(last->data); + recent_files = g_list_remove_link(recent_files, last); + g_list_free_1(last); + } + update_recent_menu(); +} + +void update_toc() { + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + + GList *headers = md_get_headers(text); + gtk_list_store_clear(toc_store); + + for (GList *l = headers; l != NULL; l = l->next) { + MdHeader *h = (MdHeader*)l->data; + GtkTreeIter iter; + gtk_list_store_append(toc_store, &iter); + char *indented = g_strdup_printf("%*s%s", (h->level - 1) * 2, "", h->text); + gtk_list_store_set(toc_store, &iter, COL_TEXT, indented, COL_LINE, h->line, -1); + g_free(indented); + } + + md_free_headers(headers); + g_free(text); +} + +static void on_toc_row_activated(GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data) { + GtkTreeIter iter; + if (gtk_tree_model_get_iter(GTK_TREE_MODEL(toc_store), &iter, path)) { + int line; + gtk_tree_model_get(GTK_TREE_MODEL(toc_store), &iter, COL_LINE, &line, -1); + + GtkTextIter text_iter; + gtk_text_buffer_get_iter_at_line(buffer_editor, &text_iter, line); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view_editor), &text_iter, 0.0, TRUE, 0.0, 0.0); + + gtk_text_buffer_get_iter_at_line(buffer_viewer, &text_iter, line); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view_viewer), &text_iter, 0.0, TRUE, 0.0, 0.0); + } +} + +static gboolean on_viewer_motion_notify(GtkWidget *widget, GdkEventMotion *event, gpointer user_data) { + GtkTextIter iter; + int x, y; + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT, event->x, event->y, &x, &y); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y); + + GSList *tags = gtk_text_iter_get_tags(&iter); + gboolean is_link = FALSE; + for (GSList *l = tags; l; l = l->next) { + GtkTextTag *tag = (GtkTextTag*)l->data; + if (g_object_get_data(G_OBJECT(tag), "url")) { + is_link = TRUE; + break; + } + } + g_slist_free(tags); + + GdkCursor *cursor = is_link ? gdk_cursor_new(GDK_HAND2) : NULL; + gdk_window_set_cursor(gtk_text_view_get_window(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT), cursor); + if (cursor) gdk_cursor_unref(cursor); + + return FALSE; +} + +static gboolean on_viewer_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { + if (event->type == GDK_BUTTON_PRESS) { + GdkEventButton *bevent = (GdkEventButton *)event; + GtkTextIter iter; + int x, y; + gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(widget), GTK_TEXT_WINDOW_TEXT, bevent->x, bevent->y, &x, &y); + gtk_text_view_get_iter_at_location(GTK_TEXT_VIEW(widget), &iter, x, y); + + GSList *tags = gtk_text_iter_get_tags(&iter); + for (GSList *l = tags; l; l = l->next) { + GtkTextTag *tag = l->data; + char *url = g_object_get_data(G_OBJECT(tag), "url"); + if (url) { + char *cmd = g_strdup_printf("xdg-open '%s' &", url); + system(cmd); + g_free(cmd); + g_slist_free(tags); + return TRUE; + } + } + g_slist_free(tags); + } + return FALSE; +} + +void update_status_bar() { + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + + int chars = g_utf8_strlen(text, -1); + int words = 0; + int in_word = 0; + + for (char *p = text; *p; p = g_utf8_next_char(p)) { + gunichar c = g_utf8_get_char(p); + if (g_unichar_isspace(c)) { + in_word = 0; + } else if (!in_word) { + in_word = 1; + words++; + } + } + + char status_msg[256]; + const char *display_name = current_filename ? g_path_get_basename(current_filename) : "New File"; + snprintf(status_msg, sizeof(status_msg), "%s | Words: %d | Characters: %d", display_name, words, chars); + if (current_filename) g_free((char*)display_name); + + gtk_statusbar_pop(GTK_STATUSBAR(statusbar), status_context_id); + gtk_statusbar_push(GTK_STATUSBAR(statusbar), status_context_id, status_msg); + + g_free(text); +} + +static void on_find_next(GtkWidget *widget, gpointer data) { + const char *text = gtk_entry_get_text(GTK_ENTRY(find_entry)); + GtkTextIter start, match_start, match_end; + + gtk_text_buffer_get_iter_at_mark(buffer_editor, &start, gtk_text_buffer_get_insert(buffer_editor)); + + if (gtk_text_iter_forward_search(&start, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &match_start, &match_end, NULL)) { + gtk_text_buffer_select_range(buffer_editor, &match_start, &match_end); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view_editor), &match_start, 0.0, FALSE, 0.0, 0.0); + } else { + // Wrap search + gtk_text_buffer_get_start_iter(buffer_editor, &start); + if (gtk_text_iter_forward_search(&start, text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &match_start, &match_end, NULL)) { + gtk_text_buffer_select_range(buffer_editor, &match_start, &match_end); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view_editor), &match_start, 0.0, FALSE, 0.0, 0.0); + } + } +} + +static void on_replace(GtkWidget *widget, gpointer data) { + GtkTextIter start, end; + if (gtk_text_buffer_get_selection_bounds(buffer_editor, &start, &end)) { + const char *replacement = gtk_entry_get_text(GTK_ENTRY(replace_entry)); + gtk_text_buffer_delete(buffer_editor, &start, &end); + gtk_text_buffer_insert(buffer_editor, &start, replacement, -1); + } + on_find_next(NULL, NULL); +} + +static void on_replace_all(GtkWidget *widget, gpointer data) { + const char *find_text = gtk_entry_get_text(GTK_ENTRY(find_entry)); + const char *rep_text = gtk_entry_get_text(GTK_ENTRY(replace_entry)); + GtkTextIter iter; + gtk_text_buffer_get_start_iter(buffer_editor, &iter); + + int count = 0; + GtkTextIter m_start, m_end; + while (gtk_text_iter_forward_search(&iter, find_text, GTK_TEXT_SEARCH_VISIBLE_ONLY, &m_start, &m_end, NULL)) { + gtk_text_buffer_delete(buffer_editor, &m_start, &m_end); + gtk_text_buffer_insert(buffer_editor, &m_start, rep_text, -1); + iter = m_start; + gtk_text_iter_forward_chars(&iter, strlen(rep_text)); + count++; + } +} + +static void on_find_activate(GtkMenuItem *item, gpointer user_data) { + if (find_dialog) { + gtk_window_present(GTK_WINDOW(find_dialog)); + return; + } + + find_dialog = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(find_dialog), "Find and Replace"); + gtk_window_set_transient_for(GTK_WINDOW(find_dialog), GTK_WINDOW(user_data)); + gtk_window_set_destroy_with_parent(GTK_WINDOW(find_dialog), TRUE); + g_signal_connect(find_dialog, "destroy", G_CALLBACK(gtk_widget_destroyed), &find_dialog); + + GtkWidget *vbox = gtk_vbox_new(FALSE, 5); + gtk_container_set_border_width(GTK_CONTAINER(vbox), 10); + gtk_container_add(GTK_CONTAINER(find_dialog), vbox); + + GtkWidget *table = gtk_table_new(2, 2, FALSE); + gtk_table_set_row_spacings(GTK_TABLE(table), 5); + gtk_table_set_col_spacings(GTK_TABLE(table), 5); + gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0); + + gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new("Find:"), 0, 1, 0, 1); + find_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), find_entry, 1, 2, 0, 1); + + gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new("Replace with:"), 0, 1, 1, 2); + replace_entry = gtk_entry_new(); + gtk_table_attach_defaults(GTK_TABLE(table), replace_entry, 1, 2, 1, 2); + + GtkWidget *bbox = gtk_hbutton_box_new(); + gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); + gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); + + GtkWidget *btn_find = gtk_button_new_with_label("Find Next"); + g_signal_connect(btn_find, "clicked", G_CALLBACK(on_find_next), NULL); + gtk_container_add(GTK_CONTAINER(bbox), btn_find); + + GtkWidget *btn_replace = gtk_button_new_with_label("Replace"); + g_signal_connect(btn_replace, "clicked", G_CALLBACK(on_replace), NULL); + gtk_container_add(GTK_CONTAINER(bbox), btn_replace); + + GtkWidget *btn_all = gtk_button_new_with_label("Replace All"); + g_signal_connect(btn_all, "clicked", G_CALLBACK(on_replace_all), NULL); + gtk_container_add(GTK_CONTAINER(bbox), btn_all); + + gtk_widget_show_all(find_dialog); +} + +void save_file(const char *filename); + +void update_line_numbers() { + int lines = gtk_text_buffer_get_line_count(buffer_editor); + GString *nums = g_string_new(""); + for (int i = 1; i <= lines; i++) { + g_string_append_printf(nums, "%d\n", i); + } + gtk_text_buffer_set_text(buffer_line_nums, nums->str, -1); + g_string_free(nums, TRUE); +} + +void update_preview() { + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + + // Highlight editor + md_render_highlight_editor(buffer_editor, dark_mode); + + // Render viewer + md_render_to_buffer(buffer_viewer, text, dark_mode); + + // Update Table of Contents + update_toc(); + + g_free(text); +} + +static void on_wrap_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) { + line_wrap = gtk_check_menu_item_get_active(checkmenuitem); + GtkWrapMode mode = line_wrap ? GTK_WRAP_WORD : GTK_WRAP_NONE; + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view_editor), mode); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view_viewer), mode); +} + +static void on_dark_mode_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) { + dark_mode = gtk_check_menu_item_get_active(checkmenuitem); + update_ui_colors(); + update_preview(); +} + +static void on_toc_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) { + show_toc = gtk_check_menu_item_get_active(checkmenuitem); + if (show_toc) { + gtk_widget_show(toc_sidebar); + } else { + gtk_widget_hide(toc_sidebar); + } +} + +static void on_auto_save_toggled(GtkCheckMenuItem *checkmenuitem, gpointer user_data) { + auto_save_enabled = gtk_check_menu_item_get_active(checkmenuitem); +} + +static void insert_markdown_marker(const char *marker_start, const char *marker_end) { + GtkTextIter start, end; + if (gtk_text_buffer_get_selection_bounds(buffer_editor, &start, &end)) { + gtk_text_buffer_begin_user_action(buffer_editor); + gtk_text_buffer_insert(buffer_editor, &end, marker_end, -1); + gtk_text_buffer_insert(buffer_editor, &start, marker_start, -1); + gtk_text_buffer_end_user_action(buffer_editor); + } else { + gtk_text_buffer_insert_at_cursor(buffer_editor, marker_start, -1); + gtk_text_buffer_insert_at_cursor(buffer_editor, marker_end, -1); + GtkTextIter cursor; + gtk_text_buffer_get_iter_at_mark(buffer_editor, &cursor, gtk_text_buffer_get_insert(buffer_editor)); + gtk_text_iter_backward_chars(&cursor, strlen(marker_end)); + gtk_text_buffer_place_cursor(buffer_editor, &cursor); + } +} + +static void on_export_activate(GtkMenuItem *item, gpointer user_data) { + GtkWidget *dialog = gtk_file_chooser_dialog_new("Export to HTML", GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + char *html = md_to_html(text); + g_file_set_contents(filename, html, -1, NULL); + g_free(text); + g_free(html); + g_free(filename); + } + gtk_widget_destroy(dialog); +} + +static void on_print_draw_page(GtkPrintOperation *operation, GtkPrintContext *context, int page_nr, gpointer user_data) { + cairo_t *cr = gtk_print_context_get_cairo_context(context); + PangoLayout *layout = gtk_print_context_create_pango_layout(context); + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_viewer, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_viewer, &start, &end, FALSE); + pango_layout_set_text(layout, text, -1); + pango_layout_set_width(layout, gtk_print_context_get_width(context) * PANGO_SCALE); + pango_cairo_show_layout(cr, layout); + g_free(text); + g_object_unref(layout); +} + +static void on_print_activate(GtkMenuItem *item, gpointer user_data) { + GtkPrintOperation *print = gtk_print_operation_new(); + gtk_print_operation_set_n_pages(print, 1); + g_signal_connect(print, "draw-page", G_CALLBACK(on_print_draw_page), NULL); + gtk_print_operation_run(print, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, GTK_WINDOW(user_data), NULL); + g_object_unref(print); +} + +static gboolean debounce_cb(gpointer user_data) { + update_preview(); + GtkTextIter start; + gtk_text_buffer_get_start_iter(buffer_viewer, &start); + gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(text_view_viewer), &start, 0.0, TRUE, 0.0, 0.0); + debounce_id = 0; + return FALSE; +} + +static void on_new_activate(GtkMenuItem *item, gpointer user_data) { + g_signal_handlers_block_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + gtk_text_buffer_set_text(buffer_editor, "", 0); + g_signal_handlers_unblock_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + + if (current_filename) { + g_free(current_filename); + current_filename = NULL; + } + + g_list_free_full(undo_manager->undo_stack, g_free); + g_list_free_full(undo_manager->redo_stack, g_free); + undo_manager->undo_stack = NULL; + undo_manager->redo_stack = NULL; + undo_push(""); + + update_preview(); +} + +static void on_editor_changed(GtkTextBuffer *buffer, gpointer user_data) { + if (debounce_id) g_source_remove(debounce_id); + debounce_id = g_timeout_add(300, debounce_cb, NULL); + + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer, &start, &end); + char *text = gtk_text_buffer_get_text(buffer, &start, &end, FALSE); + undo_push(text); + g_free(text); + + update_status_bar(); + if (auto_save_enabled && current_filename) { + save_file(current_filename); + } +} + +void save_file(const char *filename) { + if (!filename) return; + + FILE *f = fopen(filename, "w"); + if (!f) { + perror("fopen"); + return; + } + + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + fputs(text, f); + g_free(text); + fclose(f); +} + +static void on_save_activate(GtkMenuItem *item, gpointer user_data) { + if (current_filename) { + save_file(current_filename); + } else { + GtkWidget *dialog; + dialog = gtk_file_chooser_dialog_new("Save File", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_SAVE, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, + NULL); + gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + save_file(filename); + if (current_filename) g_free(current_filename); + current_filename = g_strdup(filename); + g_free(filename); + add_recent_file(current_filename); + } + gtk_widget_destroy(dialog); + } +} + +static void on_open_activate(GtkMenuItem *item, gpointer user_data) { + GtkWidget *dialog; + dialog = gtk_file_chooser_dialog_new("Open File", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_OPEN, + GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, + GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, + NULL); + + if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { + char *filename; + filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); + load_file(filename); + g_free(filename); + } + gtk_widget_destroy(dialog); +} + +void load_file(const char *filename) { + FILE *f = fopen(filename, "r"); + if (!f) { + perror("fopen"); + return; + } + + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + + char *content = malloc(size + 1); + fread(content, 1, size, f); + content[size] = '\0'; + fclose(f); + + if (current_filename) g_free(current_filename); + current_filename = g_strdup(filename); + add_recent_file(filename); + + g_signal_handlers_block_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + gtk_text_buffer_set_text(buffer_editor, content, -1); + g_signal_handlers_unblock_by_func(buffer_editor, G_CALLBACK(on_editor_changed), NULL); + + update_preview(); + update_line_numbers(); + update_status_bar(); + free(content); +} + +static void on_bold_shortcut() { insert_markdown_marker("**", "**"); } +static void on_italic_shortcut() { insert_markdown_marker("*", "*"); } +static void on_link_shortcut() { insert_markdown_marker("[", "](url)"); } +static void on_list_shortcut() { insert_markdown_marker("- ", ""); } +static void on_image_shortcut() { insert_markdown_marker("![Alt text](", ")"); } + +int main(int argc, char *argv[]) { + gtk_init(&argc, &argv); + load_config(); + + undo_manager = g_malloc0(sizeof(UndoManager)); + undo_manager->max_depth = 50; + + GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(window), "GTK2 Markdown Editor & Viewer"); + gtk_window_set_default_size(GTK_WINDOW(window), 1000, 700); + g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + + GtkAccelGroup *accel_group = gtk_accel_group_new(); + gtk_window_add_accel_group(GTK_WINDOW(window), accel_group); + + GtkWidget *vbox = gtk_vbox_new(FALSE, 0); + gtk_container_add(GTK_CONTAINER(window), vbox); + + // Menu + GtkWidget *menubar = gtk_menu_bar_new(); + + // File Menu + GtkWidget *file_menu_item = gtk_menu_item_new_with_label("File"); + GtkWidget *file_menu = gtk_menu_new(); + GtkWidget *new_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL); + GtkWidget *open_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL); + GtkWidget *recent_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_OPEN, NULL); + gtk_menu_item_set_label(GTK_MENU_ITEM(recent_item), "Open Recent"); + recent_menu = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu); + update_recent_menu(); + + GtkWidget *save_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_SAVE, NULL); + GtkWidget *export_item = gtk_menu_item_new_with_label("Export to HTML"); + GtkWidget *print_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_PRINT, NULL); + GtkWidget *auto_save_item = gtk_check_menu_item_new_with_label("Auto-Save"); + GtkWidget *sep = gtk_separator_menu_item_new(); + GtkWidget *quit_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT, NULL); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_menu_item), file_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), new_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), open_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), recent_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), save_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), export_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), print_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), auto_save_item); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), sep); + gtk_menu_shell_append(GTK_MENU_SHELL(file_menu), quit_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file_menu_item); + + // Edit Menu + GtkWidget *edit_menu_item = gtk_menu_item_new_with_label("Edit"); + GtkWidget *edit_menu = gtk_menu_new(); + GtkWidget *undo_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_UNDO, NULL); + GtkWidget *redo_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_REDO, NULL); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(edit_menu_item), edit_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), undo_item); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), redo_item); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), gtk_separator_menu_item_new()); + + GtkWidget *find_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_FIND, NULL); + g_signal_connect(find_item, "activate", G_CALLBACK(on_find_activate), window); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), find_item); + + GtkWidget *rep_item = gtk_image_menu_item_new_from_stock(GTK_STOCK_FIND_AND_REPLACE, NULL); + g_signal_connect(rep_item, "activate", G_CALLBACK(on_find_activate), window); + gtk_menu_shell_append(GTK_MENU_SHELL(edit_menu), rep_item); + + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), edit_menu_item); + + // View Menu + GtkWidget *view_menu_item = gtk_menu_item_new_with_label("View"); + GtkWidget *view_menu = gtk_menu_new(); + GtkWidget *wrap_item = gtk_check_menu_item_new_with_label("Line Wrap"); + GtkWidget *dark_item = gtk_check_menu_item_new_with_label("Dark Mode"); + GtkWidget *toc_toggle_item = gtk_check_menu_item_new_with_label("Show Table of Contents"); + + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(wrap_item), line_wrap); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(dark_item), dark_mode); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(toc_toggle_item), show_toc); + gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(auto_save_item), auto_save_enabled); + + gtk_menu_item_set_submenu(GTK_MENU_ITEM(view_menu_item), view_menu); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), wrap_item); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), dark_item); + gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), toc_toggle_item); + gtk_menu_shell_append(GTK_MENU_SHELL(menubar), view_menu_item); + + gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); + + g_signal_connect(new_item, "activate", G_CALLBACK(on_new_activate), window); + g_signal_connect(open_item, "activate", G_CALLBACK(on_open_activate), window); + g_signal_connect(save_item, "activate", G_CALLBACK(on_save_activate), window); + g_signal_connect(export_item, "activate", G_CALLBACK(on_export_activate), window); + g_signal_connect(print_item, "activate", G_CALLBACK(on_print_activate), window); + g_signal_connect(auto_save_item, "activate", G_CALLBACK(on_auto_save_toggled), NULL); + g_signal_connect(quit_item, "activate", G_CALLBACK(gtk_main_quit), NULL); + + g_signal_connect(undo_item, "activate", G_CALLBACK(undo_perform), NULL); + g_signal_connect(redo_item, "activate", G_CALLBACK(redo_perform), NULL); + g_signal_connect(wrap_item, "toggled", G_CALLBACK(on_wrap_toggled), NULL); + g_signal_connect(dark_item, "toggled", G_CALLBACK(on_dark_mode_toggled), NULL); + g_signal_connect(toc_toggle_item, "toggled", G_CALLBACK(on_toc_toggled), NULL); + + // Shortcuts + GClosure *new_closure = g_cclosure_new(G_CALLBACK(on_new_activate), window, NULL); + gtk_accel_group_connect(accel_group, GDK_n, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, new_closure); + GClosure *bold_closure = g_cclosure_new(G_CALLBACK(on_bold_shortcut), NULL, NULL); + gtk_accel_group_connect(accel_group, GDK_b, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, bold_closure); + GClosure *italic_closure = g_cclosure_new(G_CALLBACK(on_italic_shortcut), NULL, NULL); + gtk_accel_group_connect(accel_group, GDK_i, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, italic_closure); + GClosure *link_closure = g_cclosure_new(G_CALLBACK(on_link_shortcut), NULL, NULL); + gtk_accel_group_connect(accel_group, GDK_k, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, link_closure); + + GClosure *undo_closure = g_cclosure_new(G_CALLBACK(undo_perform), NULL, NULL); + gtk_accel_group_connect(accel_group, GDK_z, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, undo_closure); + GClosure *redo_closure = g_cclosure_new(G_CALLBACK(redo_perform), NULL, NULL); + gtk_accel_group_connect(accel_group, GDK_y, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, redo_closure); + + GClosure *find_closure = g_cclosure_new(G_CALLBACK(on_find_activate), window, NULL); + gtk_accel_group_connect(accel_group, GDK_f, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, find_closure); + GClosure *rep_closure = g_cclosure_new(G_CALLBACK(on_find_activate), window, NULL); + gtk_accel_group_connect(accel_group, GDK_h, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE, rep_closure); + + // Outer Paned (ToC | Contents) + GtkWidget *outer_paned = gtk_hpaned_new(); + gtk_box_pack_start(GTK_BOX(vbox), outer_paned, TRUE, TRUE, 0); + + // ToC Sidebar (Left) + toc_sidebar = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(toc_sidebar), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_paned_add1(GTK_PANED(outer_paned), toc_sidebar); + + toc_store = gtk_list_store_new(NUM_COLS, G_TYPE_STRING, G_TYPE_INT); + GtkWidget *toc_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(toc_store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(toc_list), FALSE); + GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); + GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("Header", renderer, "text", COL_TEXT, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(toc_list), column); + gtk_container_add(GTK_CONTAINER(toc_sidebar), toc_list); + g_signal_connect(toc_list, "row-activated", G_CALLBACK(on_toc_row_activated), NULL); + + // Inner Paned (Editor | Viewer) + GtkWidget *inner_paned = gtk_hpaned_new(); + gtk_paned_add2(GTK_PANED(outer_paned), inner_paned); + + // Editor (Mid) + GtkWidget *sw_editor = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw_editor), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_paned_add1(GTK_PANED(inner_paned), sw_editor); + + GtkWidget *editor_hbox = gtk_hbox_new(FALSE, 0); + gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(sw_editor), editor_hbox); + + line_num_view = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(line_num_view), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(line_num_view), FALSE); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(line_num_view), 5); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(line_num_view), 5); + gtk_widget_set_sensitive(line_num_view, FALSE); + buffer_line_nums = gtk_text_view_get_buffer(GTK_TEXT_VIEW(line_num_view)); + gtk_box_pack_start(GTK_BOX(editor_hbox), line_num_view, FALSE, FALSE, 0); + + text_view_editor = gtk_text_view_new(); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view_editor), line_wrap ? GTK_WRAP_WORD : GTK_WRAP_NONE); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_editor), 10); + gtk_box_pack_start(GTK_BOX(editor_hbox), text_view_editor, TRUE, TRUE, 0); + buffer_editor = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_editor)); + g_signal_connect(buffer_editor, "changed", G_CALLBACK(on_editor_changed), NULL); + + // Viewer (Right) + GtkWidget *sw_viewer = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw_viewer), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_paned_add2(GTK_PANED(inner_paned), sw_viewer); + + text_view_viewer = gtk_text_view_new(); + gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view_viewer), FALSE); + gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(text_view_viewer), FALSE); + gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view_viewer), line_wrap ? GTK_WRAP_WORD : GTK_WRAP_NONE); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view_viewer), 20); + gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_view_viewer), 20); + gtk_container_add(GTK_CONTAINER(sw_viewer), text_view_viewer); + buffer_viewer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view_viewer)); + gtk_widget_add_events(text_view_viewer, GDK_POINTER_MOTION_MASK); + g_signal_connect(text_view_viewer, "button-press-event", G_CALLBACK(on_viewer_event), NULL); + g_signal_connect(text_view_viewer, "motion-notify-event", G_CALLBACK(on_viewer_motion_notify), NULL); + + // Status Bar + statusbar = gtk_statusbar_new(); + gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, FALSE, 0); + status_context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar), "main"); + + // Initialize Markdown tags + md_render_init_tags(buffer_editor); + md_render_init_tags(buffer_viewer); + + update_ui_colors(); + gtk_paned_set_position(GTK_PANED(outer_paned), 200); + gtk_paned_set_position(GTK_PANED(inner_paned), 400); + + if (!show_toc) gtk_widget_hide(toc_sidebar); + + if (argc > 1) { + load_file(argv[1]); + } else { + gtk_text_buffer_set_text(buffer_editor, "# Welcome\n\nType here to see live preview on the right.", -1); + } + + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer_editor, &start, &end); + char *initial_text = gtk_text_buffer_get_text(buffer_editor, &start, &end, FALSE); + undo_push(initial_text); + g_free(initial_text); + + gtk_widget_show_all(window); + if (!show_toc) gtk_widget_hide(toc_sidebar); + update_status_bar(); + gtk_main(); + + save_config(); + if (undo_manager) undo_free(); + if (current_filename) g_free(current_filename); + + return 0; +} diff --git a/md_render.c b/md_render.c new file mode 100644 index 0000000..476a3e4 --- /dev/null +++ b/md_render.c @@ -0,0 +1,881 @@ +#include +#include +#include +#include +#include +#include "md_render.h" + +typedef struct { + char **cells; + int count; +} TableRow; + +static void free_table_row(gpointer data) { + TableRow *row = (TableRow*)data; + if (row) { + for (int i = 0; i < row->count; i++) g_free(row->cells[i]); + g_free(row->cells); + g_free(row); + } +} + +static TableRow* parse_table_row(const char *line) { + char *copy = g_strdup(line); + char *start = copy; + while (*start == ' ' || *start == '\t') start++; + if (*start == '|') start++; + + char *end = start + strlen(start) - 1; + while (end > start && (*end == ' ' || *end == '|' || *end == '\r' || *end == '\n' || *end == '\t')) { + *end = '\0'; + end--; + } + + char **parts = g_strsplit(start, "|", -1); + int count = 0; + while (parts[count]) count++; + + TableRow *row = g_malloc0(sizeof(TableRow)); + row->cells = g_malloc0(sizeof(char*) * count); + row->count = count; + for (int i = 0; i < count; i++) { + row->cells[i] = g_strstrip(g_strdup(parts[i])); + } + + g_strfreev(parts); + g_free(copy); + return row; +} + + +static char* process_inline_html(const char *text) { + if (!text) return g_strdup(""); + GString *s = g_string_new(""); + const char *p = text; + while (*p) { + if (strncmp(p, "~~", 2) == 0) { + const char *end = strstr(p + 2, "~~"); + if (end) { + char *inner = g_strndup(p + 2, end - p - 2); + char *processed = process_inline_html(inner); + g_string_append_printf(s, "%s", processed); + g_free(inner); g_free(processed); + p = end + 2; continue; + } + } + if (strncmp(p, "***", 3) == 0) { + const char *end = strstr(p + 3, "***"); + if (end) { + char *inner = g_strndup(p + 3, end - p - 3); + char *processed = process_inline_html(inner); + g_string_append_printf(s, "%s", processed); + g_free(inner); g_free(processed); + p = end + 3; continue; + } + } + 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); + char *processed = process_inline_html(inner); + g_string_append_printf(s, "%s", processed); + g_free(inner); g_free(processed); + p = end + 2; continue; + } + } + 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); + char *processed = process_inline_html(inner); + g_string_append_printf(s, "%s", processed); + g_free(inner); g_free(processed); + p = end + 1; continue; + } + } + if (*p == '`') { + const char *end = strchr(p + 1, '`'); + if (end) { + char *inner = g_strndup(p + 1, end - p - 1); + g_string_append_printf(s, "%s", inner); + p = end + 1; g_free(inner); continue; + } + } + 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 *alt = g_strndup(p + 2, alt_end - p - 2); + char *url = g_strndup(alt_end + 2, url_end - alt_end - 2); + g_string_append_printf(s, "\"%s\"", url, alt); + g_free(alt); g_free(url); + p = url_end + 1; continue; + } + } + } + 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); + char *processed_txt = process_inline_html(txt); + g_string_append_printf(s, "%s", url, processed_txt); + g_free(txt); g_free(url); g_free(processed_txt); + p = url_end + 1; continue; + } + } + } + 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); + g_string_append_printf(s, "%s", url, url); + g_free(url); + p = end; continue; + } + g_string_append_c(s, *p); + p++; + } + return g_string_free(s, FALSE); +} + +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 - use marks to preserve position + GtkTextMark *start_mark = gtk_text_buffer_create_mark(buffer, NULL, iter, TRUE); + gtk_text_buffer_insert(buffer, iter, inner, -1); + GtkTextIter start_ins; + gtk_text_buffer_get_iter_at_mark(buffer, &start_ins, start_mark); + for (GSList *l = new_tags; l; l = l->next) { + gtk_text_buffer_apply_tag(buffer, (GtkTextTag*)l->data, &start_ins, iter); + } + gtk_text_buffer_delete_mark(buffer, start_mark); + 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); + 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); + + GtkTextMark *start_mark = gtk_text_buffer_create_mark(buffer, NULL, iter, TRUE); + gtk_text_buffer_insert(buffer, iter, url, -1); + GtkTextIter start_ins; + gtk_text_buffer_get_iter_at_mark(buffer, &start_ins, start_mark); + + // 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); + + gtk_text_buffer_delete_mark(buffer, start_mark); + g_free(url); + p = end; continue; + } + + // Plain text + GtkTextMark *start_mark = gtk_text_buffer_create_mark(buffer, NULL, iter, TRUE); + char buf[2] = {*p, 0}; + gtk_text_buffer_insert(buffer, iter, buf, 1); + GtkTextIter start_ins; + gtk_text_buffer_get_iter_at_mark(buffer, &start_ins, start_mark); + for (GSList *l = tags; l; l = l->next) { + gtk_text_buffer_apply_tag(buffer, (GtkTextTag*)l->data, &start_ins, iter); + } + gtk_text_buffer_delete_mark(buffer, start_mark); + p++; + } +} + +void md_render_init_tags(GtkTextBuffer *buffer) { + gtk_text_buffer_create_tag(buffer, "h1", "weight", PANGO_WEIGHT_BOLD, "size", 24 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "h2", "weight", PANGO_WEIGHT_BOLD, "size", 20 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "h3", "weight", PANGO_WEIGHT_BOLD, "size", 16 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "h4", "weight", PANGO_WEIGHT_BOLD, "size", 14 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "h5", "weight", PANGO_WEIGHT_BOLD, "size", 12 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "h6", "weight", PANGO_WEIGHT_BOLD, "size", 10 * PANGO_SCALE, NULL); + gtk_text_buffer_create_tag(buffer, "bold", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(buffer, "italic", "style", PANGO_STYLE_ITALIC, NULL); + gtk_text_buffer_create_tag(buffer, "bold_italic", "weight", PANGO_WEIGHT_BOLD, "style", PANGO_STYLE_ITALIC, NULL); + gtk_text_buffer_create_tag(buffer, "code", "family", "monospace", NULL); + gtk_text_buffer_create_tag(buffer, "checkbox_off", "foreground", "red", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(buffer, "checkbox_on", "foreground", "green", "weight", PANGO_WEIGHT_BOLD, NULL); + gtk_text_buffer_create_tag(buffer, "code_block", "family", "monospace", + "pixels-above-lines", 8, "pixels-below-lines", 8, + "left-margin", 15, "right-margin", 15, NULL); + gtk_text_buffer_create_tag(buffer, "list", "left-margin", 20, NULL); + gtk_text_buffer_create_tag(buffer, "blockquote", "left-margin", 30, "style", PANGO_STYLE_ITALIC, NULL); + gtk_text_buffer_create_tag(buffer, "hr", "underline", PANGO_UNDERLINE_SINGLE, "pixels-above-lines", 10, "pixels-below-lines", 10, NULL); + gtk_text_buffer_create_tag(buffer, "table", "family", "monospace", "left-margin", 10, NULL); + gtk_text_buffer_create_tag(buffer, "link", "foreground", "blue", "underline", PANGO_UNDERLINE_SINGLE, NULL); + gtk_text_buffer_create_tag(buffer, "strikethrough", "strikethrough", TRUE, NULL); + gtk_text_buffer_create_tag(buffer, "normal_text", NULL); +} + +static void update_tag_colors(GtkTextBuffer *buffer, int theme) { + const char *h1_fg = theme ? "#ffffff" : "#1a1a1a"; + const char *h2_fg = theme ? "#f0f0f0" : "#2d2d2d"; + const char *h3_fg = theme ? "#e0e0e0" : "#444444"; + const char *h4_fg = theme ? "#cccccc" : "#666666"; + const char *h5_fg = theme ? "#bbbbbb" : "#777777"; + const char *h6_fg = theme ? "#aaaaaa" : "#888888"; + const char *text_fg = theme ? "#ffffff" : "#24292e"; + const char *bold_fg = theme ? "#ffffff" : "#000000"; + const char *italic_fg = theme ? "#cccccc" : "#555555"; + const char *bq_fg = theme ? "#95a5a6" : "#7f8c8d"; + const char *code_bg = theme ? "#2d3436" : "#f0f0f0"; + const char *code_fg = theme ? "#fab1a0" : "#d73a49"; + const char *cb_bg = theme ? "#2d3436" : "#f6f8fa"; + const char *cb_fg = theme ? "#dfe6e9" : "#24292e"; + const char *cb_on_fg = theme ? "#55efc4" : "#27ae60"; + const char *cb_off_fg = theme ? "#ff7675" : "#d63031"; + const char *link_fg = theme ? "#a5d6ff" : "#0984e3"; + const char *hr_fg = theme ? "#636e72" : "#dfe6e9"; + + GtkTextTagTable *table = gtk_text_buffer_get_tag_table(buffer); + + g_object_set(gtk_text_tag_table_lookup(table, "checkbox_on"), "foreground", cb_on_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "checkbox_off"), "foreground", cb_off_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h1"), "foreground", h1_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h2"), "foreground", h2_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h3"), "foreground", h3_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h4"), "foreground", h4_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h5"), "foreground", h5_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "h6"), "foreground", h6_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "bold"), "foreground", bold_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "italic"), "foreground", italic_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "bold_italic"), "foreground", bold_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "blockquote"), "foreground", bq_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "code"), "background", code_bg, "foreground", code_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "code_block"), "background", cb_bg, "foreground", cb_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "list"), "foreground", text_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "link"), "foreground", link_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "hr"), "foreground", hr_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "table"), "foreground", text_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "strikethrough"), "foreground", italic_fg, NULL); + g_object_set(gtk_text_tag_table_lookup(table, "normal_text"), "foreground", text_fg, NULL); +} + +void md_render_to_buffer(GtkTextBuffer *buffer, const char *text, int theme) { + update_tag_colors(buffer, theme); + gtk_text_buffer_set_text(buffer, "", 0); + + GtkTextIter iter; + gtk_text_buffer_get_start_iter(buffer, &iter); + + char *line_copy = g_strdup(text); + char *saveptr; + char *token = strtok_r(line_copy, "\n", &saveptr); + char *pending_token = NULL; + int in_code_block = 0; + + while (token != NULL || pending_token != NULL) { + if (pending_token) { + token = pending_token; + pending_token = NULL; + } + + char *p_trimmed = token; + while (*p_trimmed == ' ' || *p_trimmed == '\t') p_trimmed++; + + if (strncmp(p_trimmed, "```", 3) == 0) { + in_code_block = !in_code_block; + token = strtok_r(NULL, "\n", &saveptr); + continue; + } + + if (in_code_block) { + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, token, -1, "code_block", NULL); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (p_trimmed[0] == '|') { + GList *rows = NULL; + int max_cols = 0; + char *current_line = token; + + while (current_line) { + char *cl_trimmed = current_line; + while (*cl_trimmed == ' ' || *cl_trimmed == '\t') cl_trimmed++; + if (cl_trimmed[0] == '|') { + TableRow *row = parse_table_row(current_line); + rows = g_list_append(rows, row); + if (row->count > max_cols) max_cols = row->count; + current_line = strtok_r(NULL, "\n", &saveptr); + } else { + pending_token = current_line; + break; + } + } + + if (rows) { + int *col_widths = g_malloc0(sizeof(int) * max_cols); + for (GList *l = rows; l; l = l->next) { + TableRow *row = (TableRow*)l->data; + for (int i = 0; i < row->count; i++) { + int len = g_utf8_strlen(row->cells[i], -1); + if (len > col_widths[i]) col_widths[i] = len; + } + } + + for (GList *l = rows; l; l = l->next) { + TableRow *row = (TableRow*)l->data; + if (row->count > 0 && strstr(row->cells[0], "---")) continue; // Skip divider row in visual view + + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, "| ", 2, "table", NULL); + for (int i = 0; i < max_cols; i++) { + const char *cell_text = (i < row->count) ? row->cells[i] : ""; + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, cell_text, -1, "table", NULL); + int padding = col_widths[i] - g_utf8_strlen(cell_text, -1); + for (int k = 0; k < padding; k++) gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, " ", 1, "table", NULL); + if (i < max_cols - 1) gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, " | ", 3, "table", NULL); + } + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, " |", 2, "table", NULL); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } + g_free(col_widths); + g_list_free_full(rows, free_table_row); + } + if (pending_token) continue; + } else if (strncmp(p_trimmed, "---", 3) == 0 || strncmp(p_trimmed, "***", 3) == 0 || strncmp(p_trimmed, "___", 3) == 0) { + gtk_text_buffer_insert_with_tags_by_name(buffer, &iter, " ", -1, "hr", NULL); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "###### ", 7) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h6"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 7, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "##### ", 6) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h5"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 6, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "#### ", 5) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h4"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 5, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "### ", 4) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h3"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 4, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "## ", 3) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h2"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 3, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "# ", 2) == 0) { + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "h1"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, p_trimmed + 2, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (p_trimmed[0] == '>') { + int bq_level = 0; + char *p = p_trimmed; + while (*p == '>' || *p == ' ') { + if (*p == '>') bq_level++; + p++; + } + char tag[32]; + snprintf(tag, sizeof(tag), "blockquote_%d", bq_level > 5 ? 5 : bq_level); + if (!gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag)) { + gtk_text_buffer_create_tag(buffer, tag, "left-margin", bq_level * 30, "style", PANGO_STYLE_ITALIC, NULL); + } + GtkTextTag *bq_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag); + GtkTextTag *base_bq = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "blockquote"); + GSList *tags = g_slist_prepend(g_slist_prepend(NULL, bq_tag), base_bq); + insert_recursive(buffer, &iter, p, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "- [ ]", 5) == 0 && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + int indent = p_trimmed - token; + char tag[32]; + snprintf(tag, sizeof(tag), "list_%d", indent); + if (!gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag)) { + gtk_text_buffer_create_tag(buffer, tag, "left-margin", 20 + indent * 10, NULL); + } + GtkTextTag *list_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag); + GtkTextTag *cb_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "checkbox_off"); + // Checkbox part + gtk_text_buffer_insert_with_tags(buffer, &iter, "☐ ", -1, cb_tag, list_tag, NULL); + // Text part + GSList *tags = g_slist_prepend(NULL, list_tag); + insert_recursive(buffer, &iter, p_trimmed + (p_trimmed[5] == ' ' ? 6 : 5), tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if ((strncmp(p_trimmed, "- [x]", 5) == 0 || strncmp(p_trimmed, "- [X]", 5) == 0) && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + int indent = p_trimmed - token; + char tag[32]; + snprintf(tag, sizeof(tag), "list_%d", indent); + if (!gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag)) { + gtk_text_buffer_create_tag(buffer, tag, "left-margin", 20 + indent * 10, NULL); + } + GtkTextTag *list_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag); + GtkTextTag *cb_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "checkbox_on"); + // Checkbox part + gtk_text_buffer_insert_with_tags(buffer, &iter, "☑ ", -1, cb_tag, list_tag, NULL); + // Text part + GSList *tags = g_slist_prepend(NULL, list_tag); + insert_recursive(buffer, &iter, p_trimmed + (p_trimmed[5] == ' ' ? 6 : 5), tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (strncmp(p_trimmed, "- ", 2) == 0 || strncmp(p_trimmed, "* ", 2) == 0) { + int indent = p_trimmed - token; + char tag[32]; + snprintf(tag, sizeof(tag), "list_%d", indent); + if (!gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag)) { + gtk_text_buffer_create_tag(buffer, tag, "left-margin", 20 + indent * 10, NULL); + } + GtkTextTag *list_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag); + GtkTextTag *base_list = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "list"); + // Bullet part + gtk_text_buffer_insert_with_tags(buffer, &iter, "• ", -1, list_tag, base_list, NULL); + // Text part + GSList *tags = g_slist_prepend(g_slist_prepend(NULL, list_tag), base_list); + insert_recursive(buffer, &iter, p_trimmed + 2, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else if (isdigit(p_trimmed[0]) && strstr(p_trimmed, ". ")) { + char *p = strstr(p_trimmed, ". "); + if (p - p_trimmed < 4) { + int indent = p_trimmed - token; + char tag[32]; + snprintf(tag, sizeof(tag), "list_%d", indent); + if (!gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag)) { + gtk_text_buffer_create_tag(buffer, tag, "left-margin", 20 + indent * 10, NULL); + } + GtkTextTag *list_tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), tag); + GtkTextTag *base_list = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "list"); + GSList *tags = g_slist_prepend(g_slist_prepend(NULL, list_tag), base_list); + insert_recursive(buffer, &iter, p_trimmed, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else { + goto normal_text; + } + } else if (strncmp(token, "![", 2) == 0) { + insert_recursive(buffer, &iter, token, NULL); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } else { + normal_text: ; + GtkTextTag *tag = gtk_text_tag_table_lookup(gtk_text_buffer_get_tag_table(buffer), "normal_text"); + GSList *tags = g_slist_prepend(NULL, tag); + insert_recursive(buffer, &iter, token, tags); + g_slist_free(tags); + gtk_text_buffer_insert(buffer, &iter, "\n", 1); + } + token = strtok_r(NULL, "\n", &saveptr); + } + free(line_copy); +} + +char* md_to_html(const char *text) { + GString *html = g_string_new(""); + g_string_append(html, ""); + + char *line_copy = g_strdup(text); + char *saveptr; + char *token = strtok_r(line_copy, "\n", &saveptr); + char *pending_token = NULL; + int in_code_block = 0; + + while (token != NULL || pending_token != NULL) { + if (pending_token) { + token = pending_token; + pending_token = NULL; + } + + char *p_trimmed = token; + while (*p_trimmed == ' ' || *p_trimmed == '\t') p_trimmed++; + + if (strncmp(p_trimmed, "```", 3) == 0) { + if (in_code_block) g_string_append(html, "\n"); + else g_string_append(html, "
\n");
+            in_code_block = !in_code_block;
+            token = strtok_r(NULL, "\n", &saveptr);
+            continue;
+        }
+
+        if (in_code_block) {
+            g_string_append(html, token);
+            g_string_append(html, "\n");
+        } else if (strncmp(token, "---", 3) == 0 || strncmp(token, "***", 3) == 0) {
+            g_string_append(html, "
\n"); + } else if (strncmp(token, "###### ", 7) == 0) { + char *inline_text = process_inline_html(token + 7); + g_string_append_printf(html, "
%s
\n", inline_text); + g_free(inline_text); + } else if (strncmp(token, "##### ", 6) == 0) { + char *inline_text = process_inline_html(token + 6); + g_string_append_printf(html, "
%s
", inline_text); + g_free(inline_text); + } else if (strncmp(token, "#### ", 5) == 0) { + char *inline_text = process_inline_html(token + 5); + g_string_append_printf(html, "

%s

", inline_text); + g_free(inline_text); + } else if (strncmp(token, "### ", 4) == 0) { + char *inline_text = process_inline_html(token + 4); + g_string_append_printf(html, "

%s

", inline_text); + g_free(inline_text); + } else if (strncmp(token, "## ", 3) == 0) { + char *inline_text = process_inline_html(token + 3); + g_string_append_printf(html, "

%s

", inline_text); + g_free(inline_text); + } else if (strncmp(token, "# ", 2) == 0) { + char *inline_text = process_inline_html(token + 2); + g_string_append_printf(html, "

%s

\n", inline_text); + g_free(inline_text); + } else if (p_trimmed[0] == '>') { + int bq_level = 0; + char *p = p_trimmed; + while (*p == '>' || *p == ' ') { + if (*p == '>') bq_level++; + p++; + } + char *inline_text = process_inline_html(p); + for (int i = 0; i < bq_level; i++) g_string_append(html, "
"); + g_string_append_printf(html, "%s", inline_text); + for (int i = 0; i < bq_level; i++) g_string_append(html, "
\n"); + g_free(inline_text); + } else if (strncmp(p_trimmed, "- [ ]", 5) == 0 && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + char *inline_text = process_inline_html(p_trimmed + (p_trimmed[5] == ' ' ? 6 : 5)); + g_string_append_printf(html, "\t
  • %s
  • \n", inline_text); + g_free(inline_text); + } else if ((strncmp(p_trimmed, "- [x]", 5) == 0 || strncmp(p_trimmed, "- [X]", 5) == 0) && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + char *inline_text = process_inline_html(p_trimmed + (p_trimmed[5] == ' ' ? 6 : 5)); + g_string_append_printf(html, "\t
  • %s
  • \n", inline_text); + g_free(inline_text); + } else if (strncmp(p_trimmed, "- ", 2) == 0 || strncmp(p_trimmed, "* ", 2) == 0) { + int indent = p_trimmed - token; + char *inline_text = process_inline_html(p_trimmed + 2); + g_string_append_printf(html, "\t
  • %s
  • \n", indent * 20, inline_text); + g_free(inline_text); + } else if (p_trimmed[0] == '!' && p_trimmed[1] == '[') { + char *alt_start = p_trimmed + 2; + char *alt_end = strchr(alt_start, ']'); + if (alt_end && alt_end[1] == '(') { + char *url_start = alt_end + 2; + char *url_end = strchr(url_start, ')'); + if (url_end) { + *alt_end = '\0'; + *url_end = '\0'; + g_string_append_printf(html, "\"%s\"\n", url_start, alt_start); + *alt_end = ']'; + *url_end = ')'; + } + } + } else if (token[0] == '|') { + g_string_append(html, ""); + int first_row = 1; + char *current_line = token; + while (current_line) { + if (current_line[0] == '|') { + TableRow *row = parse_table_row(current_line); + if (row->count > 0 && strstr(row->cells[0], "---")) { + free_table_row(row); + } else { + g_string_append(html, ""); + for (int i = 0; i < row->count; i++) { + const char *tag = first_row ? "th" : "td"; + g_string_append_printf(html, "<%s>%s", tag, row->cells[i], tag); + } + g_string_append(html, ""); + first_row = 0; + free_table_row(row); + } + current_line = strtok_r(NULL, "\n", &saveptr); + } else { + pending_token = current_line; + break; + } + } + g_string_append(html, "
    "); + if (pending_token) continue; + } else { + char *inline_text = process_inline_html(token); + g_string_append_printf(html, "

    %s

    ", inline_text); + g_free(inline_text); + } + if (!pending_token) + token = strtok_r(NULL, "\n", &saveptr); + } + + if (in_code_block) g_string_append(html, "
    \n"); + g_string_append(html, "\n"); + free(line_copy); + return g_string_free(html, FALSE); +} + +void md_render_highlight_editor(GtkTextBuffer *buffer, int theme) { + GtkTextIter start, end; + gtk_text_buffer_get_bounds(buffer, &start, &end); + + const char *tags[] = {"h1", "h2", "h3", "h4", "h5", "h6", "bold", "italic", "bold_italic", "code", "code_block", "list", "blockquote", "checkbox_on", "checkbox_off", "normal_text"}; + for (int i = 0; i < 16; i++) { + gtk_text_buffer_remove_tag_by_name(buffer, tags[i], &start, &end); + } + + update_tag_colors(buffer, theme); + + int line_count = gtk_text_buffer_get_line_count(buffer); + for (int i = 0; i < line_count; i++) { + GtkTextIter line_start, line_end; + gtk_text_buffer_get_iter_at_line(buffer, &line_start, i); + line_end = line_start; + gtk_text_iter_forward_to_line_end(&line_end); + + char *line_text = gtk_text_buffer_get_text(buffer, &line_start, &line_end, FALSE); + if (!line_text) continue; + + char *p_trimmed = line_text; + while (*p_trimmed == ' ' || *p_trimmed == '\t') p_trimmed++; + + if (strncmp(p_trimmed, "###### ", 7) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h6", &line_start, &line_end); + } else if (strncmp(p_trimmed, "##### ", 6) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h5", &line_start, &line_end); + } else if (strncmp(p_trimmed, "#### ", 5) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h4", &line_start, &line_end); + } else if (strncmp(p_trimmed, "### ", 4) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h3", &line_start, &line_end); + } else if (strncmp(p_trimmed, "## ", 3) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h2", &line_start, &line_end); + } else if (strncmp(p_trimmed, "# ", 2) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "h1", &line_start, &line_end); + } else if (strncmp(p_trimmed, ">> ", 3) == 0 || strncmp(p_trimmed, "> ", 2) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "blockquote", &line_start, &line_end); + } else if (strncmp(p_trimmed, "- [ ]", 5) == 0 && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + gtk_text_buffer_apply_tag_by_name(buffer, "checkbox_off", &line_start, &line_end); + } else if ((strncmp(p_trimmed, "- [x]", 5) == 0 || strncmp(p_trimmed, "- [X]", 5) == 0) && (p_trimmed[5] == ' ' || p_trimmed[5] == '\0')) { + gtk_text_buffer_apply_tag_by_name(buffer, "checkbox_on", &line_start, &line_end); + } else if (strncmp(p_trimmed, "- ", 2) == 0 || strncmp(p_trimmed, "* ", 2) == 0 || isdigit(p_trimmed[0])) { + gtk_text_buffer_apply_tag_by_name(buffer, "list", &line_start, &line_end); + } else if (strncmp(p_trimmed, "```", 3) == 0) { + gtk_text_buffer_apply_tag_by_name(buffer, "code_block", &line_start, &line_end); + } else { + const char *p = line_text; + while (*p) { + if (strncmp(p, "~~", 2) == 0) { + const char *end_p = strstr(p + 2, "~~"); + if (end_p) { + GtkTextIter match_start = line_start; + GtkTextIter match_end = line_start; + gtk_text_iter_forward_chars(&match_start, p - line_text); + gtk_text_iter_forward_chars(&match_end, end_p + 2 - line_text); + gtk_text_buffer_apply_tag_by_name(buffer, "strikethrough", &match_start, &match_end); + p = end_p + 1; + } + } + if (strncmp(p, "**", 2) == 0) { + const char *end_p = strstr(p + 2, "**"); + if (end_p) { + GtkTextIter match_start = line_start; + GtkTextIter match_end = line_start; + gtk_text_iter_forward_chars(&match_start, p - line_text); + gtk_text_iter_forward_chars(&match_end, end_p + 2 - line_text); + gtk_text_buffer_apply_tag_by_name(buffer, "bold", &match_start, &match_end); + p = end_p + 1; + } + } else if (*p == '*' || *p == '_') { + const char *end_p = strpbrk(p + 1, "*_"); + if (end_p && *end_p == *p) { + GtkTextIter match_start = line_start; + GtkTextIter match_end = line_start; + gtk_text_iter_forward_chars(&match_start, p - line_text); + gtk_text_iter_forward_chars(&match_end, end_p + 1 - line_text); + gtk_text_buffer_apply_tag_by_name(buffer, "italic", &match_start, &match_end); + p = end_p; + } + } else if (*p == '`') { + const char *end_p = strchr(p + 1, '`'); + if (end_p) { + GtkTextIter match_start = line_start; + GtkTextIter match_end = line_start; + gtk_text_iter_forward_chars(&match_start, p - line_text); + gtk_text_iter_forward_chars(&match_end, end_p + 1 - line_text); + gtk_text_buffer_apply_tag_by_name(buffer, "code", &match_start, &match_end); + p = end_p; + } + } + p++; + } + } + g_free(line_text); + } +} + +GList* md_get_headers(const char *text) { + GList *headers = NULL; + char *line_copy = strdup(text); + char *saveptr; + char *token = strtok_r(line_copy, "\n", &saveptr); + int line_num = 0; + + while (token != NULL) { + int level = 0; + if (strncmp(token, "###### ", 7) == 0) level = 6; + else if (strncmp(token, "##### ", 6) == 0) level = 5; + else if (strncmp(token, "#### ", 5) == 0) level = 4; + else if (strncmp(token, "### ", 4) == 0) level = 3; + else if (strncmp(token, "## ", 3) == 0) level = 2; + else if (strncmp(token, "# ", 2) == 0) level = 1; + + if (level > 0) { + MdHeader *h = g_malloc0(sizeof(MdHeader)); + h->text = g_strdup(token + level + 1); + h->line = line_num; + h->level = level; + headers = g_list_append(headers, h); + } + token = strtok_r(NULL, "\n", &saveptr); + line_num++; + } + free(line_copy); + return headers; +} + +void md_free_headers(GList *headers) { + if (!headers) return; + for (GList *l = headers; l != NULL; l = l->next) { + MdHeader *h = (MdHeader*)l->data; + g_free(h->text); + g_free(h); + } + g_list_free(headers); +} diff --git a/md_render.h b/md_render.h new file mode 100644 index 0000000..82e95fb --- /dev/null +++ b/md_render.h @@ -0,0 +1,19 @@ +#ifndef MD_RENDER_H +#define MD_RENDER_H + +#include + +typedef struct { + char *text; + int line; + int level; +} MdHeader; + +void md_render_init_tags(GtkTextBuffer *buffer); +void md_render_highlight_editor(GtkTextBuffer *buffer, int theme); +void md_render_to_buffer(GtkTextBuffer *buffer, const char *text, int theme); +char* md_to_html(const char *text); +GList* md_get_headers(const char *text); +void md_free_headers(GList *headers); + +#endif diff --git a/mdviewer b/mdviewer new file mode 100755 index 0000000000000000000000000000000000000000..8eda30cd9bb967b7a9da564208dea751a071210d GIT binary patch literal 187832 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≠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 Nmj9KF42{R)e*lz Markdown is a lightweight markup language with plain-text-formatting syntax, created in 2004 by John Gruber with Aaron Swartz. +> +>> Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor. + +## Tables + +| Left columns | Right columns | +| ------------- |:-------------:| +| left foo | right foo | +| left bar | right bar | +| left baz | right baz | + +## Blocks of code + +``` +let message = 'Hello world'; +alert(message); +``` + +## Inline code + +This web site is using `markedjs/marked`.