This is a huge reorganization of GtkDropTarget. I did not know how to split this up, so it's unfortunately all one commit. Highlights: - Split GtkDropTarget into GtkDropTarget and GtkDropTargetAsync GtkDropTarget is the simple one that only works with GTypes and offers a synchronous interface. GtkDropTargetAsync retains the full old functionality and allows handling mime types. - Drop events are handled differently Instead of picking a single drop target and sending all DND events to it, every event is sent to every drop target. The first one to handle the event gets to call gdk_drop_status(), further handlers do not interact with the GdkDrop. Of course, for the ultimate GDK_DROP_STARTING event, only the first one to accept the drop gets to handle it. This allows stacking DND event controllers that aren't necessarily interested in handling the event or that might decide later to drop it. - Port all widgets to either of those Both have a somewhat changed API due to the new event handling. For the ones who should use the sync version, lots of cleanup was involved to operate on a sync API.
385 lines
12 KiB
C
385 lines
12 KiB
C
/* Clipboard
|
|
*
|
|
* GdkClipboard is used for clipboard handling. This demo shows how to
|
|
* copy and paste text to and from the clipboard.
|
|
*
|
|
* It also shows how to transfer images via the clipboard or via
|
|
* drag-and-drop, and how to make clipboard contents persist after
|
|
* the application exits. Clipboard persistence requires a clipboard
|
|
* manager to run.
|
|
*/
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <gtk/gtk.h>
|
|
#include <string.h>
|
|
|
|
static GtkWidget *window = NULL;
|
|
|
|
void
|
|
copy_button_clicked (GtkWidget *button,
|
|
gpointer user_data)
|
|
{
|
|
GtkWidget *entry;
|
|
GdkClipboard *clipboard;
|
|
|
|
entry = GTK_WIDGET (user_data);
|
|
|
|
/* Get the clipboard object */
|
|
clipboard = gtk_widget_get_clipboard (entry);
|
|
|
|
/* Set clipboard text */
|
|
gdk_clipboard_set_text (clipboard, gtk_editable_get_text (GTK_EDITABLE (entry)));
|
|
}
|
|
|
|
void
|
|
paste_received (GObject *source_object,
|
|
GAsyncResult *result,
|
|
gpointer user_data)
|
|
{
|
|
GdkClipboard *clipboard;
|
|
GtkWidget *entry;
|
|
char *text;
|
|
GError *error = NULL;
|
|
|
|
clipboard = GDK_CLIPBOARD (source_object);
|
|
entry = GTK_WIDGET (user_data);
|
|
|
|
/* Get the resulting text of the read operation */
|
|
text = gdk_clipboard_read_text_finish (clipboard, result, &error);
|
|
|
|
if (text)
|
|
{
|
|
/* Set the entry text */
|
|
gtk_editable_set_text (GTK_EDITABLE (entry), text);
|
|
g_free (text);
|
|
}
|
|
else
|
|
{
|
|
GtkWidget *dialog;
|
|
|
|
/* Show an error about why pasting failed.
|
|
* Usually you probably want to ignore such failures,
|
|
* but for demonstration purposes, we show the error.
|
|
*/
|
|
dialog = gtk_message_dialog_new (GTK_WINDOW (window),
|
|
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
|
|
GTK_MESSAGE_ERROR,
|
|
GTK_BUTTONS_CLOSE,
|
|
"Could not paste text: %s",
|
|
error->message);
|
|
g_signal_connect (dialog, "response",
|
|
G_CALLBACK (gtk_widget_destroy), NULL);
|
|
gtk_widget_show (dialog);
|
|
|
|
g_error_free (error);
|
|
}
|
|
}
|
|
|
|
void
|
|
paste_button_clicked (GtkWidget *button,
|
|
gpointer user_data)
|
|
{
|
|
GtkWidget *entry;
|
|
GdkClipboard *clipboard;
|
|
|
|
entry = GTK_WIDGET (user_data);
|
|
|
|
/* Get the clipboard object */
|
|
clipboard = gtk_widget_get_clipboard (entry);
|
|
|
|
/* Request the contents of the clipboard, contents_received will be
|
|
called when we do get the contents.
|
|
*/
|
|
gdk_clipboard_read_text_async (clipboard, NULL, paste_received, entry);
|
|
}
|
|
|
|
static GdkPaintable *
|
|
get_image_paintable (GtkImage *image)
|
|
{
|
|
const gchar *icon_name;
|
|
GtkIconTheme *icon_theme;
|
|
GtkIconPaintable *icon;
|
|
|
|
switch (gtk_image_get_storage_type (image))
|
|
{
|
|
case GTK_IMAGE_PAINTABLE:
|
|
return g_object_ref (gtk_image_get_paintable (image));
|
|
case GTK_IMAGE_ICON_NAME:
|
|
icon_name = gtk_image_get_icon_name (image);
|
|
icon_theme = gtk_icon_theme_get_for_display (gtk_widget_get_display (GTK_WIDGET (image)));
|
|
icon = gtk_icon_theme_lookup_icon (icon_theme,
|
|
icon_name,
|
|
NULL,
|
|
48, 1,
|
|
gtk_widget_get_direction (GTK_WIDGET (image)),
|
|
0);
|
|
if (icon == NULL)
|
|
return NULL;
|
|
return GDK_PAINTABLE (icon);
|
|
default:
|
|
g_warning ("Image storage type %d not handled",
|
|
gtk_image_get_storage_type (image));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
drag_begin (GtkDragSource *source,
|
|
GdkDrag *drag,
|
|
GtkWidget *widget)
|
|
{
|
|
GdkPaintable *paintable;
|
|
|
|
paintable = get_image_paintable (GTK_IMAGE (widget));
|
|
if (paintable)
|
|
{
|
|
gtk_drag_source_set_icon (source, paintable, -2, -2);
|
|
g_object_unref (paintable);
|
|
}
|
|
}
|
|
|
|
static GdkContentProvider *
|
|
prepare_drag (GtkDragSource *source,
|
|
double x,
|
|
double y,
|
|
GtkWidget *image)
|
|
{
|
|
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (image));
|
|
|
|
if (!GDK_IS_TEXTURE (paintable))
|
|
return NULL;
|
|
|
|
return gdk_content_provider_new_typed (GDK_TYPE_TEXTURE, paintable);
|
|
}
|
|
|
|
static gboolean
|
|
drag_drop (GtkDropTarget *dest,
|
|
const GValue *value,
|
|
double x,
|
|
double y,
|
|
GtkImage *image)
|
|
{
|
|
GdkTexture *texture = g_value_get_object (value);
|
|
gtk_image_set_from_paintable (GTK_IMAGE (image), GDK_PAINTABLE (texture));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
copy_image (GSimpleAction *action,
|
|
GVariant *value,
|
|
gpointer data)
|
|
{
|
|
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
|
|
GdkPaintable *paintable = get_image_paintable (GTK_IMAGE (data));
|
|
|
|
if (GDK_IS_TEXTURE (paintable))
|
|
gdk_clipboard_set_texture (clipboard, GDK_TEXTURE (paintable));
|
|
|
|
if (paintable)
|
|
g_object_unref (paintable);
|
|
}
|
|
|
|
static void
|
|
paste_image_received (GObject *source,
|
|
GAsyncResult *result,
|
|
gpointer data)
|
|
{
|
|
GdkTexture *texture;
|
|
|
|
texture = gdk_clipboard_read_texture_finish (GDK_CLIPBOARD (source), result, NULL);
|
|
if (texture == NULL)
|
|
return;
|
|
|
|
gtk_image_set_from_paintable (GTK_IMAGE (data), GDK_PAINTABLE (texture));
|
|
g_object_unref (texture);
|
|
}
|
|
|
|
static void
|
|
paste_image (GSimpleAction *action,
|
|
GVariant *value,
|
|
gpointer data)
|
|
{
|
|
GdkClipboard *clipboard = gtk_widget_get_clipboard (GTK_WIDGET (data));
|
|
gdk_clipboard_read_texture_async (clipboard, NULL, paste_image_received, data);
|
|
}
|
|
|
|
static void
|
|
pressed_cb (GtkGesture *gesture,
|
|
int n_press,
|
|
double x,
|
|
double y,
|
|
GtkWidget *image)
|
|
{
|
|
GtkWidget *popover;
|
|
GMenu *menu;
|
|
GMenuItem *item;
|
|
|
|
menu = g_menu_new ();
|
|
item = g_menu_item_new (_("_Copy"), "clipboard.copy");
|
|
g_menu_append_item (menu, item);
|
|
|
|
item = g_menu_item_new (_("_Paste"), "clipboard.paste");
|
|
g_menu_append_item (menu, item);
|
|
|
|
popover = gtk_popover_menu_new_from_model (G_MENU_MODEL (menu));
|
|
gtk_widget_set_parent (popover, image);
|
|
|
|
gtk_popover_set_pointing_to (GTK_POPOVER (popover), &(GdkRectangle) { x, y, 1, 1});
|
|
gtk_popover_popup (GTK_POPOVER (popover));
|
|
|
|
g_object_unref (menu);
|
|
}
|
|
|
|
GtkWidget *
|
|
do_clipboard (GtkWidget *do_widget)
|
|
{
|
|
if (!window)
|
|
{
|
|
GtkWidget *vbox, *hbox;
|
|
GtkWidget *label;
|
|
GtkWidget *entry, *button;
|
|
GtkWidget *image;
|
|
GtkGesture *gesture;
|
|
GActionEntry entries[] = {
|
|
{ "copy", copy_image, NULL, NULL, NULL },
|
|
{ "paste", paste_image, NULL, NULL, NULL },
|
|
};
|
|
GActionGroup *actions;
|
|
GtkDragSource *source;
|
|
GtkDropTarget *dest;
|
|
|
|
window = gtk_window_new ();
|
|
gtk_window_set_display (GTK_WINDOW (window),
|
|
gtk_widget_get_display (do_widget));
|
|
gtk_window_set_title (GTK_WINDOW (window), "Clipboard");
|
|
|
|
g_signal_connect (window, "destroy",
|
|
G_CALLBACK (gtk_widget_destroyed), &window);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_widget_set_margin_start (vbox, 8);
|
|
gtk_widget_set_margin_end (vbox, 8);
|
|
gtk_widget_set_margin_top (vbox, 8);
|
|
gtk_widget_set_margin_bottom (vbox, 8);
|
|
|
|
gtk_container_add (GTK_CONTAINER (window), vbox);
|
|
|
|
label = gtk_label_new ("\"Copy\" will copy the text\nin the entry to the clipboard");
|
|
|
|
gtk_container_add (GTK_CONTAINER (vbox), label);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_widget_set_margin_start (hbox, 8);
|
|
gtk_widget_set_margin_end (hbox, 8);
|
|
gtk_widget_set_margin_top (hbox, 8);
|
|
gtk_widget_set_margin_bottom (hbox, 8);
|
|
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
|
|
|
/* Create the first entry */
|
|
entry = gtk_entry_new ();
|
|
gtk_container_add (GTK_CONTAINER (hbox), entry);
|
|
|
|
/* Create the button */
|
|
button = gtk_button_new_with_mnemonic (_("_Copy"));
|
|
gtk_container_add (GTK_CONTAINER (hbox), button);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (copy_button_clicked), entry);
|
|
|
|
label = gtk_label_new ("\"Paste\" will paste the text from the clipboard to the entry");
|
|
gtk_container_add (GTK_CONTAINER (vbox), label);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_widget_set_margin_start (hbox, 8);
|
|
gtk_widget_set_margin_end (hbox, 8);
|
|
gtk_widget_set_margin_top (hbox, 8);
|
|
gtk_widget_set_margin_bottom (hbox, 8);
|
|
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
|
|
|
/* Create the second entry */
|
|
entry = gtk_entry_new ();
|
|
gtk_container_add (GTK_CONTAINER (hbox), entry);
|
|
|
|
/* Create the button */
|
|
button = gtk_button_new_with_mnemonic (_("_Paste"));
|
|
gtk_container_add (GTK_CONTAINER (hbox), button);
|
|
g_signal_connect (button, "clicked",
|
|
G_CALLBACK (paste_button_clicked), entry);
|
|
|
|
label = gtk_label_new ("Images can be transferred via the clipboard, too");
|
|
gtk_container_add (GTK_CONTAINER (vbox), label);
|
|
|
|
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_widget_set_margin_start (hbox, 8);
|
|
gtk_widget_set_margin_end (hbox, 8);
|
|
gtk_widget_set_margin_top (hbox, 8);
|
|
gtk_widget_set_margin_bottom (hbox, 8);
|
|
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
|
|
|
/* Create the first image */
|
|
image = gtk_image_new_from_icon_name ("dialog-warning");
|
|
gtk_image_set_pixel_size (GTK_IMAGE (image), 48);
|
|
gtk_container_add (GTK_CONTAINER (hbox), image);
|
|
|
|
/* make image a drag source */
|
|
source = gtk_drag_source_new ();
|
|
g_signal_connect (source, "prepare", G_CALLBACK (prepare_drag), NULL);
|
|
g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
|
|
|
|
/* accept drops on image */
|
|
dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
|
|
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
|
|
|
|
/* context menu on image */
|
|
gesture = gtk_gesture_click_new ();
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
|
|
g_signal_connect (gesture, "pressed", G_CALLBACK (pressed_cb), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (gesture));
|
|
|
|
actions = G_ACTION_GROUP (g_simple_action_group_new ());
|
|
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), image);
|
|
|
|
gtk_widget_insert_action_group (image, "clipboard", actions);
|
|
|
|
g_object_unref (actions);
|
|
|
|
/* Create the second image */
|
|
image = gtk_image_new_from_icon_name ("process-stop");
|
|
gtk_image_set_pixel_size (GTK_IMAGE (image), 48);
|
|
gtk_container_add (GTK_CONTAINER (hbox), image);
|
|
|
|
/* make image a drag source */
|
|
source = gtk_drag_source_new ();
|
|
g_signal_connect (source, "prepare", G_CALLBACK (prepare_drag), NULL);
|
|
g_signal_connect (source, "drag-begin", G_CALLBACK (drag_begin), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (source));
|
|
|
|
/* accept drops on image */
|
|
dest = gtk_drop_target_new (GDK_TYPE_TEXTURE, GDK_ACTION_COPY);
|
|
g_signal_connect (dest, "drop", G_CALLBACK (drag_drop), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (dest));
|
|
|
|
/* context menu on image */
|
|
gesture = gtk_gesture_click_new ();
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY);
|
|
g_signal_connect (gesture, "pressed", G_CALLBACK (pressed_cb), image);
|
|
gtk_widget_add_controller (image, GTK_EVENT_CONTROLLER (gesture));
|
|
|
|
actions = G_ACTION_GROUP (g_simple_action_group_new ());
|
|
g_action_map_add_action_entries (G_ACTION_MAP (actions), entries, G_N_ELEMENTS (entries), image);
|
|
|
|
gtk_widget_insert_action_group (image, "clipboard", actions);
|
|
|
|
g_object_unref (actions);
|
|
}
|
|
|
|
if (!gtk_widget_get_visible (window))
|
|
gtk_widget_show (window);
|
|
else
|
|
gtk_widget_destroy (window);
|
|
|
|
return window;
|
|
}
|