]> uap-core.de Git - note.git/commitdiff
implement editor dnd and image insertion
authorOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 19 Mar 2025 19:03:24 +0000 (20:03 +0100)
committerOlaf Wintermann <olaf.wintermann@gmail.com>
Wed, 19 Mar 2025 19:03:24 +0000 (20:03 +0100)
application/gtk-text.c

index f1cd7c1202a1758bf63ee33e73ad95ccc34060de..1133d7f63906eddf27fc6a2f093ef8ff17d564d9 100644 (file)
@@ -32,6 +32,8 @@
 
 #include <cx/buffer.h>
 #include <cx/hash_map.h>
+#include <cx/string.h>
+#include <cx/printf.h>
 
 #include <ctype.h>
 #include <string.h>
@@ -58,6 +60,13 @@ static void editor_button_released_cb(
         double y,
         NoteEditor *editor);
 
+static gboolean editor_drop_cb(
+        GtkDropTarget *target,
+        const GValue *value,
+        double x,
+        double y,
+        NoteEditor *editor);
+
 #else
 
 static gboolean editor_textview_event_after(GtkWidget *textview, GdkEvent *ev, NoteEditor *editor);
@@ -97,6 +106,17 @@ void editor_init_textview(UiObject *obj, UIWIDGET textview) {
     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);
+    
+    // DnD
+    GType dnd_types[2] = { 
+        G_TYPE_FILE,
+        GDK_TYPE_PIXBUF
+    };
+    
+    GtkDropTarget *dndtarget = gtk_drop_target_new (G_TYPE_INVALID, GDK_ACTION_COPY);
+    gtk_drop_target_set_gtypes(dndtarget, dnd_types, 2);
+    g_signal_connect(dndtarget, "drop", G_CALLBACK(editor_drop_cb), editor);
+    gtk_widget_add_controller(textview, GTK_EVENT_CONTROLLER(dndtarget));
 #else
     g_signal_connect(textview, "event-after", G_CALLBACK(editor_textview_event_after), editor);
 #endif
@@ -150,6 +170,57 @@ static void editor_set_cursor_cb(
     update_cursor_paragraph_style(editor, buffer);
 }
 
+static gboolean path_is_image_file(cxstring path) {
+    if(path.length == 0) {
+        return FALSE;
+    }
+    
+    cxstring ext = (cxstring){NULL, 0};
+    for(int i=path.length-1;i>= 0;i--) {
+        if(path.ptr[i] == '.') {
+            ext = cx_strsubs(path, i+1);
+            break;
+        }
+    }
+    
+    if(ext.length == 0) {
+        return FALSE;
+    }
+    
+    const char *img_ext[] = { "png", "jpg", "jpeg", "webp", "tif", "bmp", NULL};
+    const char **i = img_ext;
+    while(*i) {
+        if(!cx_strcmp(ext, cx_str(*i))) {
+            return TRUE;
+        }
+        i++;
+    }
+    return FALSE;
+}
+
+static gboolean editor_attach_file(NoteEditor *editor, const char *path) {
+    return FALSE; // TODO
+}
+
+static void editor_attach_image(NoteEditor *editor, GdkPixbuf *pixbuf) {
+    GtkTextView *textview = GTK_TEXT_VIEW(editor->textview);
+    GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview);
+    
+    GtkTextMark *cursor = gtk_text_buffer_get_insert(buffer);
+    GtkTextIter iter;
+    gtk_text_buffer_get_iter_at_mark(buffer, &iter, cursor);
+    
+    GtkTextChildAnchor *anchor = gtk_text_buffer_create_child_anchor(buffer, &iter);
+    GdkTexture *texture = gdk_texture_new_for_pixbuf(pixbuf);
+    GtkWidget *image = gtk_image_new_from_paintable(GDK_PAINTABLE(texture));
+    
+    int width = gdk_texture_get_width(texture);
+    int height = gdk_texture_get_height(texture);
+    gtk_widget_set_size_request(image, width, height);
+    
+    gtk_text_view_add_child_at_anchor(textview, image, anchor);
+}
+
 #if GTK_CHECK_VERSION(4, 0, 0)
 
 /*
@@ -188,6 +259,57 @@ static void editor_button_released_cb(
     }
 }
 
+static gboolean editor_drop_cb(
+        GtkDropTarget *target,
+        const GValue *value,
+        double x,
+        double y,
+        NoteEditor *editor)
+{
+    gboolean success = TRUE;
+    GdkPixbuf *pixbuf = NULL;
+    if(G_VALUE_HOLDS(value, G_TYPE_FILE)) {
+        // dnd type: uri
+        GFile *file = g_value_get_object(value);
+        if(!file) {
+            return FALSE;
+        }
+        char *scheme = g_file_get_uri_scheme(file);
+        if(scheme && strcmp(scheme, "file")) {
+            g_free(scheme);
+            return FALSE;
+        }
+        g_free(scheme);
+        
+        char *path = g_file_get_path(file);
+        printf("dnd file: %s\n", path);
+        
+        if(path_is_image_file(cx_str(path))) {
+            GError *error = NULL;
+            pixbuf = gdk_pixbuf_new_from_file(path, &error);
+            if(!pixbuf) {
+                // cannot load image, attach as generic file
+                success = editor_attach_file(editor, path);
+            }
+        } else {
+            // attach generic file
+            success = editor_attach_file(editor, path);
+        }
+        g_free(path);
+    } else if(G_VALUE_HOLDS (value, GDK_TYPE_PIXBUF)) {
+        // dnd type image
+        pixbuf = g_value_get_object(value);
+    } else {
+        return FALSE;
+    }
+    
+    if(pixbuf && success) {
+        editor_attach_image(editor, pixbuf);
+    }
+    
+    return success;
+}
+
 #else
 
 static gboolean editor_textview_event_after(GtkWidget *textview, GdkEvent *ev, NoteEditor *editor) {