gtk2/gtk/gtkclipboard-wayland.c
Jasper St. Pierre f4b212abd4 wayland: Add some dumb support for the TARGETS selection
The way that GtkTextView et al pop up their context menu is to first
query to see if the clipboard has some text, and if so, enable the Paste
menu item. But since the Wayland backend hasn't had the greatest
selection and clipboard code, the callback for the clipboard got dropped
on the floor.

Add some simple code to respond to the TARGETS selection.

This makes right-clicking on a GtkTextView work fine.
2014-07-03 13:29:14 -04:00

366 lines
12 KiB
C

/* GTK - The GIMP Toolkit
* Copyright (C) 2000 Red Hat, Inc.
* Copyright (C) 2004 Nokia Corporation
* Copyright (C) 2006-2008 Imendio AB
* Copyright (C) 2011-2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "config.h"
#include "gtkclipboard-waylandprivate.h"
#ifdef GDK_WINDOWING_WAYLAND
#include <string.h>
#include "gtkmain.h"
#include "gtkmarshalers.h"
#include "gtkintl.h"
#include "gtkselectionprivate.h"
static void gtk_clipboard_wayland_owner_change (GtkClipboard *clipboard,
GdkEventOwnerChange *event);
static gboolean gtk_clipboard_wayland_set_contents (GtkClipboard *clipboard,
const GtkTargetEntry *targets,
guint n_targets,
GtkClipboardGetFunc get_func,
GtkClipboardClearFunc clear_func,
gpointer user_data,
gboolean have_owner);
static void gtk_clipboard_wayland_clear (GtkClipboard *clipboard);
static void gtk_clipboard_wayland_request_contents (GtkClipboard *clipboard,
GdkAtom target,
GtkClipboardReceivedFunc callback,
gpointer user_data);
static void gtk_clipboard_wayland_set_can_store (GtkClipboard *clipboard,
const GtkTargetEntry *targets,
gint n_targets);
static void gtk_clipboard_wayland_store (GtkClipboard *clipboard);
G_DEFINE_TYPE (GtkClipboardWayland, gtk_clipboard_wayland, GTK_TYPE_CLIPBOARD);
static void
gtk_clipboard_wayland_class_init (GtkClipboardWaylandClass *klass)
{
GtkClipboardClass *clipboard_class = GTK_CLIPBOARD_CLASS (klass);
clipboard_class->set_contents = gtk_clipboard_wayland_set_contents;
clipboard_class->clear = gtk_clipboard_wayland_clear;
clipboard_class->request_contents = gtk_clipboard_wayland_request_contents;
clipboard_class->set_can_store = gtk_clipboard_wayland_set_can_store;
clipboard_class->store = gtk_clipboard_wayland_store;
clipboard_class->owner_change = gtk_clipboard_wayland_owner_change;
}
static void
gtk_clipboard_wayland_init (GtkClipboardWayland *clipboard)
{
}
struct _SetContentClosure {
GtkClipboard *clipboard;
GtkClipboardGetFunc get_func;
GtkClipboardClearFunc clear_func;
guint info;
gboolean have_owner;
gpointer userdata;
GtkTargetPair *targets;
gint n_targets;
};
static gchar *
_offer_cb (GdkDevice *device,
const gchar *mime_type,
gssize *len,
gpointer userdata)
{
SetContentClosure *closure = (SetContentClosure *)userdata;
GtkSelectionData selection_data = { 0, };
guint info = 0;
gint i;
selection_data.target = gdk_atom_intern (mime_type, FALSE);
for (i = 0; i < closure->n_targets; i++)
{
if (closure->targets[i].target == selection_data.target)
{
info = closure->targets[i].info;
break;
}
}
closure->get_func (closure->clipboard,
&selection_data,
info,
closure->userdata);
*len = gtk_selection_data_get_length (&selection_data);
/* The caller of this callback will free this data - the GtkClipboardGetFunc
* uses gtk_selection_data_set which copies*/
return (gchar *)selection_data.data;
}
static void
clipboard_owner_destroyed (gpointer data,
GObject *owner)
{
GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (data);
GtkClipboard *gtkclipboard = GTK_CLIPBOARD (data);
SetContentClosure *last_closure = clipboard->last_closure;
last_closure->userdata = NULL;
last_closure->get_func = NULL;
last_closure->clear_func = NULL;
last_closure->have_owner = FALSE;
gtk_clipboard_clear (gtkclipboard);
}
static gboolean
gtk_clipboard_wayland_set_contents (GtkClipboard *gtkclipboard,
const GtkTargetEntry *targets,
guint n_targets,
GtkClipboardGetFunc get_func,
GtkClipboardClearFunc clear_func,
gpointer user_data,
gboolean have_owner)
{
GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (gtkclipboard);
GdkDeviceManager *device_manager;
GdkDevice *device;
gint i, j;
gchar **mimetypes;
SetContentClosure *closure, *last_closure;
if (gtkclipboard->selection != GDK_SELECTION_CLIPBOARD)
return FALSE;
last_closure = clipboard->last_closure;
if (!last_closure ||
(!last_closure->have_owner && have_owner) ||
(last_closure->userdata != user_data)) {
gtk_clipboard_clear (gtkclipboard);
closure = g_new0 (SetContentClosure, 1);
closure->clipboard = gtkclipboard;
closure->userdata = user_data;
closure->have_owner = have_owner;
if (have_owner)
g_object_weak_ref (G_OBJECT (user_data), clipboard_owner_destroyed, clipboard);
} else {
closure = last_closure;
g_free (closure->targets);
}
closure->get_func = get_func;
closure->clear_func = clear_func;
closure->targets = g_new0 (GtkTargetPair, n_targets);
device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (device_manager);
mimetypes = g_new (gchar *, n_targets);
for (i = 0, j = 0; i < n_targets; i++)
{
if (strcmp(targets[i].target, "COMPOUND_TEXT") == 0)
continue;
if (strcmp(targets[i].target, "UTF8_STRING") == 0)
continue;
if (strcmp(targets[i].target, "TEXT") == 0)
continue;
if (strcmp(targets[i].target, "STRING") == 0)
continue;
if (strcmp(targets[i].target, "GTK_TEXT_BUFFER_CONTENTS") == 0)
continue;
mimetypes[j] = targets[i].target;
closure->targets[j].target = gdk_atom_intern (targets[i].target, FALSE);
closure->targets[j].flags = targets[i].flags;
closure->targets[j].info = targets[i].info;
j++;
}
closure->n_targets = j;
gdk_wayland_device_offer_selection_content (device,
(const gchar **)mimetypes,
j,
_offer_cb,
closure);
clipboard->last_closure = closure;
g_free (mimetypes);
return TRUE;
}
static void
gtk_clipboard_wayland_clear (GtkClipboard *gtkclipboard)
{
GtkClipboardWayland *clipboard = GTK_CLIPBOARD_WAYLAND (gtkclipboard);
GdkDeviceManager *device_manager;
GdkDevice *device;
if (!clipboard->last_closure)
return;
device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (device_manager);
gdk_wayland_device_clear_selection_content (device);
if (clipboard->last_closure->clear_func)
{
clipboard->last_closure->clear_func (gtkclipboard,
clipboard->last_closure->userdata);
}
if (clipboard->last_closure->have_owner)
g_object_weak_unref (G_OBJECT (clipboard->last_closure->userdata),
clipboard_owner_destroyed, clipboard);
g_free (clipboard->last_closure->targets);
g_free (clipboard->last_closure);
clipboard->last_closure = NULL;
}
typedef struct {
GtkClipboard *clipboard;
GCallback cb;
gpointer userdata;
GdkAtom target;
} ClipboardRequestClosure;
static void
_request_generic_cb (GdkDevice *device,
const gchar *data,
gsize len,
gpointer userdata)
{
ClipboardRequestClosure *closure = (ClipboardRequestClosure *)userdata;
GtkClipboardReceivedFunc cb = (GtkClipboardReceivedFunc)closure->cb;
GtkSelectionData selection_data;
selection_data.selection = GDK_SELECTION_CLIPBOARD;
selection_data.target = closure->target;
selection_data.type = closure->target;
selection_data.length = len;
selection_data.data = (guchar *)data;
cb (closure->clipboard, &selection_data, closure->userdata);
g_free (closure);
}
static void
gtk_clipboard_wayland_request_contents (GtkClipboard *gtkclipboard,
GdkAtom target,
GtkClipboardReceivedFunc callback,
gpointer user_data)
{
GdkDeviceManager *device_manager;
GdkDevice *device;
ClipboardRequestClosure *closure;
gchar *mime_type;
device_manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (device_manager);
if (target == gdk_atom_intern_static_string ("TARGETS"))
{
GtkSelectionData selection_data;
int n_atoms;
GdkAtom *atoms;
selection_data.selection = GDK_NONE;
selection_data.format = 32;
selection_data.type = GDK_SELECTION_TYPE_ATOM;
n_atoms = gdk_wayland_device_get_selection_type_atoms (device, &atoms);
selection_data.length = n_atoms;
selection_data.data = atoms;
callback (gtkclipboard, &selection_data, user_data);
return;
}
/* When GTK+ requests text, it tries UTF8_STRING first and then
* falls back to COMPOUND_TEXT and then STRING. We rewrite
* UTF8_STRING to text/plain;charset=utf-8, and if that doesn't
* work, just fail the fallback targets. Maybe we could do this in
* a generic way that just compares the target against the targets
* advertised by the data offer. */
if (target == gdk_atom_intern_static_string ("UTF8_STRING"))
target = gdk_atom_intern_static_string ("text/plain;charset=utf-8");
else if (target == gdk_atom_intern_static_string ("COMPOUND_TEXT") ||
target == GDK_TARGET_STRING)
{
GtkSelectionData selection_data;
selection_data.selection = GDK_NONE;
selection_data.target = GDK_NONE;
selection_data.type = GDK_NONE;
selection_data.length = 0;
selection_data.data = NULL;
callback (gtkclipboard, &selection_data, user_data);
return;
}
closure = g_new0 (ClipboardRequestClosure, 1);
closure->clipboard = gtkclipboard;
closure->cb = (GCallback)callback;
closure->userdata = user_data;
closure->target = target;
/* TODO: Do we need to check that target is valid ? */
mime_type = gdk_atom_name (target);
gdk_wayland_device_request_selection_content (device,
mime_type,
_request_generic_cb,
closure);
g_free (mime_type);
}
static void
gtk_clipboard_wayland_owner_change (GtkClipboard *clipboard,
GdkEventOwnerChange *event)
{
}
static void
gtk_clipboard_wayland_set_can_store (GtkClipboard *clipboard,
const GtkTargetEntry *targets,
gint n_targets)
{
/* FIXME: Implement */
}
static void
gtk_clipboard_wayland_store (GtkClipboard *clipboard)
{
/* FIXME: Implement */
}
#endif /* GDK_WINDOWING_WAYLAND */