From: Olaf Wintermann Date: Wed, 19 Mar 2025 19:38:45 +0000 (+0100) Subject: make text anchor widgets persistent X-Git-Url: https://uap-core.de/gitweb/?a=commitdiff_plain;h=5d3f1aa753729ca8e9f097f50825269089bcd59b;p=note.git make text anchor widgets persistent --- diff --git a/application/gtk-text.c b/application/gtk-text.c index 1133d7f..1c2ef4a 100644 --- a/application/gtk-text.c +++ b/application/gtk-text.c @@ -31,6 +31,7 @@ #include "note.h" #include +#include #include #include #include @@ -41,6 +42,7 @@ // workaround for netbeans bug #define g_object_set g_object_set #define g_object_get_property g_object_get_property +#define g_object_set_data_full g_object_set_data_full #define g_object_set_data g_object_set_data #define g_object_get_data g_object_get_data @@ -52,6 +54,17 @@ static void editor_set_cursor_cb( GtkTextMark *mark, NoteEditor *editor); +static void editor_set_buffer_cb( + GObject *object, + GParamSpec *pspec, + NoteEditor *editor); + +static void editor_delete_range_cb( + GtkTextBuffer *buffer, + const GtkTextIter *start, + const GtkTextIter *end, + NoteEditor *editor); + #if GTK_CHECK_VERSION(4, 0, 0) static void editor_button_released_cb( GtkGestureClick *gesture, @@ -120,6 +133,8 @@ void editor_init_textview(UiObject *obj, UIWIDGET textview) { #else g_signal_connect(textview, "event-after", G_CALLBACK(editor_textview_event_after), editor); #endif + + g_signal_connect(textview, "notify::buffer", G_CALLBACK(editor_set_buffer_cb), editor); } @@ -170,6 +185,60 @@ static void editor_set_cursor_cb( update_cursor_paragraph_style(editor, buffer); } +static void editor_set_buffer_cb( + GObject *object, + GParamSpec *pspec, + NoteEditor *editor) +{ + GtkTextView *textview = GTK_TEXT_VIEW(object); + GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview); + + BufferEmbeddedObjects *embedded_objects = g_object_get_data(G_OBJECT(buffer), "embedded"); + if(!embedded_objects) { + return; + } + + CxIterator i = cxListIterator(embedded_objects->objects); + cx_foreach(EmbeddedWidget *, w, i) { + gtk_text_view_add_child_at_anchor(textview, w->widget, w->anchor); + } +} + +static void em_remove_anchor(BufferEmbeddedObjects *em, GtkTextChildAnchor *anchor) { + CxIterator i = cxListIterator(em->objects); + size_t index = cxListSize(em->objects); + cx_foreach(EmbeddedWidget *, w, i) { + if(w->anchor == anchor) { + index = i.index; + break; + } + } + if(cxListIndexValid(em->objects, index)) { + cxListRemove(em->objects, index); + } +} + +static void editor_delete_range_cb( + GtkTextBuffer *buffer, + const GtkTextIter *start, + const GtkTextIter *end, + NoteEditor *editor) +{ + BufferEmbeddedObjects *em = g_object_get_data(G_OBJECT(buffer), "embedded"); + if(!em) { + return; + } + + GtkTextIter iter = *start; + while (!gtk_text_iter_equal(&iter, end)) { + GtkTextChildAnchor *anchor = gtk_text_iter_get_child_anchor(&iter); + if(anchor) { + em_remove_anchor(em, anchor); + } + gtk_text_iter_forward_cursor_position(&iter); + } +} + static gboolean path_is_image_file(cxstring path) { if(path.length == 0) { return FALSE; @@ -219,6 +288,17 @@ static void editor_attach_image(NoteEditor *editor, GdkPixbuf *pixbuf) { gtk_widget_set_size_request(image, width, height); gtk_text_view_add_child_at_anchor(textview, image, anchor); + + // remember widget and store a reference in the textbuffer + // TODO: we need a cleanup strategy + EmbeddedWidget *em = malloc(sizeof(EmbeddedWidget)); + em->widget = image; + em->anchor = anchor; + em->restore = NULL; + g_object_ref(image); + + BufferEmbeddedObjects *embedded_objects = g_object_get_data(G_OBJECT(buffer), "embedded"); + cxListAdd(embedded_objects->objects, em); } #if GTK_CHECK_VERSION(4, 0, 0) @@ -361,6 +441,15 @@ void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos) { } } +static void buffer_embedded_objects_free(BufferEmbeddedObjects *em) { + cxListFree(em->objects); + free(em); +} + +static void embedded_widget_free(EmbeddedWidget *w) { + g_object_unref(w->widget); + free(w); +} void editor_init_textbuf(UiText *text) { // toolkit internals: data1 is a GtkTextBuffer, but it is only @@ -385,7 +474,13 @@ void editor_init_textbuf(UiText *text) { NoteEditor *editor = g_object_get_data(G_OBJECT(textview), "editor"); if(editor) { g_signal_connect(buf, "mark-set", G_CALLBACK(editor_set_cursor_cb), editor); + g_signal_connect(buf, "delete-range", G_CALLBACK(editor_delete_range_cb), editor); } + + BufferEmbeddedObjects *embedded_objects = malloc(sizeof(BufferEmbeddedObjects)); + embedded_objects->objects = cxArrayListCreateSimple(CX_STORE_POINTERS, 8); + embedded_objects->objects->collection.simple_destructor = (cx_destructor_func)embedded_widget_free; + g_object_set_data_full(G_OBJECT(buf), "embedded", embedded_objects, (GDestroyNotify)buffer_embedded_objects_free); } void init_textbuf(GtkTextBuffer *buf) { diff --git a/application/gtk-text.h b/application/gtk-text.h index 3f25b25..97f30dd 100644 --- a/application/gtk-text.h +++ b/application/gtk-text.h @@ -50,6 +50,17 @@ typedef struct TextLink { int type; } TextLink; +typedef struct BufferEmbeddedObjects { + CxList *objects; +} BufferEmbeddedObjects; + +typedef struct EmbeddedWidget EmbeddedWidget; +struct EmbeddedWidget { + void (*restore)(NoteEditor *, EmbeddedWidget *); + GtkWidget *widget; + GtkTextChildAnchor *anchor; +}; + typedef void (*set_style_cb)(MDActiveStyles *style, GtkTextTag *tag); void editor_try_follow_link(NoteEditor *editor, GtkTextIter *pos);