]> uap-core.de Git - note.git/commitdiff
make text anchor widgets persistent
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 19 Mar 2025 19:38:45 +0000 (20:38 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 19 Mar 2025 19:38:45 +0000 (20:38 +0100)
application/gtk-text.c
application/gtk-text.h

index 1133d7f63906eddf27fc6a2f093ef8ff17d564d9..1c2ef4ae1e779d7bdcd46b30882ba5bff94a6a47 100644 (file)
@@ -31,6 +31,7 @@
 #include "note.h"
 
 #include <cx/buffer.h>
+#include <cx/array_list.h>
 #include <cx/hash_map.h>
 #include <cx/string.h>
 #include <cx/printf.h>
@@ -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) {
index 3f25b254b3615af16b8e8a11a2a3077ad50b93ab..97f30ddf31eb4be9973f07e1151a4704574a89ea 100644 (file)
@@ -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);