gtkfilechooser: provide URI data when dragging files

This commit is contained in:
Daemonratte 2026-05-22 10:54:35 +02:00
commit d416fe6936

View file

@ -6888,6 +6888,77 @@ file_list_drag_motion_cb (GtkWidget *widget,
}
/* Constructs the popup menu for the file list if needed */
#define FILE_LIST_DRAG_URIS_KEY "gtk-file-chooser-drag-uris"
#define FILE_LIST_DRAG_ACTIVE_KEY "gtk-file-chooser-drag-active"
#define FILE_LIST_DRAG_STARTED_KEY "gtk-file-chooser-drag-started"
#define FILE_LIST_DRAG_START_X_KEY "gtk-file-chooser-drag-start-x"
#define FILE_LIST_DRAG_START_Y_KEY "gtk-file-chooser-drag-start-y"
static void
file_list_drag_uri_foreach_cb (GtkTreeModel *model,
GtkTreePath *path,
GtkTreeIter *iter,
gpointer data)
{
GPtrArray *uris = data;
GFile *file = NULL;
gchar *uri;
gtk_tree_model_get (model, iter,
MODEL_COL_FILE, &file,
-1);
if (!file)
return;
uri = g_file_get_uri (file);
if (uri)
g_ptr_array_add (uris, uri);
g_object_unref (file);
}
static void
file_list_drag_data_get_cb (GtkWidget *widget,
GdkDragContext *context,
GtkSelectionData *selection_data,
guint info,
guint time_,
GtkFileChooserDefault *impl)
{
gchar **stored_uris;
GPtrArray *uris;
stored_uris = g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_URIS_KEY);
if (stored_uris &&
g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY))
{
gtk_selection_data_set_uris (selection_data, stored_uris);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_STARTED_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_URIS_KEY, NULL);
return;
}
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_STARTED_KEY, NULL);
uris = g_ptr_array_new ();
current_selection_selected_foreach (impl,
file_list_drag_uri_foreach_cb,
uris);
if (uris->len > 0)
{
g_ptr_array_add (uris, NULL);
gtk_selection_data_set_uris (selection_data, (gchar **) uris->pdata);
}
g_ptr_array_foreach (uris, (GFunc) g_free, NULL);
g_ptr_array_free (uris, TRUE);
}
static void
file_list_build_popup_menu (GtkFileChooserDefault *impl)
{
@ -7297,6 +7368,179 @@ browse_files_select_icon_item_at_pos (GtkFileChooserDefault *impl,
return TRUE;
}
static gboolean
browse_files_clicked_item_is_selected (GtkFileChooserDefault *impl,
GtkWidget *widget,
GdkEventButton *event)
{
GtkTreePath *path;
gboolean selected = FALSE;
if (widget == impl->browse_files_tree_view)
{
GtkTreeSelection *selection;
if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (impl->browse_files_tree_view),
(gint) event->x, (gint) event->y,
&path, NULL, NULL, NULL))
return FALSE;
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (impl->browse_files_tree_view));
selected = gtk_tree_selection_path_is_selected (selection, path);
gtk_tree_path_free (path);
return selected;
}
if (widget == impl->browse_files_icon_view)
{
path = gtk_icon_view_get_path_at_pos (GTK_ICON_VIEW (impl->browse_files_icon_view),
(gint) event->x, (gint) event->y);
if (!path)
return FALSE;
selected = gtk_icon_view_path_is_selected (GTK_ICON_VIEW (impl->browse_files_icon_view), path);
gtk_tree_path_free (path);
return selected;
}
return FALSE;
}
static void
file_list_store_drag_uris (GtkWidget *widget,
GtkFileChooserDefault *impl)
{
GPtrArray *uris;
gchar **stored_uris;
uris = g_ptr_array_new ();
current_selection_selected_foreach (impl,
file_list_drag_uri_foreach_cb,
uris);
if (uris->len == 0)
{
g_ptr_array_free (uris, TRUE);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_URIS_KEY, NULL);
return;
}
g_ptr_array_add (uris, NULL);
stored_uris = (gchar **) g_ptr_array_free (uris, FALSE);
g_object_set_data_full (G_OBJECT (widget),
FILE_LIST_DRAG_URIS_KEY,
stored_uris,
(GDestroyNotify) g_strfreev);
}
static void
file_list_clear_drag_state (GtkWidget *widget)
{
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_STARTED_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_START_X_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_START_Y_KEY, NULL);
g_object_set_data (G_OBJECT (widget), FILE_LIST_DRAG_URIS_KEY, NULL);
}
static gboolean
file_list_maybe_store_drag_uris (GtkWidget *widget,
GdkEventButton *event,
GtkFileChooserDefault *impl)
{
if (event->button != 1 ||
current_selection_count_selected_rows (impl) <= 1 ||
!browse_files_clicked_item_is_selected (impl, widget, event))
{
file_list_clear_drag_state (widget);
return FALSE;
}
file_list_store_drag_uris (widget, impl);
if (!g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_URIS_KEY))
return FALSE;
g_object_set_data (G_OBJECT (widget),
FILE_LIST_DRAG_ACTIVE_KEY,
GINT_TO_POINTER (1));
g_object_set_data (G_OBJECT (widget),
FILE_LIST_DRAG_START_X_KEY,
GINT_TO_POINTER ((gint) event->x));
g_object_set_data (G_OBJECT (widget),
FILE_LIST_DRAG_START_Y_KEY,
GINT_TO_POINTER ((gint) event->y));
return TRUE;
}
static gboolean
file_list_multi_drag_motion_cb (GtkWidget *widget,
GdkEventMotion *event,
GtkFileChooserDefault *impl)
{
gint start_x;
gint start_y;
GtkTargetList *target_list;
if (!g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY))
return FALSE;
if (!(event->state & GDK_BUTTON1_MASK))
{
file_list_clear_drag_state (widget);
return FALSE;
}
if (g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_STARTED_KEY))
return TRUE;
start_x = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
FILE_LIST_DRAG_START_X_KEY));
start_y = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget),
FILE_LIST_DRAG_START_Y_KEY));
if (!gtk_drag_check_threshold (widget, start_x, start_y,
(gint) event->x, (gint) event->y))
return TRUE;
g_object_set_data (G_OBJECT (widget),
FILE_LIST_DRAG_STARTED_KEY,
GINT_TO_POINTER (1));
target_list = gtk_target_list_new (NULL, 0);
gtk_target_list_add_uri_targets (target_list, 0);
gtk_drag_begin (widget,
target_list,
GDK_ACTION_COPY | GDK_ACTION_MOVE,
1,
(GdkEvent *) event);
gtk_target_list_unref (target_list);
return TRUE;
}
static gboolean
file_list_multi_drag_button_release_cb (GtkWidget *widget,
GdkEventButton *event,
GtkFileChooserDefault *impl)
{
if (event->button == 1 &&
g_object_get_data (G_OBJECT (widget), FILE_LIST_DRAG_ACTIVE_KEY))
{
file_list_clear_drag_state (widget);
return TRUE;
}
return FALSE;
}
static gboolean
browse_files_select_clicked_item (GtkFileChooserDefault *impl,
GtkWidget *widget,
@ -7358,6 +7602,9 @@ list_button_press_event_cb (GtkWidget *widget,
if (in_press)
return FALSE;
if (file_list_maybe_store_drag_uris (widget, event, impl))
return TRUE;
if (!_gtk_button_event_triggers_context_menu (event))
return FALSE;
@ -8033,6 +8280,15 @@ create_browse_files_tree_view (GtkFileChooserDefault *impl)
NULL, 0,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
gtk_drag_source_add_uri_targets (impl->browse_files_tree_view);
g_signal_connect (impl->browse_files_tree_view, "drag-data-get",
G_CALLBACK (file_list_drag_data_get_cb), impl);
g_signal_connect (impl->browse_files_tree_view, "motion-notify-event",
G_CALLBACK (file_list_multi_drag_motion_cb), impl);
g_signal_connect (impl->browse_files_tree_view, "button-release-event",
G_CALLBACK (file_list_multi_drag_button_release_cb), impl);
gtk_widget_add_events (impl->browse_files_tree_view,
GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
g_signal_connect (selection, "changed",
G_CALLBACK (list_selection_changed), impl);
@ -8139,6 +8395,15 @@ create_browse_files_icon_view (GtkFileChooserDefault *impl)
NULL, 0,
GDK_ACTION_COPY | GDK_ACTION_MOVE);
gtk_drag_source_add_uri_targets (impl->browse_files_icon_view);
g_signal_connect (impl->browse_files_icon_view, "drag-data-get",
G_CALLBACK (file_list_drag_data_get_cb), impl);
g_signal_connect (impl->browse_files_icon_view, "motion-notify-event",
G_CALLBACK (file_list_multi_drag_motion_cb), impl);
g_signal_connect (impl->browse_files_icon_view, "button-release-event",
G_CALLBACK (file_list_multi_drag_button_release_cb), impl);
gtk_widget_add_events (impl->browse_files_icon_view,
GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK);
impl->list_icon_renderer = gtk_cell_renderer_pixbuf_new ();
g_object_set (G_OBJECT (impl->list_icon_renderer),