blob: 7f0bd4580029d47c3793cc6f0388706bac8c6f7f [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tools.idea.uibuilder.structure;
import com.android.annotations.NonNull;
import com.android.annotations.Nullable;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.InputEvent;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
/**
* This class will override the DragHandler to support a drag image.
* This class can be removed when JDK1.6 is no longer supported since this functionality
* is already in JDK1.7 of {link TransferHandler}.
*/
public class TransferHandlerWithDragImage extends TransferHandler {
private static final Logger LOG = Logger.getInstance(TransferHandlerWithDragImage.class);
/**
* Image for the {@link DragGestureEvent#startDrag} method
*
* @see DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
*/
private Image dragImage;
/**
* Anchor offset for the {@link DragGestureEvent#startDrag} method
*
* @see DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
*/
private Point dragImageOffset;
/**
* Sets the drag image parameter. The image has to be prepared
* for rendering by the moment of the call. The image is stored
* by reference because of some performance reasons.
*
* @param img an image to drag
*/
public void setDragImage(@Nullable Image img) {
dragImage = img;
}
/**
* Returns the drag image. If there is no image to drag,
* the returned value is {@code null}.
*
* @return the reference to the drag image
*/
@Nullable
public Image getDragImage() {
return dragImage;
}
/**
* Sets an anchor offset for the image to drag.
* It can not be {@code null}.
*
* @param p a {@code Point} object that corresponds
* to coordinates of an anchor offset of the image
* relative to the upper left corner of the image
*/
public void setDragImageOffset(@NonNull Point p) {
dragImageOffset = new Point(p);
}
/**
* Returns an anchor offset for the image to drag.
*
* @return a {@code Point} object that corresponds
* to coordinates of an anchor offset of the image
* relative to the upper left corner of the image.
* The point {@code (0,0)} returns by default.
*/
@NonNull
public Point getDragImageOffset() {
if (dragImageOffset == null) {
return new Point(0,0);
}
return new Point(dragImageOffset);
}
@Override
public void exportAsDrag(JComponent comp, InputEvent e, int action) {
if (isBeforeJava7()) {
provideNewDragHandler();
}
super.exportAsDrag(comp, e, action);
}
private static boolean isBeforeJava7() {
return StringUtil.compareVersionNumbers(SystemInfo.JAVA_VERSION, "1.7") < 0;
}
private void provideNewDragHandler() {
try {
// Hack to override the recognizer field in Transferhandler:
Field recognizer = TransferHandler.class.getDeclaredField("recognizer");
Class<?> gestureRecognizerClass = getDeclaredClass("SwingDragGestureRecognizer");
Class<?> dragHandlerClass = getDeclaredClass("DragHandler");
recognizer.setAccessible(true);
Object value = recognizer.get(this);
if (value == null && gestureRecognizerClass != null && dragHandlerClass != null) {
Constructor<?> dragHandlerConstructor = dragHandlerClass.getDeclaredConstructor();
dragHandlerConstructor.setAccessible(true);
Object defaultDragHandler = dragHandlerConstructor.newInstance();
Constructor<?> gestureConstructor = gestureRecognizerClass.getDeclaredConstructor(DragGestureListener.class);
gestureConstructor.setAccessible(true);
Object instance = gestureConstructor.newInstance(new DragHandler(defaultDragHandler));
recognizer.set(this, instance);
}
}
catch (NoSuchFieldException ex) {
LOG.info(ex);
}
catch (IllegalAccessException ex) {
LOG.info(ex);
}
catch (NoSuchMethodException ex) {
LOG.info(ex);
}
catch (InstantiationException ex) {
LOG.info(ex);
}
catch (InvocationTargetException ex) {
LOG.info(ex);
}
}
@Nullable
private static Class<?> getDeclaredClass(@NonNull String name) {
for (Class<?> klass : TransferHandler.class.getDeclaredClasses()) {
if (klass.getName().endsWith(name)) {
return klass;
}
}
return null;
}
private static class DragHandler implements DragGestureListener, DragSourceListener {
private boolean scrolls;
private DragGestureListener myDragGestureListener;
private DragSourceListener myDragSourceListener;
private DragHandler(Object delegate) {
this.myDragGestureListener = (DragGestureListener)delegate;
this.myDragSourceListener = (DragSourceListener)delegate;
}
// --- DragGestureListener methods -----------------------------------
/**
* a Drag gesture has been recognized
*/
@Override
public void dragGestureRecognized(DragGestureEvent dge) {
JComponent c = (JComponent) dge.getComponent();
TransferHandler th = c.getTransferHandler();
if (th instanceof TransferHandlerWithDragImage) {
TransferHandlerWithDragImage thid = (TransferHandlerWithDragImage)th;
Transferable t = thid.createTransferable(c);
if (t != null) {
scrolls = c.getAutoscrolls();
c.setAutoscrolls(false);
try {
Image im = thid.getDragImage();
if (im == null) {
dge.startDrag(null, t, this);
} else {
dge.startDrag(null, im, thid.getDragImageOffset(), t, this);
}
return;
} catch (RuntimeException re) {
c.setAutoscrolls(scrolls);
}
}
thid.exportDone(c, t, NONE);
} else {
myDragGestureListener.dragGestureRecognized(dge);
}
}
// --- DragSourceListener methods -----------------------------------
/**
* as the hotspot enters a platform dependent drop site
*/
@Override
public void dragEnter(DragSourceDragEvent dsde) {
}
/**
* as the hotspot moves over a platform dependent drop site
*/
@Override
public void dragOver(DragSourceDragEvent dsde) {
}
/**
* as the hotspot exits a platform dependent drop site
*/
@Override
public void dragExit(DragSourceEvent dsde) {
}
/**
* as the operation completes
*/
@Override
public void dragDropEnd(DragSourceDropEvent dsde) {
DragSourceContext dsc = dsde.getDragSourceContext();
JComponent c = (JComponent)dsc.getComponent();
TransferHandler th = c.getTransferHandler();
if (th instanceof TransferHandlerWithDragImage) {
TransferHandlerWithDragImage thid = (TransferHandlerWithDragImage)th;
if (dsde.getDropSuccess()) {
thid.exportDone(c, dsc.getTransferable(), dsde.getDropAction());
}
else {
thid.exportDone(c, dsc.getTransferable(), NONE);
}
c.setAutoscrolls(scrolls);
} else {
myDragSourceListener.dragDropEnd(dsde);
}
}
@Override
public void dropActionChanged(DragSourceDragEvent dsde) {
}
}
}