KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > windows > view > dnd > TopComponentDragSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20
21 package org.netbeans.core.windows.view.dnd;
22
23
24
25 import java.awt.AWTEvent JavaDoc;
26 import java.awt.Component JavaDoc;
27 import java.awt.Cursor JavaDoc;
28 import java.awt.Dialog JavaDoc;
29 import java.awt.Image JavaDoc;
30 import java.awt.Point JavaDoc;
31 import java.awt.Rectangle JavaDoc;
32 import java.awt.Toolkit JavaDoc;
33 import java.awt.datatransfer.*;
34 import java.awt.dnd.*;
35 import java.awt.event.*;
36 import java.lang.ref.*;
37 import java.util.*;
38 import java.util.logging.Level JavaDoc;
39 import java.util.logging.Logger JavaDoc;
40 import javax.swing.*;
41 import org.netbeans.core.windows.*;
42 import org.netbeans.core.windows.view.ui.*;
43 import org.openide.util.*;
44 import org.openide.windows.TopComponent;
45
46
47
48 /**
49  * Window system drag support for <code>TopComponet</code>'s.
50  * It imitates role of drag gesture recognizer, possible
51  * on any kind of <code>Component</code>, currently on <code>Tabbed</code>.
52  * Starts also programatically the DnD for TopComponent in container
53  * when the starting gestures are Shift+Mouse Drag or Ctrl+Mouse Drag
54  * respectivelly.
55  * It serves as <code>DragSourceListener</code> during the DnD in progress
56  * and sets dragging cursor appopriatelly.
57  *
58  * <em>Note:</em> There is used only one singleton instance in window system
59  * DnD available via {@link #getDefault}.
60  *
61  *
62  * @author Peter Zavadsky
63  *
64  * @see java awt.dnd.DragSourceListener
65  */

66 final class TopComponentDragSupport
67 implements AWTEventListener, DragSourceListener {
68     
69     /** Mime type for <code>TopComponent</code> <code>DataFlavor</code>. */
70     public static final String JavaDoc MIME_TOP_COMPONENT =
71         DataFlavor.javaJVMLocalObjectMimeType
72         // Note: important is the space after semicolon, thus to match
73
// when comparing.
74
+ "; class=org.openide.windows.TopComponent"; // NOI18N
75

76     /** Mime type for <code>TopComponent.Cloneable</code> <code>DataFlavor</code>. */
77     public static final String JavaDoc MIME_TOP_COMPONENT_CLONEABLE =
78         DataFlavor.javaJVMLocalObjectMimeType
79         + "; class=org.openide.windows.TopComponent$Cloneable"; // NOI18N
80

81
82     /** Mime type for <code>TopComponent</code>'s array <code>DataFlavor</code>. */
83     public static final String JavaDoc MIME_TOP_COMPONENT_ARRAY =
84         DataFlavor.javaJVMLocalObjectMimeType
85         + "; class=org.netbeans.core.windows.view.dnd.TopComponentDragSupport$TopComponentArray"; // NOI18N
86

87     
88     /** 'Copy window' cursor type. */
89     private static final int CURSOR_COPY = 0;
90     /** 'Copy_No window' cursor type. */
91     private static final int CURSOR_COPY_NO = 1;
92     /** 'Move window' cursor type. */
93     private static final int CURSOR_MOVE = 2;
94     /** 'Move_No window' cursor type. */
95     private static final int CURSOR_MOVE_NO = 3;
96     /** Cursor type indicating there cannont be copy operation
97      * done, but could be done move operation. In fact is
98      * the same like {@link #CURSOR_COPY_NO} with the diff name
99      * to be recognized correctly when switching action over drop target */

100     private static final int CURSOR_COPY_NO_MOVE = 4;
101     /** Move to free area cursor type */
102     private static final int CURSOR_MOVE_FREE = 5;
103
104     /** Name for 'Copy window' cursor. */
105     private static final String JavaDoc NAME_CURSOR_COPY = "CursorTopComponentCopy"; // NOI18N
106
/** Name for 'Copy_No window' cursor. */
107     private static final String JavaDoc NAME_CURSOR_COPY_NO = "CursorTopComponentCopyNo"; // NOI18N
108
/** Name for 'Move window' cursor. */
109     private static final String JavaDoc NAME_CURSOR_MOVE = "CursorTopComponentMove"; // NOI18N
110
/** Name for 'Move_No window' cursor. */
111     private static final String JavaDoc NAME_CURSOR_MOVE_NO = "CursorTopComponentMoveNo"; // NOI18N
112
/** */
113     private static final String JavaDoc NAME_CURSOR_COPY_NO_MOVE = "CursorTopComponentCopyNoMove"; // NOI18N
114
/** Name for cursor to drop to free area. */
115     private static final String JavaDoc NAME_CURSOR_MOVE_FREE = "CursorTopComponentMoveFree"; // NOI18N
116

117     /** Debugging flag. */
118     private static final boolean DEBUG = Debug.isLoggable(TopComponentDragSupport.class);
119     
120     private final WindowDnDManager windowDnDManager;
121
122     /** Weak reference to <code>DragSourceContext</code> used in processed
123      * drag operation. Used for by fixing bugs while not passed correct
124      * order of events to <code>DragSourceListener</code>. */

125     private Reference<DragSourceContext> dragContextWRef = new WeakReference<DragSourceContext>(null);
126     
127     /** Flag indicating the current window drag operation transferable
128      * can be 'copied', i.e. the dragged <code>TopComponent</code> is
129      * <code>TopComponent.Cloneable</code> instance. */

130     private boolean canCopy;
131     
132     // #21918. There is not possible to indicate drop action in "free" desktop
133
// area. This field helps to workaround the problem.
134
/** Flag indicating user drop action. */
135     private int hackUserDropAction;
136
137     // #21918. Determine the ESC pressed.
138
/** Flag indicating the user has cancelled drag operation by pressing ESC key. */
139     private boolean hackESC;
140
141     /** Weak set of componens on which we listen for ESC key. */
142     private final Set keyObservers = new WeakSet(4);
143
144     private Point JavaDoc startingPoint;
145     private Component JavaDoc startingComponent;
146     private long startingTime;
147     
148     
149     /** Creates a new instance of TopComponentDragSupport. */
150     TopComponentDragSupport(WindowDnDManager windowDnDManager) {
151         this.windowDnDManager = windowDnDManager;
152     }
153
154     
155     /** Informs whether the 'copy' operation is possible. Gets valid result
156      * during processed drag operation only.
157      * @return <code>true</code> if the drop copy operation is possible from
158      * drag source point of view
159      * @see #canCopy */

160     public boolean isCopyOperationPossible() {
161         return canCopy;
162     }
163
164     /** Simulates drag gesture recongition valid for winsys.
165      * Implements <code>AWTEventListener</code>. */

166     public void eventDispatched(AWTEvent JavaDoc evt) {
167         MouseEvent me = (MouseEvent) evt;
168
169         // #40736: only left mouse button drag should start DnD
170
if((me.getID() == MouseEvent.MOUSE_PRESSED) && SwingUtilities.isLeftMouseButton(me)) {
171                 startingPoint = me.getPoint();
172             startingComponent = me.getComponent();
173             startingTime = me.getWhen();
174         } else if(me.getID() == MouseEvent.MOUSE_RELEASED) {
175             startingPoint = null;
176             startingComponent = null;
177         }
178         
179         if(evt.getID() != MouseEvent.MOUSE_DRAGGED) {
180             return;
181         }
182         if(windowDnDManager.isDragging()) {
183             return;
184         }
185         if(startingPoint == null) {
186             return;
187         }
188         if( evt.getSource() instanceof JButton ) {
189             //do not initiate topcomponent drag when the mouse is dragged out of a tabcontrol button
190
return;
191         }
192
193         Component JavaDoc srcComp = startingComponent;
194         if(srcComp == null) {
195             return;
196         }
197         
198         final Point JavaDoc point = new Point JavaDoc(startingPoint);
199         Point JavaDoc currentPoint = me.getPoint();
200         Component JavaDoc currentComponent = me.getComponent();
201         if(currentComponent == null) {
202             return;
203         }
204         currentPoint = SwingUtilities.convertPoint(currentComponent, currentPoint, srcComp);
205         if(Math.abs(currentPoint.x - point.x) <= Constants.DRAG_GESTURE_START_DISTANCE
206         && Math.abs(currentPoint.y - point.y) <= Constants.DRAG_GESTURE_START_DISTANCE) {
207             return;
208         }
209         // time check, to prevent wild mouse clicks to be considered DnD start
210
if (me.getWhen() - startingTime <= Constants.DRAG_GESTURE_START_TIME) {
211             return;
212         }
213         startingPoint = null;
214         startingComponent = null;
215         
216         if(DEBUG) {
217             debugLog(""); // NOI18N
218
debugLog("eventDispatched (MOUSE_DRAGGED)"); // NOI18N
219
}
220         
221         // XXX Do not clash with JTree (e.g. in explorer) drag.
222
if((srcComp instanceof JTree)
223         && ((JTree)srcComp).getPathForLocation(me.getX(), me.getY()) != null) {
224             return;
225         }
226         
227         // #22622: AWT listener just passivelly listnens what is happenning around,
228
// and we need always the deepest component to start from.
229
srcComp = SwingUtilities.getDeepestComponentAt(srcComp, point.x, point.y);
230
231         boolean ctrlDown = me.isControlDown();
232         
233         TopComponent tc = null;
234         Tabbed tabbed;
235
236         if(srcComp instanceof Tabbed) {
237             tabbed = (Tabbed)srcComp;
238         } else {
239             tabbed = (Tabbed)SwingUtilities.getAncestorOfClass(Tabbed.class, srcComp);
240         }
241         if (tabbed == null) {
242             if(srcComp instanceof Tabbed.Accessor) {
243                 tabbed = ((Tabbed.Accessor)srcComp).getTabbed();
244             } else {
245                 Tabbed.Accessor acc = (Tabbed.Accessor)SwingUtilities.getAncestorOfClass(Tabbed.Accessor.class, srcComp);
246                 tabbed = acc != null ? acc.getTabbed() : null;
247             }
248         }
249         if(tabbed == null) {
250             return;
251         }
252         
253         Point JavaDoc ppp = new Point JavaDoc(point);
254         Point JavaDoc p = SwingUtilities.convertPoint(srcComp, ppp, tabbed.getComponent());
255         
256         tc = tabbed.getTopComponentAt(tabbed.tabForCoordinate(p));
257         if (tc == null) {
258             return;
259         }
260
261         // #21918. See above.
262
if (ctrlDown) {
263             hackUserDropAction = DnDConstants.ACTION_COPY;
264         }
265         else {
266             hackUserDropAction = DnDConstants.ACTION_MOVE;
267         }
268                  
269
270         List<MouseEvent> list = new ArrayList<MouseEvent>();
271         list.add(me);
272
273         // Get start droppable (if there) and its starting point.
274
TopComponentDroppable startDroppable = (TopComponentDroppable)SwingUtilities
275                             .getAncestorOfClass(TopComponentDroppable.class, tc);
276         Point JavaDoc startPoint;
277         if (startDroppable == null) {
278             startDroppable = (TopComponentDroppable)SwingUtilities
279                                 .getAncestorOfClass(TopComponentDroppable.class, tabbed.getComponent());
280         }
281         if(startDroppable != null) {
282             startPoint = point;
283             Point JavaDoc pp = new Point JavaDoc(point);
284             startPoint = SwingUtilities.convertPoint(srcComp, pp, (Component JavaDoc)startDroppable);
285         } else {
286             startPoint = null;
287         }
288         //dragSource.startDrag(event, Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR) ,image , new Point(-offX, -offY),text, this);
289

290         doStartDrag(
291             srcComp,
292             tc,
293             new DragGestureEvent(
294                 new FakeDragGestureRecognizer(windowDnDManager, me),
295                 hackUserDropAction,
296                 point,
297                 list
298             ),
299             startDroppable,
300             startPoint
301         );
302     }
303     
304     /** Actually starts the drag operation. */
305     private void doStartDrag(Component JavaDoc startingComp, Object JavaDoc transfer, DragGestureEvent evt,
306     TopComponentDroppable startingDroppable, Point JavaDoc startingPoint) {
307         if(DEBUG) {
308             debugLog(""); // NOI18N
309
debugLog("doStartDrag"); // NOI18N
310
}
311         
312         TopComponent firstTC = transfer instanceof TopComponent
313                 ? (TopComponent)transfer
314                 : (((TopComponent[])transfer)[0]);
315         
316         // #22132. If in modal dialog no drag allowed.
317
Dialog JavaDoc dlg = (Dialog JavaDoc)SwingUtilities.getAncestorOfClass(
318                 Dialog JavaDoc.class, firstTC);
319         if(dlg != null && dlg.isModal()) {
320             return;
321         }
322         
323         if(firstTC instanceof TopComponent.Cloneable) {
324             canCopy = true;
325         } else {
326             canCopy = false;
327         }
328         
329         // Inform window sys there is DnD about to start.
330
// XXX Using the firstTC in DnD manager is a hack.
331
windowDnDManager.dragStarting(startingDroppable, startingPoint, firstTC);
332
333         Cursor JavaDoc cursor = hackUserDropAction == DnDConstants.ACTION_MOVE
334             ? getDragCursor(startingComp, CURSOR_MOVE)
335             : (canCopy
336                 ? getDragCursor(startingComp, CURSOR_COPY)
337                 : getDragCursor(startingComp, CURSOR_COPY_NO_MOVE));
338
339         // Sets listnening for ESC key.
340
addListening();
341         hackESC = false;
342         
343         Tabbed tabbed = (Tabbed) SwingUtilities.getAncestorOfClass (Tabbed.class,
344             startingComp);
345         if (tabbed == null) {
346             Tabbed.Accessor acc = (Tabbed.Accessor) SwingUtilities.getAncestorOfClass (Tabbed.Accessor.class,
347                                                                                        startingComp);
348             tabbed = acc != null ? acc.getTabbed() : null;
349         }
350         
351         Image JavaDoc img = null;
352         if (tabbed != null && Constants.SWITCH_USE_DRAG_IMAGES) {
353             int idx = tabbed.indexOf(firstTC);
354             img = tabbed.createImageOfTab(idx);
355         }
356         try {
357             evt.startDrag(
358                 cursor,
359                 img,
360                 new Point JavaDoc (0,0),
361                 (transfer instanceof TopComponent
362                         ? (Transferable)new TopComponentTransferable(
363                                 (TopComponent)transfer)
364                         : (Transferable)new TopComponentArrayTransferable(
365                                 (TopComponent[])transfer)),
366                 this
367             );
368         } catch(InvalidDnDOperationException idoe) {
369             Logger.getLogger(TopComponentDragSupport.class.getName()).log(Level.WARNING, null, idoe);
370             
371             removeListening();
372             windowDnDManager.resetDragSource();
373         }
374     }
375
376     private AWTEventListener keyListener = new AWTEventListener() {
377             public void eventDispatched(AWTEvent JavaDoc event) {
378                 KeyEvent keyevent = (KeyEvent)event;
379                 
380                 if (keyevent.getID() == KeyEvent.KEY_RELEASED &&
381                     keyevent.getKeyCode() == KeyEvent.VK_ESCAPE) {
382                     hackESC = true;
383                 }
384             }
385             
386         };
387     /** Adds <code>KeyListener</code> to container and its component
388      * hierarchy to listen for ESC key. */

389     private void addListening() {
390         Toolkit.getDefaultToolkit().addAWTEventListener(keyListener, AWTEvent.KEY_EVENT_MASK);
391     }
392     
393     /** Removes ESC listening. Helper method. */
394     private void removeListening() {
395         Toolkit.getDefaultToolkit().removeAWTEventListener(keyListener);
396     }
397     
398     // >> DragSourceListener implementation >>
399
/** Implements <code>DragSourceListener</code> method.
400      * It just refreshes the weak reference of <code>DragSourceContext</code>
401      * for the sake of setSuccessCursor method.
402      * The excpected code, changing of cursor, is done in setSuccessCursor method
403      * due to an undeterministic calls of this method especially in MDI mode.
404      * @see #setSuccessCursor */

405     public void dragEnter(DragSourceDragEvent evt) {
406         if(DEBUG) {
407             debugLog(""); // NOI18N
408
debugLog("dragEnter");// NOI18N
409
}
410             
411         // Just refresh the weak ref to the context if necessary.
412
// The expected code here is done by ragExitHack method called from DropTarget's.
413
if(dragContextWRef.get() == null) {
414             dragContextWRef = new java.lang.ref.WeakReference JavaDoc<DragSourceContext>(evt.getDragSourceContext());
415         }
416     }
417
418     /** Dummy implementation of <code>DragSourceListener</code> method. */
419     public void dragOver(DragSourceDragEvent evt) {
420         if(DEBUG) {
421             debugLog(""); // NOI18N
422
debugLog("dragOver"); // NOI18N
423
}
424     }
425
426     /** Implements <code>DragSourceListener</code> method.
427      * It just refreshes the weak reference of <code>DragSourceContext</code>
428      * for the sake of setUnsuccessCursor method.
429      * The excpected code, changing of cursor, is done in setUnsuccessCursor method
430      * due to an undeterministic calls of this method especially in MDI mode.
431      * @see #setUnsuccessCursor */

432     public void dragExit(DragSourceEvent evt) {
433         if(DEBUG) {
434             debugLog(""); // NOI18N
435
debugLog("dragExit"); // NOI18N
436
}
437         
438         // Just refresh the weak ref to the context if necessary.
439
// The expected code here is done by ragExitHack method called from DropTarget's.
440
if(dragContextWRef.get() == null) {
441               dragContextWRef = new WeakReference<DragSourceContext>(evt.getDragSourceContext());
442         }
443     }
444     
445     /** Implements <code>DragSourceListener</code> method.
446      * It changes the cursor type from copy to move and bakc accordting the
447      * user action. */

448     public void dropActionChanged(DragSourceDragEvent evt) {
449         if(DEBUG) {
450             debugLog(""); // NOI18N
451
debugLog("dropActionChanged"); // NOI18N
452
}
453         String JavaDoc name = evt.getDragSourceContext().getCursor().getName();
454         
455         if(name == null) {
456             // Not our cursor??
457
return;
458         }
459
460         // For us is the user action important.
461
int userAction = evt.getUserAction();
462
463         // Consider NONE action as MOVE one.
464
if(userAction == DnDConstants.ACTION_NONE) {
465             userAction = DnDConstants.ACTION_MOVE;
466         }
467         // #21918. See above.
468
hackUserDropAction = userAction;
469         
470         int type;
471         if((NAME_CURSOR_COPY.equals(name)
472         || NAME_CURSOR_COPY_NO_MOVE.equals(name))
473         && userAction == DnDConstants.ACTION_MOVE) {
474             type = CURSOR_MOVE;
475         } else if(NAME_CURSOR_COPY_NO.equals(name)
476         && userAction == DnDConstants.ACTION_MOVE) {
477             type = CURSOR_MOVE_NO;
478         } else if(NAME_CURSOR_MOVE.equals(name)
479         && userAction == DnDConstants.ACTION_COPY) {
480             type = CURSOR_COPY;
481         } else if(NAME_CURSOR_MOVE_NO.equals(name)
482         && userAction == DnDConstants.ACTION_COPY) {
483             type = CURSOR_COPY_NO;
484         } else {
485             return;
486         }
487
488         // There can't be copy operation performed,
489
// transferreed TopComponent in not of TopComponent.Cloneable instance.
490
if(type == CURSOR_COPY && !canCopy) {
491             type = CURSOR_COPY_NO_MOVE;
492         }
493
494         // Check if there is already our cursor.
495
if(getDragCursorName(type).equals(
496         evt.getDragSourceContext().getCursor().getName())) {
497             return;
498         }
499         
500         evt.getDragSourceContext().setCursor(getDragCursor(evt.getDragSourceContext().getComponent(),type));
501     }
502
503     /** Implements <code>DragSourceListener</code> method.
504      * Informs window dnd manager the drag operation finished.
505      * @see WindowDnDManager#dragFinished */

506     public void dragDropEnd(final DragSourceDropEvent evt) {
507         if(DEBUG) {
508             debugLog(""); // NOI18N
509
debugLog("dragDropEnd"); // NOI18N
510
}
511         
512         windowDnDManager.dragFinished();
513         
514         try {
515             if(checkDropSuccess(evt)) {
516                 removeListening();
517                 return;
518             }
519             
520             // Now simulate drop into "free" desktop area.
521

522             // Finally schedule the "drop" task later to be able to
523
// detect if ESC was pressed.
524
RequestProcessor.getDefault().post(new Runnable JavaDoc() {
525                 public void run() {
526                     SwingUtilities.invokeLater(createDropIntoFreeAreaTask(
527                             evt, evt.getLocation()));
528                 }},
529                 250 // XXX #21918, Neccessary to skip after possible ESC key event.
530
);
531         } finally {
532             windowDnDManager.dragFinishedEx();
533         }
534     }
535     // << DragSourceListener implementation <<
536

537     /** Checks whether there was a successfull drop. */
538     private boolean checkDropSuccess(DragSourceDropEvent evt) {
539         // XXX #21917.
540
if(windowDnDManager.isDropSuccess()) {
541             return true;
542         }
543         
544         // Gets location.
545
Point JavaDoc location = evt.getLocation();
546         if(location == null) {
547             return true;
548         }
549         
550         if(WindowDnDManager.isInMainWindow(location)
551         || windowDnDManager.isInFloatingFrame(location)
552         || WindowDnDManager.isAroundCenterPanel(location)) {
553             return false;
554         }
555 // else if(evt.getDropSuccess()) {
556
// return true;
557
// } // PENDING it seem it is not working correctly (at least on linux).
558
return false;
559     }
560     
561     /** Creates task which performs actual drop into "free area", i.e. it
562      * creates new separated (floating) window. */

563     private Runnable JavaDoc createDropIntoFreeAreaTask(final DragSourceDropEvent evt,
564     final Point JavaDoc location) {
565         return new Runnable JavaDoc() {
566             public void run() {
567                 // XXX #21918. Don't move the check sooner
568
// (before the enclosing blocks), it would be invalid.
569
if(hackESC) {
570                     removeListening();
571                     return;
572                 }
573
574                 TopComponent[] tcArray = WindowDnDManager.extractTopComponent(
575                     false,
576                     evt.getDragSourceContext().getTransferable()
577                 );
578                 
579                 // Provide actual drop into "free" desktop area.
580
if(tcArray != null) {
581                     // XXX there is a problem if jdk dnd framework sets as drop action
582
// ACTION_NONE, there is not called drop event on DropTargetListener,
583
// even it is there.
584
// Performs hacked drop action, simulates ACTION_MOVE when
585
// system set ACTION_NONE (which we do not use).
586
windowDnDManager.tryPerformDrop(
587                         windowDnDManager.getController(),
588                         windowDnDManager.getFloatingFrames(),
589                         location,
590                         DnDConstants.ACTION_MOVE, // MOVE only
591
evt.getDragSourceContext().getTransferable());
592                 }
593             }
594         };
595     }
596
597     /** Gets bounds for the new mode created in the "free area". */
598     private static Rectangle JavaDoc getBoundsForNewMode(TopComponent tc, Point JavaDoc location) {
599         int width = tc.getWidth();
600         int height = tc.getHeight();
601         
602         // Take also the native title and borders into account.
603
java.awt.Window JavaDoc window = SwingUtilities.getWindowAncestor(tc);
604         if(window != null) {
605             java.awt.Insets JavaDoc ins = window.getInsets();
606             width += ins.left + ins.right;
607             height += ins.top + ins.bottom;
608         }
609         // PENDING else { how to get the insets of newly created window? }
610

611         Rectangle JavaDoc tcBounds = tc.getBounds();
612         Rectangle JavaDoc initBounds = new Rectangle JavaDoc(
613             location.x,
614             location.y,
615             width,
616             height
617         );
618
619         return initBounds;
620     }
621     
622     /** Hacks problems with <code>dragEnter</code> wrong method calls.
623      * It plays its role. Sets the cursor from 'no-drop' state
624      * to its 'drop' state sibling.
625      * @param freeArea true when mouse pointer in free screen area
626      * @see #dragEnter */

627     void setSuccessCursor (boolean freeArea) {
628         int dropAction = hackUserDropAction;
629         DragSourceContext ctx = dragContextWRef.get();
630         
631         if(ctx == null) {
632             return;
633         }
634
635         int type;
636         if(dropAction == DnDConstants.ACTION_MOVE) {
637             type = freeArea ? CURSOR_MOVE_FREE : CURSOR_MOVE;
638         } else if(dropAction == DnDConstants.ACTION_COPY) {
639             if(canCopy) {
640                 type = CURSOR_COPY;
641             } else {
642                 type = CURSOR_COPY_NO_MOVE;
643             }
644         } else {
645             // PENDING throw exception?
646
Logger.getLogger(TopComponentDragSupport.class.getName()).log(Level.WARNING, null,
647                               new java.lang.IllegalStateException JavaDoc("Invalid action type->" +
648                                                                   dropAction)); // NOI18N
649
return;
650         }
651
652         // Check if there is already our cursor.
653
if(getDragCursorName(type).equals(ctx.getCursor().getName())) {
654             return;
655         }
656
657         ctx.setCursor(getDragCursor(ctx.getComponent(),type));
658     }
659     
660     /** Hacks problems with <code>dragExit</code> wrong method calls.
661      * It plays its role. Sets the cursor from 'drop' state
662      * to its 'no-drop' state sibling.
663      * @see #dragExit */

664     void setUnsuccessCursor() {
665         DragSourceContext ctx = dragContextWRef.get();
666         
667         if(ctx == null) {
668             return;
669         }
670         
671         String JavaDoc name = ctx.getCursor().getName();
672         
673         int type;
674         if(NAME_CURSOR_COPY.equals(name)
675         || NAME_CURSOR_COPY_NO_MOVE.equals(name)) {
676             type = CURSOR_COPY_NO;
677         } else if(NAME_CURSOR_MOVE.equals(name) || NAME_CURSOR_MOVE_NO.equals(name)) {
678             type = CURSOR_MOVE_NO;
679         } else {
680             return;
681         }
682
683         ctx.setCursor(getDragCursor(ctx.getComponent(),type));
684     }
685
686     /** Provides cleanup when finished drag operation. Ideally the code
687      * should reside in {@ling #dragDropEnd} method only. But that one
688      * is not called in case of error in DnD framework. */

689     void dragFinished() {
690         dragContextWRef = new WeakReference<DragSourceContext>(null);
691     }
692    
693     private static void debugLog(String JavaDoc message) {
694         Debug.log(TopComponentDragSupport.class, message);
695     }
696     
697     // Helpers>>
698
/** Gets window drag <code>Cursor</code> of specified type. Utility method.
699      * @param type valid one of {@link #CURSOR_COPY}, {@link #CURSOR_COPY_NO},
700      * {@link #CURSOR_MOVE}, {@link #CURSOR_MOVE_NO}, {@link #CURSOR_MOVE_FREE} */

701     private static String JavaDoc getDragCursorName(int type) {
702         if(type == CURSOR_COPY) {
703             return NAME_CURSOR_COPY;
704         } else if(type == CURSOR_COPY_NO) {
705             return NAME_CURSOR_COPY_NO;
706         } else if(type == CURSOR_MOVE) {
707             return NAME_CURSOR_MOVE;
708         } else if(type == CURSOR_MOVE_NO) {
709             return NAME_CURSOR_MOVE_NO;
710         } else if(type == CURSOR_COPY_NO_MOVE) {
711             return NAME_CURSOR_COPY_NO_MOVE;
712         } else if(type == CURSOR_MOVE_FREE) {
713             return NAME_CURSOR_MOVE_FREE;
714         } else {
715             return null;
716         }
717     }
718     
719     /** Gets window drag <code>Cursor</code> of specified type. Utility method.
720      * @param type valid one of {@link #CURSOR_COPY}, {@link #CURSOR_COPY_NO},
721      * {@link #CURSOR_MOVE}, {@link #CURSOR_MOVE_NO}, {@link #CURSOR_MOVE_FREE}
722      * @exception IllegalArgumentException if invalid type parameter passed in */

723     private static Cursor JavaDoc getDragCursor( Component JavaDoc comp, int type ) {
724         Image JavaDoc image = null;
725         String JavaDoc name = null;
726         
727         if(type == CURSOR_COPY) {
728             image = Utilities.loadImage(
729                 "org/netbeans/core/resources/topComponentDragCopy.gif"); // NOI18N
730
name = NAME_CURSOR_COPY;
731         } else if(type == CURSOR_COPY_NO) {
732             image = Utilities.loadImage(
733                 "org/netbeans/core/resources/topComponentDragCopyNo.gif"); // NOI18N
734
name = NAME_CURSOR_COPY_NO;
735         } else if(type == CURSOR_MOVE) {
736             image = Utilities.loadImage(
737                 "org/netbeans/core/resources/topComponentDragMove.gif"); // NOI18N
738
name = NAME_CURSOR_MOVE;
739         } else if(type == CURSOR_MOVE_NO) {
740             image = Utilities.loadImage(
741                 "org/netbeans/core/resources/topComponentDragMoveNo.gif"); // NOI18N
742
name = NAME_CURSOR_MOVE_NO;
743         } else if(type == CURSOR_COPY_NO_MOVE) {
744             image = Utilities.loadImage(
745                 "org/netbeans/core/resources/topComponentDragCopyNo.gif"); // NOI18N
746
name = NAME_CURSOR_COPY_NO_MOVE;
747         } else if(type == CURSOR_MOVE_FREE) {
748             image = Utilities.loadImage(
749                 "org/netbeans/core/windows/resources/topComponentDragMoveFreeArea.gif"); // NOI18N
750
name = NAME_CURSOR_MOVE_FREE;
751         } else {
752             throw new IllegalArgumentException JavaDoc("Unknown cursor type=" + type); // NOI18N
753
}
754         
755         return Utilities.createCustomCursor( comp, image, name );
756     }
757
758     // Helpers<<
759

760     /** <code>Transferable</code> used for <code>TopComponent</code> instances
761      * to be used in window system DnD. */

762     private static class TopComponentTransferable extends Object JavaDoc
763     implements Transferable {
764
765         /** <code>TopComponent</code> to be transferred. */
766         private TopComponent tc;
767
768         
769         /** Crates <code>Transferable</code> for specified <code>TopComponent</code> */
770         public TopComponentTransferable(TopComponent tc) {
771             this.tc = tc;
772         }
773
774         
775         // >> Transferable implementation >>
776
/** Implements <code>Transferable</code> method.
777          * @return <code>TopComponent</code> instance for <code>DataFlavor</code>
778          * with mimetype equal to {@link #MIME_TOP_COMPONENT} or if mimetype
779          * equals to {@link #MIME_CLONEABLE_TOP_COMPONENT} and the top component
780          * is instance of <code>TopComponent.Cloneable</code> returns the instance */

781         public Object JavaDoc getTransferData(DataFlavor df) {
782             if(MIME_TOP_COMPONENT.equals(df.getMimeType())) {
783                 return tc;
784             } else if(MIME_TOP_COMPONENT_CLONEABLE.equals(
785                 df.getMimeType())
786             && tc instanceof TopComponent.Cloneable) {
787                 return tc;
788             }
789
790             return null;
791         }
792
793         /** Implements <code>Transferable</code> method.
794          * @return Array of <code>DataFlavor</code> with mimetype
795          * {@link #MIME_TOP_COMPONENT} and also with mimetype
796          * {@link #MIME_CLONEABLE_TOP_COMPONENT}
797          * if the <code>tc</code> is instance
798          * of <code>TopComponent.Cloneable</code> */

799         public DataFlavor[] getTransferDataFlavors() {
800             try {
801                 if(tc instanceof TopComponent.Cloneable) {
802                     return new DataFlavor[]{
803                         new DataFlavor(MIME_TOP_COMPONENT, null, TopComponent.class.getClassLoader()),
804                         new DataFlavor(MIME_TOP_COMPONENT_CLONEABLE, null, TopComponent.Cloneable.class.getClassLoader())};
805                 } else {
806                     return new DataFlavor[] {
807                         new DataFlavor(MIME_TOP_COMPONENT, null, TopComponent.class.getClassLoader())
808                     };
809                 }
810             } catch (ClassNotFoundException JavaDoc ex) {
811                 Logger.getLogger(TopComponentDragSupport.class.getName()).log(
812                         Level.WARNING, ex.getMessage(), ex);
813             }
814             return new DataFlavor[0];
815         }
816
817         /** Implements <code>Transferable</code> method.
818          * @return <code>true</code> for <code>DataFlavor</code> with mimetype
819          * equal to {@link #MIME_TOP_COMPONENT}
820          * and if <code>tc</code> is instance
821          * of <code>TopComponent.Cloneable</code> also for the one
822          * with mimetype {@link #MIME_TOP_COMPONENT_CLONEABLE},
823          * <code>false</code> otherwise */

824         public boolean isDataFlavorSupported(DataFlavor df) {
825             if(MIME_TOP_COMPONENT.equals(df.getMimeType())) {
826                 return true;
827             } else if(MIME_TOP_COMPONENT_CLONEABLE.equals(
828                 df.getMimeType())
829             && tc instanceof TopComponent.Cloneable) {
830                 return true;
831             }
832
833             return false;
834         }
835         // << Transferable implementation <<
836
} // End of class TopComponentTransferable.
837

838     /** <code>Transferable</code> used for <code>TopComponent</code> instances
839      * to be used in window system DnD. */

840     private static class TopComponentArrayTransferable extends Object JavaDoc
841     implements Transferable {
842
843         /** <code>TopComponent</code> to be transferred. */
844         private TopComponent[] tcArray;
845
846         
847         /** Crates <code>Transferable</code> for specified <code>TopComponent</code> */
848         public TopComponentArrayTransferable(TopComponent[] tcArray) {
849             this.tcArray = tcArray;
850         }
851
852         
853         // >> Transferable implementation >>
854
/** Implements <code>Transferable</code> method.
855          * @return <code>TopComponent</code> instance for <code>DataFlavor</code>
856          * with mimetype equal to {@link #MIME_TOP_COMPONENT}
857          * or if mimetype equals to
858          * {@link #MIME_CLONEABLE_TOP_COMPONENT} and
859          * the top component is instance
860          * of <code>TopComponent.Cloneable</code> returns the instance. */

861         public Object JavaDoc getTransferData(DataFlavor df) {
862             if(MIME_TOP_COMPONENT_ARRAY.equals(df.getMimeType())) {
863                 return tcArray;
864             }
865             return null;
866         }
867
868         /** Implements <code>Transferable</code> method.
869          * @return Array of <code>DataFlavor</code> with mimetype
870          * {@link #MIME_TOP_COMPONENT} and also with mimetype
871          * {@link #MIME_CLONEABLE_TOP_COMPONENT}
872          * if the <code>tc</code> is
873          * instance of <code>TopComponent.Cloneable</code> */

874         public DataFlavor[] getTransferDataFlavors() {
875             try {
876                 return new DataFlavor[]{new DataFlavor(MIME_TOP_COMPONENT_ARRAY,
877                                                        null,
878                                                        TopComponent.class.getClassLoader())};
879             }
880             catch (ClassNotFoundException JavaDoc ex) {
881                 Logger.getLogger(TopComponentDragSupport.class.getName()).log(
882                         Level.WARNING, ex.getMessage(), ex);
883             }
884             return new DataFlavor[0];
885         }
886
887         /** Implements <code>Transferable</code> method.
888          * @return <code>true</code> for <code>DataFlavor</code> with mimetype
889          * equal to {@link #MIME_TOP_COMPONENT}
890          * and if <code>tc</code> is instance
891          * of <code>TopComponent.Cloneable</code> also for the one
892          * with mimetype {@link #MIME_TOP_COMPONENT_CLONEABLE},
893          * <code>false</code> otherwise. */

894         public boolean isDataFlavorSupported(DataFlavor df) {
895             if(MIME_TOP_COMPONENT_ARRAY.equals(
896             df.getMimeType())) {
897                 return true;
898             }
899
900             return false;
901         }
902         // << Transferable implementation <<
903
} // End of class TopComponentArrayTransferable.
904

905     
906     /** Fake <code>DragGestureRecognizer</code> used when starting
907      * DnD programatically. */

908     private static class FakeDragGestureRecognizer extends DragGestureRecognizer {
909
910         /** Constructs <code>FakeDragGestureRecpgnizer</code>.
911          * @param evt trigger event */

912         public FakeDragGestureRecognizer(WindowDnDManager windowDnDManager, MouseEvent evt) {
913             super(windowDnDManager.getWindowDragSource(),
914                 (Component JavaDoc)evt.getSource(), DnDConstants.ACTION_COPY_OR_MOVE, null);
915
916             appendEvent(evt);
917         }
918
919         /** Dummy implementation of superclass abstract method. */
920         public void registerListeners() {}
921         /** Dummy implementation of superclass abstract method. */
922         public void unregisterListeners() {}
923         
924     } // End of class FakeDragGestureRecognizer
925

926     
927     /**
928      * Ugly fake class to pass by the issue #4752224. There is not possible
929      * to create DataFlavor of mime type application/x-java-jvm-local-objectref
930      * for array class type. */

931     static class TopComponentArray {
932     } // End of TopComponentArray.
933

934 }
935
Popular Tags