From 3f042a30bcac7e9c74093899333fb6ad3bc4e7bf Mon Sep 17 00:00:00 2001 From: Eric Koegel Date: Fri, 6 Jan 2012 21:48:08 +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 depending on what files were dropped. 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 | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 200 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 cb13fd4..dab29f2 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; } @@ -3112,6 +3128,147 @@ 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; + gboolean first = TRUE; + + g_return_if_fail ( RSTTO_IS_MAIN_WINDOW(window) ); + + /* User dropped an image, load all images in the directory + * and select the image. + */ + if ( g_strv_length (uris) == 1) + { + file = rstto_file_new (g_file_new_for_uri (uris[n])); + if ( TRUE == rstto_main_window_is_valid_image (window, file)) + { + GFile *p_file; + 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; + } + + /* Not a valid image, but it might be a valid directory */ + g_object_unref (file); + } + + 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)) + { + /* User dropped a selection of images, load only them. + * On the first valid image, we reset the thumbnailbar. + */ + if ( TRUE == first ) + { + first = FALSE; + rstto_image_list_set_directory ( + window->priv->image_list, + NULL, + NULL); + } + + rstto_image_list_add_file ( window->priv->image_list, file, NULL); + rstto_image_list_iter_find_file ( window->priv->iter, + file ); + } + 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