From 9609d1ffe12f01d6f372ce319ac16d89631cd87a Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Tue, 3 Jan 2012 21:28:15 +0300 Subject: [PATCH] Drag and drop for images. Adds support to open image files into ristretto as well as all the images in that same directory. Optionally, the user can just drop a single folder and ristretto will load all the images in there. For bug 3688. --- src/image_viewer.c | 46 +++++++++++++++++- src/main_window.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/image_viewer.c b/src/image_viewer.c index 557e1e3..5aa7d64 100644 --- a/src/image_viewer.c +++ b/src/image_viewer.c @@ -188,6 +188,9 @@ static void cb_rstto_image_loader_closed (GdkPixbufLoader *loader, RsttoImageViewerTransaction *transaction); static gboolean cb_rstto_image_viewer_update_pixbuf (RsttoImageViewer *viewer); +static void +cb_rstto_image_viewer_dnd (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, + guint info, guint time_, RsttoImageViewer *viewer); static gboolean rstto_scroll_event ( @@ -307,6 +310,11 @@ rstto_image_viewer_init ( GObject *object ) "notify::revert-zoom-direction", G_CALLBACK (cb_rstto_zoom_direction_changed), viewer); + g_signal_connect ( + G_OBJECT(viewer), + "drag-data-received", + G_CALLBACK (cb_rstto_image_viewer_dnd), + viewer); gtk_widget_set_events (GTK_WIDGET(viewer), GDK_BUTTON_PRESS_MASK | @@ -315,10 +323,11 @@ rstto_image_viewer_init ( GObject *object ) GDK_ENTER_NOTIFY_MASK | GDK_POINTER_MOTION_MASK); - /* - gtk_drag_dest_set(GTK_WIDGET(viewer), 0, drop_targets, G_N_ELEMENTS(drop_targets), + + gtk_drag_dest_set(GTK_WIDGET(viewer), GTK_DEST_DEFAULT_ALL, NULL, 0, GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_MOVE | GDK_ACTION_PRIVATE); - */ + gtk_drag_dest_add_uri_targets (GTK_WIDGET(viewer)); + } /** @@ -378,6 +387,14 @@ rstto_image_viewer_class_init(RsttoImageViewerClass *viewer_class) NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); + g_signal_new ("files-dnd", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, + G_TYPE_POINTER); } /** @@ -2488,3 +2505,26 @@ rstto_popup_menu ( } return FALSE; } + +static void +cb_rstto_image_viewer_dnd (GtkWidget *widget, GdkDragContext *context, gint x, gint y, GtkSelectionData *data, + guint info, guint time_, RsttoImageViewer *viewer) +{ + g_return_if_fail ( RSTTO_IS_IMAGE_VIEWER(viewer) ); + + if ((data->length >= 0) && (data->format == 8)) + { + gchar **uris; + + uris = g_uri_list_extract_uris ((const gchar*)data->data); + + g_signal_emit_by_name (viewer, "files-dnd", uris); + + gtk_drag_finish (context, TRUE, FALSE, time_); + g_strfreev (uris); + } + else + { + gtk_drag_finish (context, FALSE, FALSE, time_); + } +} diff --git a/src/main_window.c b/src/main_window.c index 26577fe..8cd2853 100644 --- a/src/main_window.c +++ b/src/main_window.c @@ -117,6 +117,8 @@ struct _RsttoMainWindowPriv gboolean playing; gint play_timeout_id; + + GtkFileFilter *filter; }; enum @@ -192,6 +194,8 @@ static void cb_rstto_main_window_save_copy (GtkWidget *widget, RsttoMainWindow *window); static void cb_rstto_main_window_delete (GtkWidget *widget, RsttoMainWindow *window); +static void +cb_rstto_main_window_dnd_files (GtkWidget *widget, gchar **uris, RsttoMainWindow *window); static void cb_rstto_main_window_set_as_wallpaper (GtkWidget *widget, RsttoMainWindow *window); @@ -452,6 +456,11 @@ rstto_main_window_init (RsttoMainWindow *window) window->priv->recent_manager = gtk_recent_manager_get_default(); window->priv->settings_manager = rstto_settings_new(); + /* Setup the image filter list for drag and drop */ + window->priv->filter = gtk_file_filter_new (); + g_object_ref_sink (window->priv->filter); + gtk_file_filter_add_pixbuf_formats (window->priv->filter); + /* D-Bus stuff */ window->priv->connection = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); @@ -768,6 +777,7 @@ rstto_main_window_init (RsttoMainWindow *window) g_signal_connect(G_OBJECT(window->priv->thumbnailbar), "button-press-event", G_CALLBACK(cb_rstto_main_window_navigationtoolbar_button_press_event), window); g_signal_connect(G_OBJECT(window->priv->image_viewer), "size-ready", G_CALLBACK(cb_rstto_main_window_update_statusbar), window); g_signal_connect(G_OBJECT(window->priv->image_viewer), "scale-changed", G_CALLBACK(cb_rstto_main_window_update_statusbar), window); + g_signal_connect(G_OBJECT(window->priv->image_viewer), "files-dnd", G_CALLBACK(cb_rstto_main_window_dnd_files), window); if ( TRUE == rstto_settings_get_boolean_property (window->priv->settings_manager, "merge-toolbars")) { @@ -869,6 +879,12 @@ rstto_main_window_dispose(GObject *object) g_object_unref (window->priv->image_list); window->priv->image_list = NULL; } + + if (window->priv->filter) + { + g_object_unref (window->priv->filter); + window->priv->filter= NULL; + } g_free (window->priv); window->priv = NULL; } @@ -3096,6 +3112,118 @@ cb_rstto_main_window_delete ( g_object_unref (file); } +static gboolean +rstto_main_window_is_valid_image (RsttoMainWindow *window, + RsttoFile *file) +{ + GtkFileFilterInfo filter_info; + + filter_info.contains = GTK_FILE_FILTER_MIME_TYPE | GTK_FILE_FILTER_URI; + filter_info.uri = rstto_file_get_uri (file); + filter_info.mime_type = rstto_file_get_content_type (file); + + return gtk_file_filter_filter (window->priv->filter, &filter_info); +} + +/** + * cb_rstto_main_window_dnd_files: + * @widget: + * @uris: + * @window: + * + */ +static void +cb_rstto_main_window_dnd_files (GtkWidget *widget, + gchar **uris, + RsttoMainWindow *window) +{ + RsttoFile *file; + guint n; + + g_return_if_fail ( RSTTO_IS_MAIN_WINDOW(window) ); + + for (n = 0; n < g_strv_length (uris); n++) + { + file = rstto_file_new (g_file_new_for_uri (uris[n])); + + if ( TRUE == rstto_main_window_is_valid_image (window, file)) + { + GFile *p_file; + /* First valid file found, use it and this directory */ + p_file = g_file_get_parent ( rstto_file_get_file (file) ); + rstto_image_list_set_directory ( + window->priv->image_list, + p_file, + NULL ); + rstto_image_list_iter_find_file ( + window->priv->iter, + file ); + return; + } + else if ( g_file_query_file_type (rstto_file_get_file (file), + G_FILE_QUERY_INFO_NONE, + NULL) == G_FILE_TYPE_DIRECTORY) + { + GFileEnumerator *enumerator; + + /* User dropped in a directory, get the files in it */ + enumerator = g_file_enumerate_children ( + rstto_file_get_file (file), + "standard::name,access::can-read", + G_FILE_QUERY_INFO_NONE, + NULL, + NULL); + + if (enumerator) + { + GFileInfo *f_info; + RsttoFile *child; + + /* Check all the files for a valid image */ + for (f_info = g_file_enumerator_next_file (enumerator, NULL, NULL); + f_info != NULL; + f_info = g_file_enumerator_next_file (enumerator, NULL, NULL)) + { + gchar *path = g_strdup_printf ("%s/%s", + rstto_file_get_path (file), + g_file_info_get_name (f_info)); + + child = rstto_file_new (g_file_new_for_path (path)); + + g_object_unref (f_info); + g_free (path); + + if ( TRUE == rstto_main_window_is_valid_image ( + window, + child)) + { + /* Found a valid image, use the directory + * and select the first image in the dir */ + rstto_image_list_set_directory ( + window->priv->image_list, + rstto_file_get_file (file), + NULL ); + rstto_image_list_iter_find_file ( + window->priv->iter, + child ); + + break; + } + /* Not a valid image file */ + g_object_unref (child); + } + g_file_enumerator_close (enumerator, NULL, NULL); + g_object_unref (enumerator); + } + } + else + { + /* Not an image file or directory */ + g_object_unref (file); + } + } +} + /**********************/ /* PRINTING CALLBACKS */ /**********************/ -- 1.7.5.4