1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package com.sun.glass.ui.swt; 26 27 import com.sun.glass.ui.Pixels; 28 import com.sun.glass.ui.Clipboard; 29 import com.sun.glass.ui.SystemClipboard; 30 31 import java.nio.IntBuffer; 32 import java.util.HashMap; 33 import java.util.Set; 34 35 import org.eclipse.swt.*; 36 import org.eclipse.swt.dnd.*; 37 import org.eclipse.swt.graphics.*; 38 import org.eclipse.swt.widgets.*; 39 40 class SWTClipboard extends SystemClipboard { 41 org.eclipse.swt.dnd.Clipboard clipboard; 42 static final String CLIPBOARD_KEY = "SWTClipboard"; 43 44 //TODO - temporary code to enable multiple transfers on Windows only 45 static final boolean MULTIPLE_TRANSFERS = SWT.getPlatform().equals("win32"); 46 47 // Define local constants to avoid name conflicts 48 static final int DROP_NONE = org.eclipse.swt.dnd.DND.DROP_NONE; 49 static final int DROP_COPY = org.eclipse.swt.dnd.DND.DROP_COPY; 50 static final int DROP_MOVE = org.eclipse.swt.dnd.DND.DROP_MOVE; 51 static final int DROP_LINK = org.eclipse.swt.dnd.DND.DROP_LINK; 52 53 private static final boolean MUTIPLE_TRANSFERS = false; 54 55 // Define standard transfer types including custom transfers 56 static Transfer [] StandardTransfers = new Transfer [] { 57 TextTransfer.getInstance(), 58 RTFTransfer.getInstance(), 59 HTMLTransfer.getInstance(), 60 URLTransfer.getInstance(), 61 ImageTransfer.getInstance(), 62 FileTransfer.getInstance(), 63 }; 64 static Transfer [] CustomTransfers = new Transfer [0]; 65 66 public SWTClipboard(String name) { 67 super(name); 68 //TODO - implement selection clipboard for Linux 69 if (name.equals(SYSTEM)) { 70 Display display = Display.getDefault(); 71 clipboard = (org.eclipse.swt.dnd.Clipboard) display.getData(CLIPBOARD_KEY); 72 if (clipboard == null) { 73 clipboard = new org.eclipse.swt.dnd.Clipboard(display); 74 display.setData(CLIPBOARD_KEY, clipboard); 75 display.disposeExec(() -> clipboard.dispose()); 76 } 77 } 78 } 79 80 static Transfer [] getAllTransfers () { 81 Transfer [] transfers = new Transfer[StandardTransfers.length + CustomTransfers.length]; 82 System.arraycopy(StandardTransfers, 0, transfers, 0, StandardTransfers.length); 83 System.arraycopy(CustomTransfers, 0, transfers, StandardTransfers.length, CustomTransfers.length); 84 return transfers; 85 } 86 87 static Transfer getCustomTransfer(String mime) { 88 for (int i=0; i<CustomTransfers.length; i++) { 89 if (((CustomTransfer)CustomTransfers[i]).getMime().equals(mime)) { 90 return CustomTransfers[i]; 91 } 92 } 93 Transfer transfer = new CustomTransfer (mime, mime); 94 Transfer [] newCustom = new Transfer [CustomTransfers.length + 1]; 95 System.arraycopy(CustomTransfers, 0, newCustom, 0, CustomTransfers.length); 96 newCustom[CustomTransfers.length] = transfer; 97 CustomTransfers = newCustom; 98 return transfer; 99 } 100 101 static Transfer [] getTransferTypes(String [] mimeTypes) { 102 int count= 0; 103 Transfer [] transfers = new Transfer [mimeTypes.length]; 104 for (int i=0; i<mimeTypes.length; i++) { 105 Transfer transfer = getTransferType(mimeTypes[i]); 106 if (transfer != null) transfers [count++] = transfer; 107 } 108 if (count != mimeTypes.length) { 109 Transfer [] newTransfers = new Transfer[count]; 110 System.arraycopy(transfers, 0, newTransfers, 0, count); 111 transfers = newTransfers; 112 } 113 return transfers; 114 } 115 116 //TODO - make this a lookup table 117 static String getMime(TransferData data) { 118 if (TextTransfer.getInstance().isSupportedType(data)) return TEXT_TYPE; 119 if (RTFTransfer.getInstance().isSupportedType(data)) return RTF_TYPE; 120 if (HTMLTransfer.getInstance().isSupportedType(data)) return HTML_TYPE; 121 if (URLTransfer.getInstance().isSupportedType(data)) return URI_TYPE; 122 if (ImageTransfer.getInstance().isSupportedType(data)) return RAW_IMAGE_TYPE; 123 if (FileTransfer.getInstance().isSupportedType(data)) return FILE_LIST_TYPE; 124 for (int i=0; i<CustomTransfers.length; i++) { 125 if (CustomTransfers[i].isSupportedType(data)){ 126 return ((CustomTransfer)CustomTransfers[i]).getMime(); 127 } 128 } 129 return null; 130 } 131 132 //TODO - make this a lookup table 133 static String getMime(Transfer transfer) { 134 if (transfer.equals(TextTransfer.getInstance())) return TEXT_TYPE; 135 if (transfer.equals(RTFTransfer.getInstance())) return RTF_TYPE; ; 136 if (transfer.equals( HTMLTransfer.getInstance())) return HTML_TYPE; 137 if (transfer.equals(URLTransfer.getInstance())) return URI_TYPE; 138 if (transfer.equals( ImageTransfer.getInstance())) return RAW_IMAGE_TYPE; 139 if (transfer.equals(FileTransfer.getInstance())) return FILE_LIST_TYPE; 140 //if (mime.equals(FileTransfer.getInstance()) return "java.file-list"; 141 if (transfer instanceof CustomTransfer) { 142 return ((CustomTransfer)transfer).getMime(); 143 } 144 return null; 145 } 146 147 static String [] getMimes(TransferData [] transfers) { 148 int count= 0; 149 String [] result = new String [transfers.length]; 150 for (int i=0; i<transfers.length; i++) { 151 result [count++] = getMime (transfers [i]); 152 } 153 if (count != result.length) { 154 String [] newResult = new String[count]; 155 System.arraycopy(result, 0, newResult, 0, count); 156 result = newResult; 157 } 158 return result; 159 } 160 161 static String [] getMimes(Transfer [] transfers, TransferData data) { 162 int count= 0; 163 String [] result = new String [transfers.length]; 164 for (int i=0; i<transfers.length; i++) { 165 if (transfers[i].isSupportedType(data)) { 166 result [count++] = getMime (transfers [i]); 167 } 168 } 169 if (count != result.length) { 170 String [] newResult = new String[count]; 171 System.arraycopy(result, 0, newResult, 0, count); 172 result = newResult; 173 } 174 return result; 175 } 176 177 //TODO - make this a lookup table 178 static Transfer getTransferType(String mime) { 179 if (mime.equals(TEXT_TYPE)) return TextTransfer.getInstance(); 180 if (mime.equals(RTF_TYPE)) return RTFTransfer.getInstance(); 181 if (mime.equals(HTML_TYPE)) return HTMLTransfer.getInstance(); 182 if (mime.equals(URI_TYPE)) return URLTransfer.getInstance(); 183 if (mime.equals(RAW_IMAGE_TYPE)) return ImageTransfer.getInstance(); 184 if (mime.equals(FILE_LIST_TYPE)) {// || mime.equals("java.file-list")) { 185 return FileTransfer.getInstance(); 186 } 187 return getCustomTransfer(mime); 188 } 189 190 //TODO - make this a lookup table 191 static Object getData(String mime, TransferData data) { 192 if (mime.equals(TEXT_TYPE)) return TextTransfer.getInstance().nativeToJava(data); 193 if (mime.equals(RTF_TYPE)) return RTFTransfer.getInstance().nativeToJava(data); 194 if (mime.equals(HTML_TYPE)) return HTMLTransfer.getInstance().nativeToJava(data); 195 if (mime.equals(URI_TYPE)) return URLTransfer.getInstance().nativeToJava(data); 196 if (mime.equals(RAW_IMAGE_TYPE)) return ImageTransfer.getInstance().nativeToJava(data); 197 if (mime.equals(FILE_LIST_TYPE)) {// || mime.equals("java.file-list")) { 198 return FileTransfer.getInstance().nativeToJava(data); 199 } 200 Transfer transfer = getCustomTransfer(mime); 201 if (transfer != null) return ((CustomTransfer)transfer).nativeToJava(data); 202 return null; 203 } 204 205 @Override 206 protected boolean isOwner() { 207 return MUTIPLE_TRANSFERS; 208 } 209 210 static int getSWTAction(int actions) { 211 int result = ACTION_NONE; 212 if ((actions & ACTION_COPY) != 0) result |= DROP_COPY; 213 if ((actions & ACTION_MOVE) != 0) result |= DROP_MOVE; 214 if ((actions & ACTION_REFERENCE) != 0) result |=DROP_LINK; 215 return result; 216 } 217 218 //TODO - better name that indicates it is the invers of getDragActions() 219 static int getFXAction(int actions) { 220 int result = DROP_NONE; 221 if ((actions & DROP_COPY) != 0) result |= ACTION_COPY; 222 if ((actions & DROP_MOVE) != 0) result |= ACTION_MOVE; 223 if ((actions & DROP_LINK) != 0) result |= ACTION_REFERENCE; 224 return result; 225 } 226 227 @Override 228 protected void pushToSystem(HashMap<String, Object> data, int supportedActions) { 229 int count = 0; 230 ImageData imageData = null; 231 Set<String> keys = data.keySet(); 232 Object [] objects = new Object [keys.size()]; 233 Transfer[] transfers = new Transfer[keys.size()]; 234 for (String key : keys) { 235 Transfer transfer = getTransferType(key); 236 if (transfer != null) { 237 //TODO - image not done, format wrong, alpha wrong 238 if (transfer instanceof ImageTransfer && data.get(key) instanceof Pixels) { 239 Pixels pixels = (Pixels) data.get(key); 240 objects[count] = imageData = SWTApplication.createImageData(pixels); 241 } else { 242 objects[count] = data.get(key); 243 } 244 transfers [count] = transfer; 245 count++; 246 } 247 } 248 if (count == 0) return; 249 if (count != objects.length) { 250 Object [] newObjects = new Object [objects.length]; 251 System.arraycopy(objects, 0, newObjects, 0, objects.length); 252 objects = newObjects; 253 Transfer [] newTransfers = new Transfer [transfers.length]; 254 System.arraycopy(transfers, 0, newTransfers, 0, transfers.length); 255 transfers = newTransfers; 256 } 257 if (clipboard != null) { 258 //TODO - setting and empty string to the clipboard causes an exception 259 //TODO - clear the contents instead (what about multiple objects?) 260 for (int i=0; i<transfers.length; i++) { 261 if (transfers[i] instanceof TextTransfer && objects[i] instanceof String) { 262 if (((String)objects[i]).length() == 0) { 263 clipboard.clearContents(); 264 return; 265 } 266 } 267 } 268 clipboard.setContents(objects, transfers); 269 } else { 270 //TODO - does setting an empty string fail for drag and drop like the clipboard 271 final Control control = Display.getDefault().getFocusControl(); 272 if (control != null && control.getData() instanceof SWTView) { 273 final SWTView view = (SWTView) control.getData(); 274 int dragOperation = getSWTAction(supportedActions); 275 final DragSource dragSource = new DragSource(control, dragOperation); 276 dragSource.setTransfer(transfers); 277 dragSource.setData("objects", objects); 278 dragSource.setData("imageData", imageData); 279 dragSource.addDragListener(new DragSourceListener() { 280 Image image = null; 281 public void dragFinished(DragSourceEvent event) { 282 if (image != null) { 283 image.dispose(); 284 image = null; 285 } 286 dragSource.setData("objects", null); 287 dragSource.setData("imageData", null); 288 dragSource.dispose(); 289 view.notifyDragEnd(getFXAction(event.detail)); 290 } 291 public void dragSetData(DragSourceEvent event) { 292 Object [] objects = (Object []) dragSource.getData("objects"); 293 Transfer [] transfers = dragSource.getTransfer(); 294 for (int i=0; i<transfers.length; i++) { 295 if (transfers[i].isSupportedType(event.dataType)) { 296 String mime = getMime(transfers[i]); 297 if (mime != null) { 298 event.doit = true; 299 event.data = objects [i]; 300 return; 301 } 302 } 303 event.doit = false; 304 } 305 } 306 public void dragStart(DragSourceEvent event) { 307 ImageData imageData = (ImageData) dragSource.getData("imageData"); 308 if (imageData != null) { 309 event.image = image = new Image(event.display, imageData); 310 event.offsetX = imageData.width / 2; 311 event.offsetY = imageData.height / 2; 312 } 313 Point point = control.toDisplay(event.x, event.y); 314 //TODO - button number is hard coded 315 view.notifyDragStart(1, event.x, event.y, point.x, point.y); 316 } 317 }); 318 //TODO - not sure, why do we need to set the drop target transfers when drag starts? 319 //TODO - is there another place that makes more sense to make sure they are up to date 320 if (view.dropTarget != null) view.dropTarget.setTransfer(getAllTransfers()); 321 control.notifyListeners(SWT.DragDetect, null); 322 } 323 } 324 } 325 326 @Override 327 protected void pushTargetActionToSystem(int actionDone) { 328 //TODO - what is the correct implementation for this method? 329 //System.out.println("SWTClipboard.pushTargetActionToSystem"); 330 } 331 332 @Override 333 protected Object popFromSystem(String mimeType) { 334 Transfer transfer = getTransferType(mimeType); 335 if (transfer != null) { 336 //TODO - image not done, format wrong, alpha wrong 337 Object data = null; 338 if (clipboard != null) { 339 data = clipboard.getContents(transfer); 340 } else { 341 if (MULTIPLE_TRANSFERS) { 342 for (int i=0; i<transferData.length; i++) { 343 if (transfer.isSupportedType(transferData[i])) { 344 data = getData(mimeType, transferData[i]); 345 break; 346 } 347 } 348 } else { 349 data = currentData; 350 } 351 } 352 if (data instanceof ImageData) { 353 return SWTApplication.createPixels((ImageData) data); 354 } 355 return data; 356 } 357 return null; 358 } 359 360 //TODO - glass API should include the idea that a control takes part in DND 361 //TODO - don't use statics, they are never cleared, shared state is not correct etc. 362 static int operations = DROP_NONE; 363 static TransferData currentTransferData; 364 static TransferData [] transferData; 365 static Object currentData; 366 367 @Override 368 protected int supportedSourceActionsFromSystem() { 369 if (clipboard != null) return Clipboard.ACTION_COPY; 370 return getFXAction(operations); 371 } 372 373 static DropTarget createDropTarget(final Control control) { 374 final SWTView view = (SWTView) control.getData(); 375 final DropTarget dropTarget = new DropTarget(control, DROP_COPY | DROP_LINK | DROP_MOVE); 376 dropTarget.setTransfer(getAllTransfers()); 377 dropTarget.addDropListener(new DropTargetListener() { 378 //Object currentData; 379 //TransferData [] transferData; 380 //TransferData currentTransferData; 381 int detail = DROP_NONE; 382 //int operations = DROP_NONE; 383 public void dragEnter(DropTargetEvent event) { 384 dropTarget.setTransfer(getAllTransfers()); 385 detail = event.detail; 386 operations = event.operations; 387 dragOver (event, true, detail); 388 } 389 public void dragLeave(DropTargetEvent event) { 390 detail = operations = DROP_NONE; 391 currentData = null; 392 transferData = null; 393 currentTransferData = null; 394 view.notifyDragLeave(); 395 } 396 public void dragOperationChanged(DropTargetEvent event) { 397 detail = event.detail; 398 operations = event.operations; 399 dragOver(event, false, detail); 400 } 401 public void dragOver(DropTargetEvent event) { 402 operations = event.operations; 403 dragOver (event, false, detail); 404 } 405 public void dragOver(DropTargetEvent event, boolean enter, int detail) { 406 transferData = event.dataTypes; 407 currentTransferData = event.currentDataType; 408 Point pt = control.toControl(event.x, event.y); 409 if (detail == DROP_NONE) detail = DROP_COPY; 410 int action = getFXAction(detail), acceptAction; 411 if (enter) { 412 acceptAction = view.notifyDragEnter(pt.x, pt.y, event.x, event.y, action); 413 } else { 414 acceptAction = view.notifyDragOver(pt.x, pt.y, event.x, event.y, action); 415 } 416 event.detail = getSWTAction(acceptAction); 417 //TODO - FX should set the transfer type that is desired for the drop 418 //currentTransferData = event.currentDataType; 419 } 420 public void drop(DropTargetEvent event) { 421 detail = event.detail; 422 operations = event.operations; 423 currentData = event.data; 424 transferData = event.dataTypes; 425 currentTransferData = event.currentDataType; 426 Point pt = control.toControl(event.x, event.y); 427 int action = getFXAction(event.detail); 428 int acceptAction = view.notifyDragDrop(pt.x, pt.y, event.x, event.y, action); 429 event.detail = getSWTAction(acceptAction); 430 currentData = null; 431 transferData = null; 432 currentTransferData = null; 433 } 434 public void dropAccept(DropTargetEvent event) { 435 } 436 }); 437 return dropTarget; 438 } 439 440 @Override 441 protected String[] mimesFromSystem() { 442 //TODO - return non-standard clipboard/drag and drop mimes 443 if (clipboard != null) { 444 int count= 0; 445 TransferData[] data = clipboard.getAvailableTypes(); 446 String [] result = new String [data.length]; 447 for (int i=0; i<data.length; i++) { 448 String mime = getMime(data[i]); 449 if (mime != null) result [count++] = mime; 450 } 451 if (count == result.length) return result; 452 String [] newResult = new String [count]; 453 System.arraycopy(result, 0, newResult, 0, count); 454 return newResult; 455 } else { 456 if (MULTIPLE_TRANSFERS) { 457 // Gets the mime type for the available objects 458 return getMimes(transferData); 459 } else { 460 // Gets the mime type for the dropped object 461 if (currentTransferData == null) return new String [0]; 462 return getMimes(getAllTransfers(), currentTransferData); 463 } 464 } 465 } 466 }