#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>
// 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
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,
#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);
}
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;
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)
}
}
+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
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) {