/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.swt.dnd;
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.Callback;
import org.eclipse.swt.internal.Converter;
import org.eclipse.swt.internal.gtk.GtkSelectionData;
import org.eclipse.swt.internal.gtk.GtkTargetEntry;
import org.eclipse.swt.internal.gtk.OS;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
class ClipboardProxy {
/* Data is not flushed to the clipboard immediately.
* This class will remember the data and provide it when requested.
*/
Object[] clipboardData;
Transfer[] clipboardDataTypes;
Object[] primaryClipboardData;
Transfer[] primaryClipboardDataTypes;
long /*int*/ clipboardOwner = OS.gtk_window_new(0);
Display display;
Clipboard activeClipboard = null;
Clipboard activePrimaryClipboard = null;
Callback getFunc;
Callback clearFunc;
static String ID = "CLIPBOARD PROXY OBJECT"; //$NON-NLS-1$
static ClipboardProxy _getInstance(final Display display) {
ClipboardProxy proxy = (ClipboardProxy) display.getData(ID);
if (proxy != null) return proxy;
proxy = new ClipboardProxy(display);
display.setData(ID, proxy);
display.addListener(SWT.Dispose, new Listener() {
public void handleEvent(Event event) {
ClipboardProxy clipbordProxy = (ClipboardProxy)display.getData(ID);
if (clipbordProxy == null) return;
display.setData(ID, null);
clipbordProxy.dispose();
}
});
return proxy;
}
ClipboardProxy(Display display) {
this.display = display;
getFunc = new Callback( this, "getFunc", 4); //$NON-NLS-1$
if (getFunc.getAddress() == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
clearFunc = new Callback( this, "clearFunc", 2); //$NON-NLS-1$
if (clearFunc.getAddress() == 0) SWT.error(SWT.ERROR_NO_MORE_CALLBACKS);
}
void clear (Clipboard owner, int clipboards) {
if ((clipboards & DND.CLIPBOARD) != 0 && activeClipboard == owner) {
OS.gtk_clipboard_clear(Clipboard.GTKCLIPBOARD);
}
if ((clipboards & DND.SELECTION_CLIPBOARD) != 0 && activePrimaryClipboard == owner) {
OS.gtk_clipboard_clear(Clipboard.GTKPRIMARYCLIPBOARD);
}
}
long /*int*/ clearFunc(long /*int*/ clipboard,long /*int*/ user_data_or_owner){
if (clipboard == Clipboard.GTKCLIPBOARD) {
activeClipboard = null;
clipboardData = null;
clipboardDataTypes = null;
}
if (clipboard == Clipboard.GTKPRIMARYCLIPBOARD) {
activePrimaryClipboard = null;
primaryClipboardData = null;
primaryClipboardDataTypes = null;
}
return 1;
}
void dispose () {
if (display == null) return;
if (activeClipboard != null) {
OS.gtk_clipboard_store(Clipboard.GTKCLIPBOARD);
}
if (activePrimaryClipboard != null) {
OS.gtk_clipboard_store(Clipboard.GTKPRIMARYCLIPBOARD);
}
display = null;
if (getFunc != null ) getFunc.dispose();
getFunc = null;
if (clearFunc != null) clearFunc.dispose();
clearFunc = null;
clipboardData = null;
clipboardDataTypes = null;
primaryClipboardData = null;
primaryClipboardDataTypes = null;
if (clipboardOwner != 0) OS.gtk_widget_destroy (clipboardOwner);
clipboardOwner = 0;
}
/**
* This function provides the data to the clipboard on request.
* When this clipboard is disposed, the data will no longer be available.
*/
long /*int*/ getFunc(long /*int*/ clipboard, long /*int*/ selection_data, long /*int*/ info, long /*int*/ user_data_or_owner){
if (selection_data == 0) return 0;
long /*int*/ target;
if (OS.GTK_VERSION >= OS.VERSION(2, 14, 0)) {
target = OS.gtk_selection_data_get_target(selection_data);
} else {
GtkSelectionData selectionData = new GtkSelectionData();
OS.memmove(selectionData, selection_data, GtkSelectionData.sizeof);
target = selectionData.target;
}
TransferData tdata = new TransferData();
tdata.type = target;
Transfer[] types = (clipboard == Clipboard.GTKCLIPBOARD) ? clipboardDataTypes : primaryClipboardDataTypes;
int index = -1;
for (int i = 0; i < types.length; i++) {
if (types[i].isSupportedType(tdata)) {
index = i;
break;
}
}
if (index == -1) return 0;
Object[] data = (clipboard == Clipboard.GTKCLIPBOARD) ? clipboardData : primaryClipboardData;
types[index].javaToNative(data[index], tdata);
if (tdata.format < 8 || tdata.format % 8 != 0) {
return 0;
}
OS.gtk_selection_data_set(selection_data, tdata.type, tdata.format, tdata.pValue, tdata.length);
OS.g_free(tdata.pValue);
return 1;
}
boolean setData(Clipboard owner, Object[] data, Transfer[] dataTypes, int clipboards) {
GtkTargetEntry[] entries = new GtkTargetEntry [0];
long /*int*/ pTargetsList = 0;
try {
for (int i = 0; i < dataTypes.length; i++) {
Transfer transfer = dataTypes[i];
int[] typeIds = transfer.getTypeIds();
String[] typeNames = transfer.getTypeNames();
for (int j = 0; j < typeIds.length; j++) {
GtkTargetEntry entry = new GtkTargetEntry();
entry.info = typeIds[j];
byte[] buffer = Converter.wcsToMbcs(null, typeNames[j], true);
long /*int*/ pName = OS.g_malloc(buffer.length);
OS.memmove(pName, buffer, buffer.length);
entry.target = pName;
GtkTargetEntry[] tmp = new GtkTargetEntry [entries.length + 1];
System.arraycopy(entries, 0, tmp, 0, entries.length);
tmp[entries.length] = entry;
entries = tmp;
}
}
pTargetsList = OS.g_malloc(GtkTargetEntry.sizeof * entries.length);
int offset = 0;
for (int i = 0; i < entries.length; i++) {
OS.memmove(pTargetsList + offset, entries[i], GtkTargetEntry.sizeof);
offset += GtkTargetEntry.sizeof;
}
if ((clipboards & DND.CLIPBOARD) != 0) {
clipboardData = data;
clipboardDataTypes = dataTypes;
long /*int*/ getFuncProc = getFunc.getAddress();
long /*int*/ clearFuncProc = clearFunc.getAddress();
/*
* Feature in GTK. When the contents are set again, clipboard_set_with_data()
* invokes clearFunc and then, getFunc is not sequentially called.
* If we clear the content before calling set_with_data(), then there is a fair
* chance for other apps like Klipper to claim the ownership of the clipboard.
* The fix is to make sure that the content is not cleared before the data is
* set again. GTK does not invoke clearFunc for clipboard_set_with_owner()
* though we set the data again. So, this API has to be used whenever we
* are setting the contents.
*/
if (!OS.gtk_clipboard_set_with_owner (Clipboard.GTKCLIPBOARD, pTargetsList, entries.length, getFuncProc, clearFuncProc, clipboardOwner)) {
return false;
}
OS.gtk_clipboard_set_can_store(Clipboard.GTKCLIPBOARD, 0, 0);
activeClipboard = owner;
}
if ((clipboards & DND.SELECTION_CLIPBOARD) != 0) {
primaryClipboardData = data;
primaryClipboardDataTypes = dataTypes;
long /*int*/ getFuncProc = getFunc.getAddress();
long /*int*/ clearFuncProc = clearFunc.getAddress();
if (!OS.gtk_clipboard_set_with_owner (Clipboard.GTKPRIMARYCLIPBOARD, pTargetsList, entries.length, getFuncProc, clearFuncProc, clipboardOwner)) {
return false;
}
OS.gtk_clipboard_set_can_store(Clipboard.GTKPRIMARYCLIPBOARD, 0, 0);
activePrimaryClipboard = owner;
}
return true;
} finally {
for (int i = 0; i < entries.length; i++) {
GtkTargetEntry entry = entries[i];
if( entry.target != 0) OS.g_free(entry.target);
}
if (pTargetsList != 0) OS.g_free(pTargetsList);
}
}
}