Skip to content

Commit 5fb0b74

Browse files
committed
[GTK4] Implement Clipboard supporting copy and pasting to other applications
The pre-existing implementation of Clipboard for GTK4 only allowed copying and pasting within the application. Fixes #2126
1 parent 12ac449 commit 5fb0b74

File tree

25 files changed

+1998
-208
lines changed

25 files changed

+1998
-208
lines changed

bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/Clipboard.java

Lines changed: 45 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616

1717
import org.eclipse.swt.*;
18-
import org.eclipse.swt.graphics.*;
1918
import org.eclipse.swt.internal.*;
2019
import org.eclipse.swt.internal.gtk.*;
2120
import org.eclipse.swt.internal.gtk3.*;
@@ -39,15 +38,24 @@ public class Clipboard {
3938

4039
static long GTKCLIPBOARD;
4140
static long GTKPRIMARYCLIPBOARD;
41+
/**
42+
* GTK3 only
43+
*/
4244
private static long TARGET;
4345

4446
static {
45-
GTKCLIPBOARD = GTK.GTK4 ? GDK.gdk_display_get_clipboard(GDK.gdk_display_get_default()) : GTK3.gtk_clipboard_get (GDK.GDK_NONE);
46-
byte[] buffer = Converter.wcsToMbcs("PRIMARY", true);
47-
long primary = GTK.GTK4 ? 0 : GDK.gdk_atom_intern(buffer, false);
48-
GTKPRIMARYCLIPBOARD = GTK.GTK4 ? GDK.gdk_display_get_primary_clipboard(GDK.gdk_display_get_default()) : GTK3.gtk_clipboard_get(primary);
49-
buffer = Converter.wcsToMbcs("TARGETS", true);
50-
TARGET = GTK.GTK4 ? 0 : GDK.gdk_atom_intern(buffer, false);
47+
if (GTK.GTK4) {
48+
GTKCLIPBOARD = GDK.gdk_display_get_clipboard(GDK.gdk_display_get_default());
49+
GTKPRIMARYCLIPBOARD = GDK.gdk_display_get_primary_clipboard(GDK.gdk_display_get_default());
50+
TARGET = 0;
51+
} else {
52+
GTKCLIPBOARD = GTK3.gtk_clipboard_get(GDK.GDK_NONE);
53+
byte[] buffer = Converter.wcsToMbcs("PRIMARY", true);
54+
long primary = GDK.gdk_atom_intern(buffer, false);
55+
GTKPRIMARYCLIPBOARD = GTK3.gtk_clipboard_get(primary);
56+
buffer = Converter.wcsToMbcs("TARGETS", true);
57+
TARGET = GDK.gdk_atom_intern(buffer, false);
58+
}
5159
}
5260

5361
/**
@@ -188,8 +196,13 @@ public void clearContents() {
188196
*/
189197
public void clearContents(int clipboards) {
190198
checkWidget();
191-
ClipboardProxy proxy = ClipboardProxy._getInstance(display);
192-
proxy.clear(this, clipboards);
199+
if (GTK.GTK4) {
200+
ClipboardProxyGTK4 proxy = ClipboardProxyGTK4._getInstance(display);
201+
proxy.clear(this, clipboards);
202+
} else {
203+
ClipboardProxy proxy = ClipboardProxy._getInstance(display);
204+
proxy.clear(this, clipboards);
205+
}
193206
}
194207

195208
/**
@@ -290,14 +303,12 @@ public Object getContents(Transfer transfer) {
290303
* @since 3.1
291304
*/
292305
public Object getContents(Transfer transfer, int clipboards) {
293-
checkWidget();
294-
if (transfer == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
295-
296-
if(GTK.GTK4) {
297-
Object result = getContents_gtk4(transfer, clipboards);
298-
return result;
306+
if (GTK.GTK4) {
307+
return getContents_gtk4(transfer, clipboards);
299308
}
300309

310+
checkWidget();
311+
if (transfer == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
301312
long selection_data = 0;
302313
int[] typeIds = transfer.getTypeIds();
303314
boolean textTransfer = transfer.getTypeNames()[0].equals("UTF8_STRING");
@@ -327,65 +338,11 @@ public Object getContents(Transfer transfer, int clipboards) {
327338
}
328339

329340
private Object getContents_gtk4(Transfer transfer, int clipboards) {
341+
checkWidget();
342+
if (transfer == null) DND.error(SWT.ERROR_NULL_ARGUMENT);
330343

331-
long contents = GTK4.gdk_clipboard_get_content(Clipboard.GTKCLIPBOARD);
332-
if(contents == 0) return null;
333-
long value = OS.g_malloc (OS.GValue_sizeof ());
334-
C.memset (value, 0, OS.GValue_sizeof ());
335-
336-
//Pasting of text (TextTransfer/RTFTransfer)
337-
if(transfer.getTypeNames()[0].equals("text/plain") || transfer.getTypeNames()[0].equals("text/rtf")) {
338-
OS.g_value_init(value, OS.G_TYPE_STRING());
339-
if (!GTK4.gdk_content_provider_get_value (contents, value, null)) return null;
340-
long cStr = OS.g_value_get_string(value);
341-
long [] items_written = new long [1];
342-
long utf16Ptr = OS.g_utf8_to_utf16(cStr, -1, null, items_written, null);
343-
OS.g_free(cStr);
344-
if (utf16Ptr == 0) return null;
345-
int length = (int)items_written[0];
346-
char[] buffer = new char[length];
347-
C.memmove(buffer, utf16Ptr, length * 2);
348-
OS.g_free(utf16Ptr);
349-
String str = new String(buffer);
350-
if(transfer.getTypeNames()[0].equals("text/rtf") && !str.contains("{\\rtf1")) {
351-
return null;
352-
}
353-
if(transfer.getTypeNames()[0].equals("text/plain") && str.contains("{\\rtf1")){
354-
return null;
355-
}
356-
return str;
357-
}
358-
//Pasting of Image
359-
if(transfer.getTypeIds()[0] == (int)GDK.GDK_TYPE_PIXBUF()) {
360-
ImageData imgData = null;
361-
OS.g_value_init(value, GDK.GDK_TYPE_PIXBUF());
362-
if (!GTK4.gdk_content_provider_get_value (contents, value, null)) return null;
363-
long pixbufObj = OS.g_value_get_object(value);
364-
if (pixbufObj != 0) {
365-
Image img = Image.gtk_new_from_pixbuf(Display.getCurrent(), SWT.BITMAP, pixbufObj);
366-
imgData = img.getImageData();
367-
img.dispose();
368-
}
369-
return imgData;
370-
}
371-
//Pasting of HTML
372-
if(transfer.getTypeNames()[0].equals("text/html")) {
373-
OS.g_value_init(value, OS.G_TYPE_STRING());
374-
if (!GTK4.gdk_content_provider_get_value (contents, value, null)) return null;
375-
long cStr = OS.g_value_get_string(value);
376-
long [] items_written = new long [1];
377-
long utf16Ptr = OS.g_utf8_to_utf16(cStr, -1, null, items_written, null);
378-
OS.g_free(cStr);
379-
if (utf16Ptr == 0) return null;
380-
int length = (int)items_written[0];
381-
char[] buffer = new char[length];
382-
C.memmove(buffer, utf16Ptr, length * 2);
383-
OS.g_free(utf16Ptr);
384-
String str = new String(buffer);
385-
return str;
386-
}
387-
//TODO: [GTK4] Other cases
388-
return null;
344+
ClipboardProxyGTK4 proxy = ClipboardProxyGTK4._getInstance(display);
345+
return proxy.getData(this, transfer, clipboards);
389346
}
390347

391348
/**
@@ -525,9 +482,16 @@ public void setContents(Object[] data, Transfer[] dataTypes, int clipboards) {
525482
DND.error(SWT.ERROR_INVALID_ARGUMENT);
526483
}
527484
}
528-
ClipboardProxy proxy = ClipboardProxy._getInstance(display);
529-
if (!proxy.setData(this, data, dataTypes, clipboards)) {
530-
DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD);
485+
if (GTK.GTK4) {
486+
ClipboardProxyGTK4 proxy = ClipboardProxyGTK4._getInstance(display);
487+
if (!proxy.setData(this, data, dataTypes, clipboards)) {
488+
DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD);
489+
}
490+
} else {
491+
ClipboardProxy proxy = ClipboardProxy._getInstance(display);
492+
if (!proxy.setData(this, data, dataTypes, clipboards)) {
493+
DND.error(DND.ERROR_CANNOT_SET_CLIPBOARD);
494+
}
531495
}
532496
}
533497

@@ -623,8 +587,10 @@ public TransferData[] getAvailableTypes(int clipboards) {
623587
public String[] getAvailableTypeNames() {
624588
checkWidget();
625589
if(GTK.GTK4) {
590+
// TODO make sure this code works with new GTK4 impl
626591
long formatsCStr = GTK4.gdk_content_formats_to_string(GTK4.gdk_clipboard_get_formats(Clipboard.GTKCLIPBOARD));
627592
String formatsStr = Converter.cCharPtrToJavaString(formatsCStr, true);
593+
System.out.println(formatsStr);
628594
String[] types = formatsStr.split(" ");
629595
return types;
630596
}
@@ -673,6 +639,7 @@ private int[] getAvailableClipboardTypes () {
673639
return gtk3_getAvailableTypes(GTKCLIPBOARD);
674640
}
675641

642+
// TODO make sure this code works with new GTK4 impl
676643
private int[] gtk4_getAvailableTypes(long clipboard) {
677644
long formats = GTK4.gdk_clipboard_get_formats(clipboard);
678645
long[] n_gtypes = new long[1];
@@ -723,4 +690,6 @@ long gtk_clipboard_wait_for_contents(long clipboard, long target) {
723690
}
724691
return selection_data;
725692
}
693+
694+
726695
}

bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/ClipboardProxy.java

Lines changed: 14 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2000, 2017 IBM Corporation and others.
2+
* Copyright (c) 2000, 2025 IBM Corporation and others.
33
*
44
* This program and the accompanying materials
55
* are made available under the terms of the Eclipse Public License 2.0
@@ -14,14 +14,15 @@
1414
package org.eclipse.swt.dnd;
1515

1616

17-
import org.eclipse.swt.*;
18-
import org.eclipse.swt.graphics.*;
1917
import org.eclipse.swt.internal.*;
2018
import org.eclipse.swt.internal.gtk.*;
2119
import org.eclipse.swt.internal.gtk3.*;
22-
import org.eclipse.swt.internal.gtk4.*;
2320
import org.eclipse.swt.widgets.*;
2421

22+
/**
23+
* Clipboard proxy used to copy data to the clipboard in GTK3 only
24+
* @see ClipboardProxyGTK4 the GTK4 version
25+
*/
2526
class ClipboardProxy {
2627
/* Data is not flushed to the clipboard immediately.
2728
* This class will remember the data and provide it when requested.
@@ -31,7 +32,7 @@ class ClipboardProxy {
3132
Object[] primaryClipboardData;
3233
Transfer[] primaryClipboardDataTypes;
3334

34-
long clipboardOwner = GTK.GTK4 ? GTK4.gtk_window_new() : GTK3.gtk_window_new(GTK.GTK_WINDOW_TOPLEVEL);
35+
long clipboardOwner = GTK3.gtk_window_new(GTK.GTK_WINDOW_TOPLEVEL);
3536

3637
Display display;
3738
Clipboard activeClipboard = null;
@@ -42,6 +43,9 @@ class ClipboardProxy {
4243
static String ID = "CLIPBOARD PROXY OBJECT"; //$NON-NLS-1$
4344

4445
static ClipboardProxy _getInstance(final Display display) {
46+
if (GTK.GTK4) {
47+
throw new UnsupportedOperationException("Illegal attempt to use GTK3 ClipboardProxy on GTK4");
48+
}
4549
ClipboardProxy proxy = (ClipboardProxy) display.getData(ID);
4650
if (proxy != null) return proxy;
4751
proxy = new ClipboardProxy(display);
@@ -55,7 +59,7 @@ static ClipboardProxy _getInstance(final Display display) {
5559
return proxy;
5660
}
5761

58-
ClipboardProxy(Display display) {
62+
private ClipboardProxy(Display display) {
5963
this.display = display;
6064
getFunc = new Callback( this, "getFunc", 4); //$NON-NLS-1$
6165
clearFunc = new Callback( this, "clearFunc", 2); //$NON-NLS-1$
@@ -71,11 +75,7 @@ void clear (Clipboard owner, int clipboards) {
7175
}
7276

7377
void gtk_gdk_clipboard_clear(long clipboard) {
74-
if (GTK.GTK4) {
75-
GDK.gdk_clipboard_set_content(clipboard, 0);
76-
} else {
77-
GTK3.gtk_clipboard_clear(clipboard);
78-
}
78+
GTK3.gtk_clipboard_clear(clipboard);
7979
}
8080

8181
long clearFunc(long clipboard,long user_data_or_owner){
@@ -95,10 +95,10 @@ long clearFunc(long clipboard,long user_data_or_owner){
9595
void dispose () {
9696
if (display == null) return;
9797
if (activeClipboard != null) {
98-
if(!GTK.GTK4) GTK3.gtk_clipboard_store(Clipboard.GTKCLIPBOARD);
98+
GTK3.gtk_clipboard_store(Clipboard.GTKCLIPBOARD);
9999
}
100100
if (activePrimaryClipboard != null) {
101-
if(!GTK.GTK4) GTK3.gtk_clipboard_store(Clipboard.GTKPRIMARYCLIPBOARD);
101+
GTK3.gtk_clipboard_store(Clipboard.GTKPRIMARYCLIPBOARD);
102102
}
103103
display = null;
104104
if (getFunc != null ) getFunc.dispose();
@@ -110,11 +110,7 @@ void dispose () {
110110
primaryClipboardData = null;
111111
primaryClipboardDataTypes = null;
112112
if (clipboardOwner != 0) {
113-
if (GTK.GTK4) {
114-
GTK4.gtk_window_destroy(clipboardOwner);
115-
} else {
116-
GTK3.gtk_widget_destroy(clipboardOwner);
117-
}
113+
GTK3.gtk_widget_destroy(clipboardOwner);
118114
}
119115
clipboardOwner = 0;
120116
}
@@ -148,9 +144,6 @@ long getFunc(long clipboard, long selection_data, long info, long user_data_or_o
148144
}
149145

150146
boolean setData(Clipboard owner, Object[] data, Transfer[] dataTypes, int clipboards) {
151-
152-
if(GTK.GTK4) return setData_gtk4(owner, data, dataTypes, clipboards);
153-
154147
GtkTargetEntry[] entries = new GtkTargetEntry [0];
155148
long pTargetsList = 0;
156149
try {
@@ -219,66 +212,4 @@ boolean setData(Clipboard owner, Object[] data, Transfer[] dataTypes, int clipbo
219212
if (pTargetsList != 0) OS.g_free(pTargetsList);
220213
}
221214
}
222-
223-
private boolean setData_gtk4(Clipboard owner, Object[] data, Transfer[] dataTypes, int clipboards) {
224-
boolean result = false;
225-
long [] providers = new long[0];
226-
for (int i = 0; i < dataTypes.length; i++) {
227-
Transfer transfer = dataTypes[i];
228-
String[] typeNames = transfer.getTypeNames();
229-
//Build the GdkContentProvider for each and store in array
230-
long provider = setProviderFromType(typeNames[0], data[i]);
231-
if(provider != 0) {
232-
long[] tmp = new long [providers.length + 1];
233-
System.arraycopy(providers, 0, tmp, 0, providers.length);
234-
tmp[providers.length] = provider;
235-
providers = tmp;
236-
}
237-
}
238-
//Build the GdkContentProvider Union
239-
if (providers.length == 0) return false;
240-
long union = GTK4.gdk_content_provider_new_union(providers, providers.length);
241-
242-
if ((clipboards & DND.CLIPBOARD) != 0){
243-
clipboardData = data;
244-
clipboardDataTypes = dataTypes;
245-
result = GTK4.gdk_clipboard_set_content(Clipboard.GTKCLIPBOARD, union);
246-
activeClipboard = owner;
247-
}
248-
return result;
249-
}
250-
251-
private long setProviderFromType(String string, Object data) {
252-
long provider = 0;
253-
254-
if (data == null ) SWT.error(SWT.ERROR_NULL_ARGUMENT);
255-
else {
256-
if(string.equals("text/plain") || string.equals("text/rtf")) {
257-
long value = OS.g_malloc (OS.GValue_sizeof());
258-
C.memset (value, 0, OS.GValue_sizeof ());
259-
OS.g_value_init(value, OS.G_TYPE_STRING());
260-
OS.g_value_set_string(value, Converter.javaStringToCString((String)data));
261-
provider = GTK4.gdk_content_provider_new_for_value(value);
262-
}
263-
if(string.equals("PIXBUF")) {
264-
if(!(data instanceof ImageData)) DND.error(DND.ERROR_INVALID_DATA);
265-
ImageData imgData = (ImageData)data;
266-
Image image = new Image(Display.getCurrent(), imgData);
267-
long pixbuf = ImageList.createPixbuf(image);
268-
if (pixbuf != 0) {
269-
provider = GTK4.gdk_content_provider_new_typed(GDK.GDK_TYPE_PIXBUF(), pixbuf);
270-
}
271-
image.dispose();
272-
}
273-
if(string.equals("text/html")) {
274-
long value = OS.g_malloc (OS.GValue_sizeof());
275-
C.memset (value, 0, OS.GValue_sizeof ());
276-
OS.g_value_init(value, OS.G_TYPE_STRING());
277-
OS.g_value_set_string(value, Converter.javaStringToCString((String)data));
278-
provider = GTK4.gdk_content_provider_new_for_value(value);
279-
}
280-
281-
}
282-
return provider;
283-
}
284215
}

0 commit comments

Comments
 (0)