0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.core.windows.view.dnd;
0043:
0044: import java.awt.AWTEvent;
0045: import java.awt.Color;
0046: import java.awt.Component;
0047: import java.awt.Cursor;
0048: import java.awt.Dialog;
0049: import java.awt.Graphics2D;
0050: import java.awt.GraphicsConfiguration;
0051: import java.awt.GraphicsEnvironment;
0052: import java.awt.Image;
0053: import java.awt.Point;
0054: import java.awt.Toolkit;
0055: import java.awt.datatransfer.*;
0056: import java.awt.dnd.*;
0057: import java.awt.event.*;
0058: import java.awt.image.BufferedImage;
0059: import java.lang.ref.*;
0060: import java.util.*;
0061: import java.util.logging.Level;
0062: import java.util.logging.Logger;
0063: import javax.swing.*;
0064: import org.netbeans.core.windows.*;
0065: import org.netbeans.core.windows.view.ui.*;
0066: import org.openide.util.*;
0067: import org.openide.windows.TopComponent;
0068:
0069: /**
0070: * Window system drag support for <code>TopComponet</code>'s.
0071: * It imitates role of drag gesture recognizer, possible
0072: * on any kind of <code>Component</code>, currently on <code>Tabbed</code>.
0073: * Starts also programatically the DnD for TopComponent in container
0074: * when the starting gestures are Shift+Mouse Drag or Ctrl+Mouse Drag
0075: * respectivelly.
0076: * It serves as <code>DragSourceListener</code> during the DnD in progress
0077: * and sets dragging cursor appopriatelly.
0078: *
0079: * <em>Note:</em> There is used only one singleton instance in window system
0080: * DnD available via {@link #getDefault}.
0081: *
0082: *
0083: * @author Peter Zavadsky
0084: *
0085: * @see java awt.dnd.DragSourceListener
0086: */
0087: final class TopComponentDragSupport implements AWTEventListener,
0088: DragSourceListener, DragSourceMotionListener {
0089:
0090: /** Mime type for <code>TopComponent</code> <code>DataFlavor</code>. */
0091: public static final String MIME_TOP_COMPONENT = DataFlavor.javaJVMLocalObjectMimeType
0092: // Note: important is the space after semicolon, thus to match
0093: // when comparing.
0094: + "; class=org.openide.windows.TopComponent"; // NOI18N
0095:
0096: /** Mime type for <code>TopComponent.Cloneable</code> <code>DataFlavor</code>. */
0097: public static final String MIME_TOP_COMPONENT_CLONEABLE = DataFlavor.javaJVMLocalObjectMimeType
0098: + "; class=org.openide.windows.TopComponent$Cloneable"; // NOI18N
0099:
0100: /** Mime type for <code>TopComponent</code>'s array <code>DataFlavor</code>. */
0101: public static final String MIME_TOP_COMPONENT_ARRAY = DataFlavor.javaJVMLocalObjectMimeType
0102: + "; class=org.netbeans.core.windows.view.dnd.TopComponentDragSupport$TopComponentArray"; // NOI18N
0103:
0104: /** 'Copy window' cursor type. */
0105: private static final int CURSOR_COPY = 0;
0106: /** 'Copy_No window' cursor type. */
0107: private static final int CURSOR_COPY_NO = 1;
0108: /** 'Move window' cursor type. */
0109: private static final int CURSOR_MOVE = 2;
0110: /** 'Move_No window' cursor type. */
0111: private static final int CURSOR_MOVE_NO = 3;
0112: /** Cursor type indicating there cannont be copy operation
0113: * done, but could be done move operation. In fact is
0114: * the same like {@link #CURSOR_COPY_NO} with the diff name
0115: * to be recognized correctly when switching action over drop target */
0116: private static final int CURSOR_COPY_NO_MOVE = 4;
0117: /** Move to free area cursor type */
0118: private static final int CURSOR_MOVE_FREE = 5;
0119:
0120: /** Name for 'Copy window' cursor. */
0121: private static final String NAME_CURSOR_COPY = "CursorTopComponentCopy"; // NOI18N
0122: /** Name for 'Copy_No window' cursor. */
0123: private static final String NAME_CURSOR_COPY_NO = "CursorTopComponentCopyNo"; // NOI18N
0124: /** Name for 'Move window' cursor. */
0125: private static final String NAME_CURSOR_MOVE = "CursorTopComponentMove"; // NOI18N
0126: /** Name for 'Move_No window' cursor. */
0127: private static final String NAME_CURSOR_MOVE_NO = "CursorTopComponentMoveNo"; // NOI18N
0128: /** */
0129: private static final String NAME_CURSOR_COPY_NO_MOVE = "CursorTopComponentCopyNoMove"; // NOI18N
0130: /** Name for cursor to drop to free area. */
0131: private static final String NAME_CURSOR_MOVE_FREE = "CursorTopComponentMoveFree"; // NOI18N
0132:
0133: /** Debugging flag. */
0134: private static final boolean DEBUG = Debug
0135: .isLoggable(TopComponentDragSupport.class);
0136:
0137: private final WindowDnDManager windowDnDManager;
0138:
0139: /** Weak reference to <code>DragSourceContext</code> used in processed
0140: * drag operation. Used for by fixing bugs while not passed correct
0141: * order of events to <code>DragSourceListener</code>. */
0142: private Reference<DragSourceContext> dragContextWRef = new WeakReference<DragSourceContext>(
0143: null);
0144:
0145: /** Flag indicating the current window drag operation transferable
0146: * can be 'copied', i.e. the dragged <code>TopComponent</code> is
0147: * <code>TopComponent.Cloneable</code> instance. */
0148: private boolean canCopy;
0149:
0150: // #21918. There is not possible to indicate drop action in "free" desktop
0151: // area. This field helps to workaround the problem.
0152: /** Flag indicating user drop action. */
0153: private int hackUserDropAction;
0154:
0155: // #21918. Determine the ESC pressed.
0156: /** Flag indicating the user has cancelled drag operation by pressing ESC key. */
0157: private boolean hackESC;
0158:
0159: /** Weak set of componens on which we listen for ESC key. */
0160: private final Set keyObservers = new WeakSet(4);
0161:
0162: private Point startingPoint;
0163: private Component startingComponent;
0164: private long startingTime;
0165:
0166: private DragAndDropFeedbackVisualizer visualizer;
0167:
0168: private boolean dropFailed = false;
0169:
0170: /** Creates a new instance of TopComponentDragSupport. */
0171: TopComponentDragSupport(WindowDnDManager windowDnDManager) {
0172: this .windowDnDManager = windowDnDManager;
0173: }
0174:
0175: /** Informs whether the 'copy' operation is possible. Gets valid result
0176: * during processed drag operation only.
0177: * @return <code>true</code> if the drop copy operation is possible from
0178: * drag source point of view
0179: * @see #canCopy */
0180: public boolean isCopyOperationPossible() {
0181: return canCopy;
0182: }
0183:
0184: /** Simulates drag gesture recongition valid for winsys.
0185: * Implements <code>AWTEventListener</code>. */
0186: public void eventDispatched(AWTEvent evt) {
0187: MouseEvent me = (MouseEvent) evt;
0188: //#118828
0189: if (!(evt.getSource() instanceof Component)) {
0190: return;
0191: }
0192:
0193: // #40736: only left mouse button drag should start DnD
0194: if ((me.getID() == MouseEvent.MOUSE_PRESSED)
0195: && SwingUtilities.isLeftMouseButton(me)) {
0196: startingPoint = me.getPoint();
0197: startingComponent = me.getComponent();
0198: startingTime = me.getWhen();
0199: } else if (me.getID() == MouseEvent.MOUSE_RELEASED) {
0200: startingPoint = null;
0201: startingComponent = null;
0202: }
0203:
0204: if (evt.getID() != MouseEvent.MOUSE_DRAGGED) {
0205: return;
0206: }
0207: if (windowDnDManager.isDragging()) {
0208: return;
0209: }
0210: if (startingPoint == null) {
0211: return;
0212: }
0213: if (evt.getSource() instanceof JButton) {
0214: //do not initiate topcomponent drag when the mouse is dragged out of a tabcontrol button
0215: return;
0216: }
0217: if (!WindowDnDManager.isDnDEnabled()) {
0218: return;
0219: }
0220:
0221: Component srcComp = startingComponent;
0222: if (srcComp == null) {
0223: return;
0224: }
0225:
0226: final Point point = new Point(startingPoint);
0227: Point currentPoint = me.getPoint();
0228: Component currentComponent = me.getComponent();
0229: if (currentComponent == null) {
0230: return;
0231: }
0232: currentPoint = SwingUtilities.convertPoint(currentComponent,
0233: currentPoint, srcComp);
0234: if (Math.abs(currentPoint.x - point.x) <= Constants.DRAG_GESTURE_START_DISTANCE
0235: && Math.abs(currentPoint.y - point.y) <= Constants.DRAG_GESTURE_START_DISTANCE) {
0236: return;
0237: }
0238: // time check, to prevent wild mouse clicks to be considered DnD start
0239: if (me.getWhen() - startingTime <= Constants.DRAG_GESTURE_START_TIME) {
0240: return;
0241: }
0242: startingPoint = null;
0243: startingComponent = null;
0244:
0245: if (DEBUG) {
0246: debugLog(""); // NOI18N
0247: debugLog("eventDispatched (MOUSE_DRAGGED)"); // NOI18N
0248: }
0249:
0250: // XXX Do not clash with JTree (e.g. in explorer) drag.
0251: if ((srcComp instanceof JTree)
0252: && ((JTree) srcComp).getPathForLocation(me.getX(), me
0253: .getY()) != null) {
0254: return;
0255: }
0256:
0257: // #22622: AWT listener just passivelly listnens what is happenning around,
0258: // and we need always the deepest component to start from.
0259: srcComp = SwingUtilities.getDeepestComponentAt(srcComp,
0260: point.x, point.y);
0261:
0262: boolean ctrlDown = me.isControlDown();
0263:
0264: TopComponent tc = null;
0265: Tabbed tabbed;
0266:
0267: if (srcComp instanceof Tabbed) {
0268: tabbed = (Tabbed) srcComp;
0269: } else {
0270: tabbed = (Tabbed) SwingUtilities.getAncestorOfClass(
0271: Tabbed.class, srcComp);
0272: }
0273: if (tabbed == null) {
0274: if (srcComp instanceof Tabbed.Accessor) {
0275: tabbed = ((Tabbed.Accessor) srcComp).getTabbed();
0276: } else {
0277: Tabbed.Accessor acc = (Tabbed.Accessor) SwingUtilities
0278: .getAncestorOfClass(Tabbed.Accessor.class,
0279: srcComp);
0280: tabbed = acc != null ? acc.getTabbed() : null;
0281: }
0282: }
0283: if (tabbed == null) {
0284: return;
0285: }
0286:
0287: Point ppp = new Point(point);
0288: Point p = SwingUtilities.convertPoint(srcComp, ppp, tabbed
0289: .getComponent());
0290:
0291: // #106761: tabForCoordinate may return -1, so check is needed
0292: int tabIndex = tabbed.tabForCoordinate(p);
0293: tc = tabIndex != -1 ? tabbed.getTopComponentAt(tabIndex) : null;
0294: if (tc == null) {
0295: return;
0296: }
0297:
0298: // #21918. See above.
0299: if (ctrlDown) {
0300: hackUserDropAction = DnDConstants.ACTION_COPY;
0301: } else {
0302: hackUserDropAction = DnDConstants.ACTION_MOVE;
0303: }
0304:
0305: List<MouseEvent> list = new ArrayList<MouseEvent>();
0306: list.add(me);
0307:
0308: // Get start droppable (if there) and its starting point.
0309: TopComponentDroppable startDroppable = (TopComponentDroppable) SwingUtilities
0310: .getAncestorOfClass(TopComponentDroppable.class, tc);
0311: Point startPoint;
0312: if (startDroppable == null) {
0313: startDroppable = (TopComponentDroppable) SwingUtilities
0314: .getAncestorOfClass(TopComponentDroppable.class,
0315: tabbed.getComponent());
0316: }
0317: if (startDroppable != null) {
0318: startPoint = point;
0319: Point pp = new Point(point);
0320: startPoint = SwingUtilities.convertPoint(srcComp, pp,
0321: (Component) startDroppable);
0322: } else {
0323: startPoint = null;
0324: }
0325: //dragSource.startDrag(event, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR) ,image , new Point(-offX, -offY),text, this);
0326:
0327: doStartDrag(srcComp, tc, new DragGestureEvent(
0328: new FakeDragGestureRecognizer(windowDnDManager, me),
0329: hackUserDropAction, point, list), startDroppable,
0330: startPoint);
0331: }
0332:
0333: /** Actually starts the drag operation. */
0334: private void doStartDrag(Component startingComp, Object transfer,
0335: DragGestureEvent evt,
0336: TopComponentDroppable startingDroppable,
0337: final Point startingPoint) {
0338: if (DEBUG) {
0339: debugLog(""); // NOI18N
0340: debugLog("doStartDrag"); // NOI18N
0341: }
0342: final TopComponent firstTC = transfer instanceof TopComponent ? (TopComponent) transfer
0343: : (((TopComponent[]) transfer)[0]);
0344:
0345: // #22132. If in modal dialog no drag allowed.
0346: Dialog dlg = (Dialog) SwingUtilities.getAncestorOfClass(
0347: Dialog.class, firstTC);
0348: if (dlg != null && dlg.isModal()) {
0349: return;
0350: }
0351:
0352: if (firstTC instanceof TopComponent.Cloneable) {
0353: canCopy = true;
0354: } else {
0355: canCopy = false;
0356: }
0357:
0358: // Inform window sys there is DnD about to start.
0359: // XXX Using the firstTC in DnD manager is a hack.
0360: windowDnDManager.dragStarting(startingDroppable, startingPoint,
0361: firstTC);
0362:
0363: Cursor cursor = hackUserDropAction == DnDConstants.ACTION_MOVE ? getDragCursor(
0364: startingComp, CURSOR_MOVE)
0365: : (canCopy ? getDragCursor(startingComp, CURSOR_COPY)
0366: : getDragCursor(startingComp,
0367: CURSOR_COPY_NO_MOVE));
0368:
0369: // Sets listnening for ESC key.
0370: addListening();
0371: hackESC = false;
0372:
0373: Tabbed tabbed = (Tabbed) SwingUtilities.getAncestorOfClass(
0374: Tabbed.class, startingComp);
0375: if (tabbed == null) {
0376: Tabbed.Accessor acc = (Tabbed.Accessor) SwingUtilities
0377: .getAncestorOfClass(Tabbed.Accessor.class,
0378: startingComp);
0379: tabbed = acc != null ? acc.getTabbed() : null;
0380: }
0381:
0382: int tabIndex = -1;
0383: Image img = createDragImage();
0384: if (tabbed != null) {//&& Constants.SWITCH_USE_DRAG_IMAGES) {
0385: tabIndex = tabbed.indexOf(firstTC);
0386:
0387: visualizer = new DragAndDropFeedbackVisualizer(tabbed,
0388: tabIndex);
0389: }
0390: try {
0391:
0392: evt
0393: .startDrag(
0394: cursor,
0395: img,
0396: new Point(0, 0),
0397: (transfer instanceof TopComponent ? (Transferable) new TopComponentTransferable(
0398: (TopComponent) transfer)
0399: : (Transferable) new TopComponentArrayTransferable(
0400: (TopComponent[]) transfer)),
0401: this );
0402: evt.getDragSource().addDragSourceMotionListener(this );
0403:
0404: if (null != visualizer) {
0405: visualizer.start(evt);
0406: }
0407:
0408: } catch (InvalidDnDOperationException idoe) {
0409: Logger.getLogger(TopComponentDragSupport.class.getName())
0410: .log(Level.WARNING, null, idoe);
0411:
0412: removeListening();
0413: windowDnDManager.resetDragSource();
0414: if (null != visualizer) {
0415: visualizer.dispose(false);
0416: visualizer = null;
0417: }
0418: }
0419: }
0420:
0421: private AWTEventListener keyListener = new AWTEventListener() {
0422: public void eventDispatched(AWTEvent event) {
0423: KeyEvent keyevent = (KeyEvent) event;
0424:
0425: if (keyevent.getID() == KeyEvent.KEY_RELEASED
0426: && keyevent.getKeyCode() == KeyEvent.VK_ESCAPE) {
0427: hackESC = true;
0428: }
0429: }
0430:
0431: };
0432:
0433: /** Adds <code>KeyListener</code> to container and its component
0434: * hierarchy to listen for ESC key. */
0435: private void addListening() {
0436: Toolkit.getDefaultToolkit().addAWTEventListener(keyListener,
0437: AWTEvent.KEY_EVENT_MASK);
0438: }
0439:
0440: /** Removes ESC listening. Helper method. */
0441: private void removeListening() {
0442: Toolkit.getDefaultToolkit().removeAWTEventListener(keyListener);
0443: }
0444:
0445: // >> DragSourceListener implementation >>
0446: /** Implements <code>DragSourceListener</code> method.
0447: * It just refreshes the weak reference of <code>DragSourceContext</code>
0448: * for the sake of setSuccessCursor method.
0449: * The excpected code, changing of cursor, is done in setSuccessCursor method
0450: * due to an undeterministic calls of this method especially in MDI mode.
0451: * @see #setSuccessCursor */
0452: public void dragEnter(DragSourceDragEvent evt) {
0453: if (DEBUG) {
0454: debugLog(""); // NOI18N
0455: debugLog("dragEnter");// NOI18N
0456: }
0457:
0458: // Just refresh the weak ref to the context if necessary.
0459: // The expected code here is done by ragExitHack method called from DropTarget's.
0460: if (dragContextWRef.get() == null) {
0461: dragContextWRef = new java.lang.ref.WeakReference<DragSourceContext>(
0462: evt.getDragSourceContext());
0463: }
0464: }
0465:
0466: /** Dummy implementation of <code>DragSourceListener</code> method. */
0467: public void dragOver(DragSourceDragEvent evt) {
0468: if (DEBUG) {
0469: debugLog(""); // NOI18N
0470: debugLog("dragOver"); // NOI18N
0471: }
0472: }
0473:
0474: /** Implements <code>DragSourceListener</code> method.
0475: * It just refreshes the weak reference of <code>DragSourceContext</code>
0476: * for the sake of setUnsuccessCursor method.
0477: * The excpected code, changing of cursor, is done in setUnsuccessCursor method
0478: * due to an undeterministic calls of this method especially in MDI mode.
0479: * @see #setUnsuccessCursor */
0480: public void dragExit(DragSourceEvent evt) {
0481: if (DEBUG) {
0482: debugLog(""); // NOI18N
0483: debugLog("dragExit"); // NOI18N
0484: }
0485:
0486: // Just refresh the weak ref to the context if necessary.
0487: // The expected code here is done by ragExitHack method called from DropTarget's.
0488: if (dragContextWRef.get() == null) {
0489: dragContextWRef = new WeakReference<DragSourceContext>(evt
0490: .getDragSourceContext());
0491: }
0492: }
0493:
0494: /** Implements <code>DragSourceListener</code> method.
0495: * It changes the cursor type from copy to move and bakc accordting the
0496: * user action. */
0497: public void dropActionChanged(DragSourceDragEvent evt) {
0498: if (DEBUG) {
0499: debugLog(""); // NOI18N
0500: debugLog("dropActionChanged"); // NOI18N
0501: }
0502: String name = evt.getDragSourceContext().getCursor().getName();
0503:
0504: if (name == null) {
0505: // Not our cursor??
0506: return;
0507: }
0508:
0509: // For us is the user action important.
0510: int userAction = evt.getUserAction();
0511:
0512: // Consider NONE action as MOVE one.
0513: if (userAction == DnDConstants.ACTION_NONE) {
0514: userAction = DnDConstants.ACTION_MOVE;
0515: }
0516: // #21918. See above.
0517: hackUserDropAction = userAction;
0518:
0519: int type;
0520: if ((NAME_CURSOR_COPY.equals(name) || NAME_CURSOR_COPY_NO_MOVE
0521: .equals(name))
0522: && userAction == DnDConstants.ACTION_MOVE) {
0523: type = CURSOR_MOVE;
0524: } else if (NAME_CURSOR_COPY_NO.equals(name)
0525: && userAction == DnDConstants.ACTION_MOVE) {
0526: type = CURSOR_MOVE_NO;
0527: } else if (NAME_CURSOR_MOVE.equals(name)
0528: && userAction == DnDConstants.ACTION_COPY) {
0529: type = CURSOR_COPY;
0530: } else if (NAME_CURSOR_MOVE_NO.equals(name)
0531: && userAction == DnDConstants.ACTION_COPY) {
0532: type = CURSOR_COPY_NO;
0533: } else {
0534: return;
0535: }
0536:
0537: // There can't be copy operation performed,
0538: // transferreed TopComponent in not of TopComponent.Cloneable instance.
0539: if (type == CURSOR_COPY && !canCopy) {
0540: type = CURSOR_COPY_NO_MOVE;
0541: }
0542:
0543: // Check if there is already our cursor.
0544: if (getDragCursorName(type).equals(
0545: evt.getDragSourceContext().getCursor().getName())) {
0546: return;
0547: }
0548:
0549: evt.getDragSourceContext()
0550: .setCursor(
0551: getDragCursor(evt.getDragSourceContext()
0552: .getComponent(), type));
0553: }
0554:
0555: /** Implements <code>DragSourceListener</code> method.
0556: * Informs window dnd manager the drag operation finished.
0557: * @see WindowDnDManager#dragFinished */
0558: public void dragDropEnd(final DragSourceDropEvent evt) {
0559: if (DEBUG) {
0560: debugLog(""); // NOI18N
0561: debugLog("dragDropEnd"); // NOI18N
0562: }
0563:
0564: try {
0565: if (checkDropSuccess(evt)) {
0566: windowDnDManager.dragFinished();
0567:
0568: removeListening();
0569: return;
0570: }
0571:
0572: // Now simulate drop into "free" desktop area.
0573: final Set<Component> floatingFrames = windowDnDManager
0574: .getFloatingFrames();
0575: // Finally schedule the "drop" task later to be able to
0576: // detect if ESC was pressed.
0577: RequestProcessor.getDefault().post(new Runnable() {
0578: public void run() {
0579: SwingUtilities
0580: .invokeLater(createDropIntoFreeAreaTask(
0581: evt, evt.getLocation(),
0582: floatingFrames));
0583: }
0584: }, 250 // XXX #21918, Neccessary to skip after possible ESC key event.
0585: );
0586: } finally {
0587: windowDnDManager.dragFinishedEx();
0588: }
0589: }
0590:
0591: // << DragSourceListener implementation <<
0592:
0593: /** Checks whether there was a successfull drop. */
0594: private boolean checkDropSuccess(DragSourceDropEvent evt) {
0595: // XXX #21917.
0596: if (windowDnDManager.isDropSuccess()) {
0597: return true;
0598: }
0599:
0600: // Gets location.
0601: Point location = evt.getLocation();
0602: if (location == null) {
0603: return true;
0604: }
0605:
0606: if (WindowDnDManager.isInMainWindow(location)
0607: || windowDnDManager.isInFloatingFrame(location)
0608: || WindowDnDManager.isAroundCenterPanel(location)) {
0609: return false;
0610: }
0611: // else if(evt.getDropSuccess()) {
0612: // return true;
0613: // } // PENDING it seem it is not working correctly (at least on linux).
0614: return false;
0615: }
0616:
0617: /** Creates task which performs actual drop into "free area", i.e. it
0618: * creates new separated (floating) window. */
0619: private Runnable createDropIntoFreeAreaTask(
0620: final DragSourceDropEvent evt, final Point location,
0621: final Set<Component> floatingFrames) {
0622: return new Runnable() {
0623: public void run() {
0624: // XXX #21918. Don't move the check sooner
0625: // (before the enclosing blocks), it would be invalid.
0626: if (hackESC) {
0627: windowDnDManager.dragFinished();
0628: removeListening();
0629: return;
0630: }
0631:
0632: TopComponent[] tcArray = WindowDnDManager
0633: .extractTopComponent(false, evt
0634: .getDragSourceContext()
0635: .getTransferable());
0636:
0637: // Provide actual drop into "free" desktop area.
0638: if (tcArray != null) {
0639: // XXX there is a problem if jdk dnd framework sets as drop action
0640: // ACTION_NONE, there is not called drop event on DropTargetListener,
0641: // even it is there.
0642: // Performs hacked drop action, simulates ACTION_MOVE when
0643: // system set ACTION_NONE (which we do not use).
0644: boolean res = windowDnDManager.tryPerformDrop(
0645: windowDnDManager.getController(),
0646: floatingFrames, location,
0647: DnDConstants.ACTION_MOVE, // MOVE only
0648: evt.getDragSourceContext()
0649: .getTransferable());
0650:
0651: }
0652: windowDnDManager.dragFinished();
0653: }
0654: };
0655: }
0656:
0657: /** Hacks problems with <code>dragEnter</code> wrong method calls.
0658: * It plays its role. Sets the cursor from 'no-drop' state
0659: * to its 'drop' state sibling.
0660: * @param freeArea true when mouse pointer in free screen area
0661: * @see #dragEnter */
0662: void setSuccessCursor(boolean freeArea) {
0663: int dropAction = hackUserDropAction;
0664: DragSourceContext ctx = dragContextWRef.get();
0665:
0666: if (ctx == null) {
0667: return;
0668: }
0669:
0670: if (null != visualizer)
0671: visualizer.setDropFeedback(true);
0672:
0673: dropFailed = false;
0674:
0675: // int type;
0676: // if(dropAction == DnDConstants.ACTION_MOVE) {
0677: // type = freeArea ? CURSOR_MOVE_FREE : CURSOR_MOVE;
0678: // } else if(dropAction == DnDConstants.ACTION_COPY) {
0679: // if(canCopy) {
0680: // type = CURSOR_COPY;
0681: // } else {
0682: // type = CURSOR_COPY_NO_MOVE;
0683: // }
0684: // } else {
0685: // // PENDING throw exception?
0686: // Logger.getLogger(TopComponentDragSupport.class.getName()).log(Level.WARNING, null,
0687: // new java.lang.IllegalStateException("Invalid action type->" +
0688: // dropAction)); // NOI18N
0689: // return;
0690: // }
0691: //
0692: // // Check if there is already our cursor.
0693: // if(getDragCursorName(type).equals(ctx.getCursor().getName())) {
0694: // return;
0695: // }
0696: //
0697: // ctx.setCursor(getDragCursor(ctx.getComponent(),type));
0698: }
0699:
0700: /** Hacks problems with <code>dragExit</code> wrong method calls.
0701: * It plays its role. Sets the cursor from 'drop' state
0702: * to its 'no-drop' state sibling.
0703: * @see #dragExit */
0704: void setUnsuccessCursor() {
0705: DragSourceContext ctx = dragContextWRef.get();
0706:
0707: if (ctx == null) {
0708: return;
0709: }
0710:
0711: if (null != visualizer)
0712: visualizer.setDropFeedback(false);
0713:
0714: String name = ctx.getCursor().getName();
0715:
0716: dropFailed = true;
0717:
0718: // int type;
0719: // if(NAME_CURSOR_COPY.equals(name)
0720: // || NAME_CURSOR_COPY_NO_MOVE.equals(name)) {
0721: // type = CURSOR_COPY_NO;
0722: // } else if(NAME_CURSOR_MOVE.equals(name) || NAME_CURSOR_MOVE_NO.equals(name)) {
0723: // type = CURSOR_MOVE_NO;
0724: // } else {
0725: // return;
0726: // }
0727: //
0728: // ctx.setCursor(getDragCursor(ctx.getComponent(),type));
0729: }
0730:
0731: /** Provides cleanup when finished drag operation. Ideally the code
0732: * should reside in {@ling #dragDropEnd} method only. But that one
0733: * is not called in case of error in DnD framework. */
0734: void dragFinished() {
0735: dragContextWRef = new WeakReference<DragSourceContext>(null);
0736: if (null != visualizer) {
0737: visualizer.dispose(!(dropFailed || hackESC));
0738: dropFailed = false;
0739: visualizer = null;
0740: }
0741: }
0742:
0743: private static void debugLog(String message) {
0744: Debug.log(TopComponentDragSupport.class, message);
0745: }
0746:
0747: // Helpers>>
0748: /** Gets window drag <code>Cursor</code> of specified type. Utility method.
0749: * @param type valid one of {@link #CURSOR_COPY}, {@link #CURSOR_COPY_NO},
0750: * {@link #CURSOR_MOVE}, {@link #CURSOR_MOVE_NO}, {@link #CURSOR_MOVE_FREE} */
0751: private static String getDragCursorName(int type) {
0752: if (type == CURSOR_COPY) {
0753: return NAME_CURSOR_COPY;
0754: } else if (type == CURSOR_COPY_NO) {
0755: return NAME_CURSOR_COPY_NO;
0756: } else if (type == CURSOR_MOVE) {
0757: return NAME_CURSOR_MOVE;
0758: } else if (type == CURSOR_MOVE_NO) {
0759: return NAME_CURSOR_MOVE_NO;
0760: } else if (type == CURSOR_COPY_NO_MOVE) {
0761: return NAME_CURSOR_COPY_NO_MOVE;
0762: } else if (type == CURSOR_MOVE_FREE) {
0763: return NAME_CURSOR_MOVE_FREE;
0764: } else {
0765: return null;
0766: }
0767: }
0768:
0769: /** Gets window drag <code>Cursor</code> of specified type. Utility method.
0770: * @param type valid one of {@link #CURSOR_COPY}, {@link #CURSOR_COPY_NO},
0771: * {@link #CURSOR_MOVE}, {@link #CURSOR_MOVE_NO}, {@link #CURSOR_MOVE_FREE}
0772: * @exception IllegalArgumentException if invalid type parameter passed in */
0773: private static Cursor getDragCursor(Component comp, int type) {
0774: Image image = null;
0775: String name = null;
0776:
0777: if (type == CURSOR_COPY) {
0778: image = Utilities
0779: .loadImage("org/netbeans/core/resources/topComponentDragCopy.gif"); // NOI18N
0780: name = NAME_CURSOR_COPY;
0781: } else if (type == CURSOR_COPY_NO) {
0782: image = Utilities
0783: .loadImage("org/netbeans/core/resources/topComponentDragCopyNo.gif"); // NOI18N
0784: name = NAME_CURSOR_COPY_NO;
0785: } else if (type == CURSOR_MOVE) {
0786: image = Utilities
0787: .loadImage("org/netbeans/core/resources/topComponentDragMove.gif"); // NOI18N
0788: name = NAME_CURSOR_MOVE;
0789: } else if (type == CURSOR_MOVE_NO) {
0790: image = Utilities
0791: .loadImage("org/netbeans/core/resources/topComponentDragMoveNo.gif"); // NOI18N
0792: name = NAME_CURSOR_MOVE_NO;
0793: } else if (type == CURSOR_COPY_NO_MOVE) {
0794: image = Utilities
0795: .loadImage("org/netbeans/core/resources/topComponentDragCopyNo.gif"); // NOI18N
0796: name = NAME_CURSOR_COPY_NO_MOVE;
0797: } else if (type == CURSOR_MOVE_FREE) {
0798: image = Utilities
0799: .loadImage("org/netbeans/core/windows/resources/topComponentDragMoveFreeArea.gif"); // NOI18N
0800: name = NAME_CURSOR_MOVE_FREE;
0801: } else {
0802: throw new IllegalArgumentException("Unknown cursor type="
0803: + type); // NOI18N
0804: }
0805:
0806: return Utilities.createCustomCursor(comp, image, name);
0807: }
0808:
0809: // Helpers<<
0810:
0811: /** <code>Transferable</code> used for <code>TopComponent</code> instances
0812: * to be used in window system DnD. */
0813: private static class TopComponentTransferable extends Object
0814: implements Transferable {
0815:
0816: // #86564: Hold TopComponent weakly to workaround AWT bug #6555816
0817: /** <code>TopComponent</code> to be transferred. */
0818: private WeakReference<TopComponent> weakTC;
0819:
0820: /** Crates <code>Transferable</code> for specified <code>TopComponent</code> */
0821: public TopComponentTransferable(TopComponent tc) {
0822: this .weakTC = new WeakReference<TopComponent>(tc);
0823: }
0824:
0825: // >> Transferable implementation >>
0826: /** Implements <code>Transferable</code> method.
0827: * @return <code>TopComponent</code> instance for <code>DataFlavor</code>
0828: * with mimetype equal to {@link #MIME_TOP_COMPONENT} or if mimetype
0829: * equals to {@link #MIME_CLONEABLE_TOP_COMPONENT} and the top component
0830: * is instance of <code>TopComponent.Cloneable</code> returns the instance */
0831: public Object getTransferData(DataFlavor df) {
0832: TopComponent tc = weakTC.get();
0833: if (MIME_TOP_COMPONENT.equals(df.getMimeType())) {
0834: return tc;
0835: } else if (MIME_TOP_COMPONENT_CLONEABLE.equals(df
0836: .getMimeType())
0837: && tc instanceof TopComponent.Cloneable) {
0838: return tc;
0839: }
0840:
0841: return null;
0842: }
0843:
0844: /** Implements <code>Transferable</code> method.
0845: * @return Array of <code>DataFlavor</code> with mimetype
0846: * {@link #MIME_TOP_COMPONENT} and also with mimetype
0847: * {@link #MIME_CLONEABLE_TOP_COMPONENT}
0848: * if the <code>tc</code> is instance
0849: * of <code>TopComponent.Cloneable</code> */
0850: public DataFlavor[] getTransferDataFlavors() {
0851: try {
0852: TopComponent tc = weakTC.get();
0853: if (tc instanceof TopComponent.Cloneable) {
0854: return new DataFlavor[] {
0855: new DataFlavor(MIME_TOP_COMPONENT, null,
0856: TopComponent.class.getClassLoader()),
0857: new DataFlavor(
0858: MIME_TOP_COMPONENT_CLONEABLE, null,
0859: TopComponent.Cloneable.class
0860: .getClassLoader()) };
0861: } else {
0862: return new DataFlavor[] { new DataFlavor(
0863: MIME_TOP_COMPONENT, null,
0864: TopComponent.class.getClassLoader()) };
0865: }
0866: } catch (ClassNotFoundException ex) {
0867: Logger.getLogger(
0868: TopComponentDragSupport.class.getName()).log(
0869: Level.WARNING, ex.getMessage(), ex);
0870: }
0871: return new DataFlavor[0];
0872: }
0873:
0874: /** Implements <code>Transferable</code> method.
0875: * @return <code>true</code> for <code>DataFlavor</code> with mimetype
0876: * equal to {@link #MIME_TOP_COMPONENT}
0877: * and if <code>tc</code> is instance
0878: * of <code>TopComponent.Cloneable</code> also for the one
0879: * with mimetype {@link #MIME_TOP_COMPONENT_CLONEABLE},
0880: * <code>false</code> otherwise */
0881: public boolean isDataFlavorSupported(DataFlavor df) {
0882: TopComponent tc = weakTC.get();
0883: if (MIME_TOP_COMPONENT.equals(df.getMimeType())) {
0884: return true;
0885: } else if (MIME_TOP_COMPONENT_CLONEABLE.equals(df
0886: .getMimeType())
0887: && tc instanceof TopComponent.Cloneable) {
0888: return true;
0889: }
0890:
0891: return false;
0892: }
0893: // << Transferable implementation <<
0894: } // End of class TopComponentTransferable.
0895:
0896: /** <code>Transferable</code> used for <code>TopComponent</code> instances
0897: * to be used in window system DnD. */
0898: private static class TopComponentArrayTransferable extends Object
0899: implements Transferable {
0900:
0901: // #86564: Hold TopComponents weakly to workaround AWT bug #6555816
0902: /** <code>TopComponent</code> to be transferred. */
0903: private List<WeakReference<TopComponent>> weakTCList;
0904:
0905: /** Crates <code>Transferable</code> for specified <code>TopComponent</code> */
0906: public TopComponentArrayTransferable(TopComponent[] tcArray) {
0907: this .weakTCList = new ArrayList<WeakReference<TopComponent>>();
0908: for (TopComponent topComponent : tcArray) {
0909: weakTCList.add(new WeakReference<TopComponent>(
0910: topComponent));
0911: }
0912: }
0913:
0914: // >> Transferable implementation >>
0915: /** Implements <code>Transferable</code> method.
0916: * @return <code>TopComponent</code> instance for <code>DataFlavor</code>
0917: * with mimetype equal to {@link #MIME_TOP_COMPONENT}
0918: * or if mimetype equals to
0919: * {@link #MIME_CLONEABLE_TOP_COMPONENT} and
0920: * the top component is instance
0921: * of <code>TopComponent.Cloneable</code> returns the instance. */
0922: public Object getTransferData(DataFlavor df) {
0923: if (MIME_TOP_COMPONENT_ARRAY.equals(df.getMimeType())) {
0924: List<TopComponent> tcList = new ArrayList<TopComponent>(
0925: weakTCList.size());
0926: TopComponent curTC = null;
0927: for (WeakReference<TopComponent> weakTC : weakTCList) {
0928: curTC = weakTC.get();
0929: if (curTC != null) {
0930: tcList.add(curTC);
0931: }
0932: }
0933: }
0934: return null;
0935: }
0936:
0937: /** Implements <code>Transferable</code> method.
0938: * @return Array of <code>DataFlavor</code> with mimetype
0939: * {@link #MIME_TOP_COMPONENT} and also with mimetype
0940: * {@link #MIME_CLONEABLE_TOP_COMPONENT}
0941: * if the <code>tc</code> is
0942: * instance of <code>TopComponent.Cloneable</code> */
0943: public DataFlavor[] getTransferDataFlavors() {
0944: try {
0945: return new DataFlavor[] { new DataFlavor(
0946: MIME_TOP_COMPONENT_ARRAY, null,
0947: TopComponent.class.getClassLoader()) };
0948: } catch (ClassNotFoundException ex) {
0949: Logger.getLogger(
0950: TopComponentDragSupport.class.getName()).log(
0951: Level.WARNING, ex.getMessage(), ex);
0952: }
0953: return new DataFlavor[0];
0954: }
0955:
0956: /** Implements <code>Transferable</code> method.
0957: * @return <code>true</code> for <code>DataFlavor</code> with mimetype
0958: * equal to {@link #MIME_TOP_COMPONENT}
0959: * and if <code>tc</code> is instance
0960: * of <code>TopComponent.Cloneable</code> also for the one
0961: * with mimetype {@link #MIME_TOP_COMPONENT_CLONEABLE},
0962: * <code>false</code> otherwise. */
0963: public boolean isDataFlavorSupported(DataFlavor df) {
0964: if (MIME_TOP_COMPONENT_ARRAY.equals(df.getMimeType())) {
0965: return true;
0966: }
0967:
0968: return false;
0969: }
0970: // << Transferable implementation <<
0971: } // End of class TopComponentArrayTransferable.
0972:
0973: /** Fake <code>DragGestureRecognizer</code> used when starting
0974: * DnD programatically. */
0975: private static class FakeDragGestureRecognizer extends
0976: DragGestureRecognizer {
0977:
0978: /** Constructs <code>FakeDragGestureRecpgnizer</code>.
0979: * @param evt trigger event */
0980: public FakeDragGestureRecognizer(
0981: WindowDnDManager windowDnDManager, MouseEvent evt) {
0982: super (windowDnDManager.getWindowDragSource(),
0983: (Component) evt.getSource(),
0984: DnDConstants.ACTION_COPY_OR_MOVE, null);
0985:
0986: appendEvent(evt);
0987: }
0988:
0989: /** Dummy implementation of superclass abstract method. */
0990: public void registerListeners() {
0991: }
0992:
0993: /** Dummy implementation of superclass abstract method. */
0994: public void unregisterListeners() {
0995: }
0996:
0997: } // End of class FakeDragGestureRecognizer
0998:
0999: /**
1000: * Ugly fake class to pass by the issue #4752224. There is not possible
1001: * to create DataFlavor of mime type application/x-java-jvm-local-objectref
1002: * for array class type. */
1003: static class TopComponentArray {
1004: } // End of TopComponentArray.
1005:
1006: public void dragMouseMoved(DragSourceDragEvent dsde) {
1007: if (null != visualizer)
1008: visualizer.update(dsde);
1009: }
1010:
1011: /**
1012: * @return An invisible (size 1x1) image to be used for dragging to replace
1013: * the default one supplied by the operating system (if any).
1014: */
1015: private Image createDragImage() {
1016: GraphicsConfiguration config = GraphicsEnvironment
1017: .getLocalGraphicsEnvironment().getDefaultScreenDevice()
1018: .getDefaultConfiguration();
1019:
1020: BufferedImage res = config.createCompatibleImage(1, 1);
1021: Graphics2D g = res.createGraphics();
1022: g.setColor(Color.white);
1023: g.fillRect(0, 0, 1, 1);
1024: return res;
1025: }
1026: }
|