diff options
Diffstat (limited to '')
| -rw-r--r-- | dropping.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/dropping.c b/dropping.c new file mode 100644 index 0000000..412f0ab --- /dev/null +++ b/dropping.c @@ -0,0 +1,120 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +static char url_list_name[] = "text/uri-list"; +static GtkTargetEntry url_list = {url_list_name, 0u, 0u}; + +static size_t +get_dropped_files(struct file **files_out, char **uris) +{ + GError *error = NULL; + char **uri, *path; + struct file *dropped_files = NULL; + size_t i = 0u; + + if (!uris) + goto out; + + for (uri = uris; *uri; uri++) + i += 1u; + if (!i) + goto out; + dropped_files = ecalloc(i, sizeof(*dropped_files)); + + for (uri = uris, i = 0u; *uri; uri++) { + error = NULL; /* g_filename_from_uri requires this initialisation */ + path = g_filename_from_uri(*uri, NULL, &error); + if (!path) { + weprintf("g_filename_from_uri: %s", error->message); + g_error_free(error); + continue; + } + dropped_files[i++].path = estrdup(path); + g_free(path); + /* the other fields are set in the next code block */ + } + +out: + *files_out = dropped_files; + return i; +} + +static void +drop_files(GtkWidget *widget, GdkDragContext *context, int x, int y, + GtkSelectionData *data, unsigned int info, unsigned int time, void *user) +{ + char **uris = NULL; + struct file *dropped_files; + size_t n; + int accepted = 1; + GdkModifierType modifiers; + GdkWindow *event_window; + + (void) x; + (void) y; + (void) info; + (void) user; + + if (gtk_drag_get_source_widget(context) == display_area) { + accepted = 0; + goto out; + } + event_window = gtk_widget_get_window(widget); + if (!event_window) + goto out; + if (!gdk_window_get_pointer(event_window, NULL, NULL, &modifiers)) + goto out; + + uris = gtk_selection_data_get_uris(data); + n = get_dropped_files(&dropped_files, uris); + if (!n) + goto out; + + if ((modifiers & GDK_CONTROL_MASK) && nfiles) { + n = files_from_array(dropped_files, n); + add_images(dropped_files, n); + free(dropped_files); + } else { + set_images_from_array(dropped_files, n); + } + +out: + gtk_drag_finish(context, accepted, 0, time); + if (uris) + g_strfreev(uris); +} + +static int +drop_motion(GtkWidget *widget, GdkDragContext *context, int x, int y, unsigned int time, void *user) +{ + GdkWindow *event_window = gtk_widget_get_window(widget); + GdkModifierType modifiers; + GdkDragAction action = 0; + + (void) x; + (void) y; + (void) user; + + if (!gtk_get_current_event_state(&modifiers)) + if (!event_window || !gdk_window_get_pointer(event_window, NULL, NULL, &modifiers)) + goto out; + if (gtk_drag_dest_find_target(widget, context, NULL) == GDK_NONE) + goto out; + + action = (modifiers & GDK_CONTROL_MASK) ? GDK_ACTION_COPY : GDK_ACTION_MOVE; + if (!(gdk_drag_context_get_actions(context) & action)) + action = 0; + +out: + gdk_drag_status(context, action, time); + return 1; +} + +void +set_up_dropping(void) +{ + gtk_drag_dest_set(window, GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, + &url_list, 1u, GDK_ACTION_COPY | GDK_ACTION_MOVE); + g_signal_connect(window, "drag-motion", G_CALLBACK(&drop_motion), NULL); + g_signal_connect(window, "drag-data-received", G_CALLBACK(&drop_files), NULL); +} |
