From 332ffe39634b553c3afc1ee4404d0bdf47fd5217 Mon Sep 17 00:00:00 2001 From: Olaf Wintermann Date: Wed, 12 Mar 2025 19:41:45 +0100 Subject: [PATCH] implement markdown links --- application/editor.c | 1 + application/editor.h | 2 +- application/gtk-text.c | 77 ++++++++++++++++++++++++++++++++++++++---- application/gtk-text.h | 12 +++++++ application/window.c | 2 ++ 5 files changed, 87 insertions(+), 7 deletions(-) diff --git a/application/editor.c b/application/editor.c index 045fb5d..179d4ea 100644 --- a/application/editor.c +++ b/application/editor.c @@ -335,6 +335,7 @@ static void linearize_paragraph(CxBuffer *buf, CxList *sections, MDPara *p) { sec.pos = start_pos; sec.length = buf->pos - start_pos; sec.style = paragraph_style(p); + sec.link = NULL; cxListAdd(sections, &sec); cxBufferPut(buf, '\n'); diff --git a/application/editor.h b/application/editor.h index 3c1499b..15dc2ab 100644 --- a/application/editor.h +++ b/application/editor.h @@ -111,7 +111,7 @@ MDDocLinear mddoc_linearization(MDDoc *doc); // platform specific implementation // (gtk-text.c) void editor_global_init(); -void editor_init_textview(UIWIDGET textview); +void editor_init_textview(UiObject *obj, UIWIDGET textview); void editor_init_textbuf(UiText *text); void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles); cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a); diff --git a/application/gtk-text.c b/application/gtk-text.c index ab7a4fb..e0b7f5c 100644 --- a/application/gtk-text.c +++ b/application/gtk-text.c @@ -34,6 +34,13 @@ static CxMap *markdown_tags; +static void editor_button_released_cb( + GtkGestureClick *gesture, + guint n_press, + double x, + double y, + NoteEditor *editor); + void editor_global_init() { markdown_tags = cxHashMapCreateSimple(sizeof(MDTag)); @@ -48,11 +55,70 @@ void editor_global_init() { } -void editor_init_textview(UIWIDGET textview) { +void editor_init_textview(UiObject *obj, UIWIDGET textview) { gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(textview), GTK_WRAP_WORD_CHAR); + gtk_text_view_set_left_margin(GTK_TEXT_VIEW(textview), 16); + + NoteEditor *editor = malloc(sizeof(NoteEditor)); + memset(editor, 0, sizeof(NoteEditor)); + + editor->obj = obj; + editor->textview = textview; + + g_object_set_data(G_OBJECT(textview), "editor", editor); + + GtkEventController *controller = GTK_EVENT_CONTROLLER(gtk_gesture_click_new()); + g_signal_connect(controller, "released", G_CALLBACK (editor_button_released_cb), editor); + gtk_widget_add_controller(textview, controller); +} + +static void editor_button_released_cb( + GtkGestureClick *gesture, + guint n_press, + double x, + double y, + NoteEditor *editor) +{ + GtkTextView *textview = GTK_TEXT_VIEW(editor->textview); + if(gtk_gesture_single_get_current_button(GTK_GESTURE_SINGLE(gesture)) != 1) { + return; + } + + GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview); + + if(gtk_text_buffer_get_has_selection(buffer)) { + return; + } + int buffer_x, buffer_y; + gtk_text_view_window_to_buffer_coords( + textview, + GTK_TEXT_WINDOW_WIDGET, + x, + y, + &buffer_x, + &buffer_y); + + GtkTextIter pos; + if(gtk_text_view_get_iter_at_location(textview, &pos, buffer_x, buffer_y)) { + editor_try_follow_link(editor, &pos); + } } +void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos) { + GSList *tags = gtk_text_iter_get_tags(pos); + while(tags) { + GtkTextTag *tag = tags->data; + TextLink *link = g_object_get_data(G_OBJECT(tag), "link"); + if(link) { + printf("open link: %s\n", link->link); + break; + } + tags = tags->next; + } +} + + void editor_init_textbuf(UiText *text) { // toolkit internals: data1 is a GtkTextBuffer, but it is only // initialized after UiText is bound to the textview @@ -143,7 +209,9 @@ void editor_apply_styles(UiText *text, CxList /* MDDocStyleSection */ *styles) { } if(sec->link) { GtkTextTag *linktag = gtk_text_tag_new(NULL); - char *link = strdup(sec->link); + TextLink *link = malloc(sizeof(TextLink)); + link->link = strdup(sec->link); + link->type = 0; g_object_set_data(G_OBJECT(linktag), "link", link); // TODO: destroy handler gtk_text_tag_table_add(tagtable, linktag); @@ -184,10 +252,7 @@ static void map_subtract(CxMap *map, CxMap *sub) { } } -cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { - char *str = ui_get(text); - - +cxmutstr editor_get_markdown(UiText *text, const CxAllocator *a) { CxBuffer out; cxBufferInit(&out, NULL, 1024, a, CX_BUFFER_AUTO_EXTEND); diff --git a/application/gtk-text.h b/application/gtk-text.h index f09d4db..57e69a9 100644 --- a/application/gtk-text.h +++ b/application/gtk-text.h @@ -35,11 +35,23 @@ extern "C" { #endif +typedef struct NoteEditor { + UiObject *obj; + GtkWidget *textview; +} NoteEditor; + typedef struct MDTag { const char *begin; const char *end; } MDTag; +typedef struct TextLink { + char *link; + int type; +} TextLink; + +void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos); + void init_textbuf(GtkTextBuffer *buf); void init_tagtable(GtkTextTagTable *table); diff --git a/application/window.c b/application/window.c index 64ca118..fec346b 100644 --- a/application/window.c +++ b/application/window.c @@ -30,6 +30,7 @@ #include "application.h" #include "store.h" #include "notebook.h" +#include "editor.h" #include #include @@ -83,6 +84,7 @@ void window_create() { ui_button(obj, .icon = "insert-link"); } wdata->textview = ui_textarea(obj, .varname = "note_text", .vfill = TRUE, .hfill = TRUE, .hexpand = TRUE, .vexpand = TRUE, .colspan = 2, .groups = UI_GROUPS(APP_STATE_NOTE_SELECTED), .fill = UI_ON); + editor_init_textview(obj, ui_textarea_gettextwidget(wdata->textview)); } } } -- 2.43.5