001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.explorer.view;
042:
043: import org.openide.nodes.Node;
044: import org.openide.util.RequestProcessor;
045: import org.openide.util.datatransfer.PasteType;
046:
047: import java.awt.datatransfer.*;
048: import java.awt.dnd.*;
049:
050: import javax.swing.JList;
051: import javax.swing.SwingUtilities;
052:
053: /**
054: *
055: * @author Dafe Simonek
056: */
057: final class ListViewDropSupport implements DropTargetListener, Runnable {
058: // Attributes
059:
060: /** true if support is active, false otherwise */
061: boolean active = false;
062: boolean dropTargetPopupAllowed;
063:
064: /** Drop target asociated with the tree */
065: DropTarget dropTarget;
066:
067: /** The index of last item the cursor hotspot was above */
068: int lastIndex = -1;
069:
070: // Associations
071:
072: /** View manager. */
073: protected ListView view;
074:
075: /** The component we are supporting with drop support */
076: protected JList list;
077:
078: // Operations
079: public ListViewDropSupport(ListView view, JList list) {
080: this (view, list, true);
081: }
082:
083: /** Creates new TreeViewDropSupport */
084: public ListViewDropSupport(ListView view, JList list,
085: boolean dropTargetPopupAllowed) {
086: this .view = view;
087: this .list = list;
088: this .dropTargetPopupAllowed = dropTargetPopupAllowed;
089: }
090:
091: public void setDropTargetPopupAllowed(boolean value) {
092: dropTargetPopupAllowed = value;
093: }
094:
095: public boolean isDropTargetPopupAllowed() {
096: return dropTargetPopupAllowed;
097: }
098:
099: /** User is starting to drag over us */
100: public void dragEnter(DropTargetDragEvent dtde) {
101: ExplorerDnDManager.getDefault().setMaybeExternalDragAndDrop(
102: true);
103: int dropAction = ExplorerDnDManager.getDefault()
104: .getAdjustedDropAction(dtde.getDropAction(),
105: view.getAllowedDropActions());
106:
107: lastIndex = indexWithCheck(dtde);
108:
109: if (lastIndex < 0) {
110: dtde.rejectDrag();
111: } else {
112: dtde.acceptDrag(dropAction);
113: NodeRenderer.dragEnter(list.getModel().getElementAt(
114: lastIndex));
115: list.repaint(list.getCellBounds(lastIndex, lastIndex));
116: }
117: }
118:
119: /** User drags over us */
120: public void dragOver(DropTargetDragEvent dtde) {
121: ExplorerDnDManager.getDefault().setMaybeExternalDragAndDrop(
122: true);
123: int dropAction = ExplorerDnDManager.getDefault()
124: .getAdjustedDropAction(dtde.getDropAction(),
125: view.getAllowedDropActions());
126:
127: int index = indexWithCheck(dtde);
128:
129: if (index < 0) {
130: dtde.rejectDrag();
131:
132: if (lastIndex >= 0) {
133: NodeRenderer.dragExit();
134: list.repaint(list.getCellBounds(lastIndex, lastIndex));
135: lastIndex = -1;
136: }
137: } else {
138: dtde.acceptDrag(dropAction);
139:
140: if (lastIndex != index) {
141: if (lastIndex < 0) {
142: lastIndex = index;
143: }
144:
145: NodeRenderer.dragExit();
146: NodeRenderer.dragEnter(list.getModel().getElementAt(
147: index));
148: list.repaint(list.getCellBounds(lastIndex, index));
149: lastIndex = index;
150: }
151: }
152: }
153:
154: public void dropActionChanged(DropTargetDragEvent dtde) {
155: // PENDING...?
156: }
157:
158: /** User exits the dragging */
159: public void dragExit(DropTargetEvent dte) {
160: ExplorerDnDManager.getDefault().setMaybeExternalDragAndDrop(
161: false);
162: if (lastIndex >= 0) {
163: NodeRenderer.dragExit();
164: list.repaint(list.getCellBounds(lastIndex, lastIndex));
165: }
166: }
167:
168: /** Performs the drop action, if we are dropping on
169: * right node and target node agrees.
170: */
171: public void drop(DropTargetDropEvent dtde) {
172: // obtain the node we have cursor on
173: int index = list.locationToIndex(dtde.getLocation());
174: Object obj = list.getModel().getElementAt(index);
175: Node dropNode = null;
176:
177: if (obj instanceof VisualizerNode) {
178: dropNode = ((VisualizerNode) obj).node;
179: }
180:
181: int dropAction = ExplorerDnDManager.getDefault()
182: .getAdjustedDropAction(dtde.getDropAction(),
183: view.getAllowedDropActions());
184:
185: ExplorerDnDManager.getDefault().setMaybeExternalDragAndDrop(
186: false);
187:
188: // return if conditions are not satisfied
189: if ((index < 0)
190: || !canDrop(dropNode, dropAction, dtde
191: .getTransferable(), index)) {
192: dtde.rejectDrop();
193:
194: return;
195: }
196:
197: // get paste types for given transferred transferable
198: Transferable t = ExplorerDnDManager.getDefault()
199: .getDraggedTransferable(
200: (DnDConstants.ACTION_MOVE & dropAction) != 0);
201: if (null == t)
202: t = dtde.getTransferable();
203: PasteType pt = DragDropUtilities.getDropType(dropNode, t,
204: dropAction, index);
205:
206: if (pt == null) {
207: dtde.dropComplete(false);
208:
209: // something is wrong, notify user
210: // ugly hack, but if we don't wait, deadlock will come
211: // (sun's issue....)
212: RequestProcessor.getDefault().post(this , 500);
213:
214: return;
215: }
216:
217: // finally perform the drop
218: dtde.acceptDrop(dropAction);
219:
220: if (dropAction == DnDConstants.ACTION_LINK) {
221: // show popup menu to the user
222: // PENDING
223: } else {
224: DragDropUtilities.performPaste(pt, null);
225: }
226: }
227:
228: /** Can node recieve given drop action? */
229:
230: // XXX canditate for more general support
231: private boolean canDrop(Node n, int dropAction,
232: Transferable dndEventTransferable, int dropIndex) {
233: if (n == null) {
234: return false;
235: }
236:
237: if (ExplorerDnDManager.getDefault().getNodeAllowedActions() == DnDConstants.ACTION_NONE) {
238: return false;
239: }
240:
241: // test if a parent of the dragged nodes isn't the node over
242: // only for MOVE action
243: if ((DnDConstants.ACTION_MOVE & dropAction) != 0) {
244: Node[] nodes = ExplorerDnDManager.getDefault()
245: .getDraggedNodes();
246:
247: if (null != nodes) {
248: for (int i = 0; i < nodes.length; i++) {
249: if (n.equals(nodes[i].getParentNode())) {
250: return false;
251: }
252: }
253: }
254: }
255:
256: Transferable trans = ExplorerDnDManager.getDefault()
257: .getDraggedTransferable(
258: (DnDConstants.ACTION_MOVE & dropAction) != 0);
259:
260: if (trans == null) {
261: trans = dndEventTransferable;
262: if (trans == null) {
263: return false;
264: }
265: }
266:
267: // get paste types for given transferred transferable
268: PasteType pt = DragDropUtilities.getDropType(n, trans,
269: dropAction, dropIndex);
270:
271: return (pt != null);
272: }
273:
274: /** Activates or deactivates Drag support on asociated JTree
275: * component
276: * @param active true if the support should be active, false
277: * otherwise
278: */
279: public void activate(boolean active) {
280: if (this .active == active) {
281: return;
282: }
283:
284: this .active = active;
285: getDropTarget().setActive(active);
286: }
287:
288: /** Implementation of the runnable interface.
289: * Notifies user in AWT thread. */
290: public void run() {
291: if (!SwingUtilities.isEventDispatchThread()) {
292: SwingUtilities.invokeLater(this );
293:
294: return;
295: }
296:
297: DragDropUtilities.dropNotSuccesfull();
298: }
299:
300: /** @return The tree path to the node the cursor is above now or
301: * null if no such node currently exists or if conditions were not
302: * satisfied to continue with DnD operation.
303: */
304: int indexWithCheck(DropTargetDragEvent dtde) {
305: int dropAction = ExplorerDnDManager.getDefault()
306: .getAdjustedDropAction(dtde.getDropAction(),
307: view.getAllowedDropActions());
308:
309: // check actions
310: if ((dropAction & view.getAllowedDropActions()) == 0) {
311: return -1;
312: }
313:
314: // check location
315: int index = list.locationToIndex(dtde.getLocation());
316: if (index == -1)
317: return -1;
318: Object obj = list.getModel().getElementAt(index);
319:
320: if (obj instanceof VisualizerNode) {
321: obj = ((VisualizerNode) obj).node;
322: }
323:
324: if (index < 0) {
325: return -1;
326: }
327:
328: if (!(obj instanceof Node)) {
329: return -1;
330: }
331:
332: /* JST: Is necessary? Cannot be replaced by the use of special
333: * transferable?
334:
335: // accept only node data flavors or multi flavor
336: if (!dtde.isDataFlavorSupported(NodeTransfer.nodeCutFlavor) &&
337: !dtde.isDataFlavorSupported(NodeTransfer.nodeCopyFlavor) &&
338: !dtde.isDataFlavorSupported(ExTransferable.multiFlavor))
339: return -1;
340: */
341:
342: // succeeded
343: return index;
344: }
345:
346: /** Safe accessor to the drop target which is asociated
347: * with the tree */
348: DropTarget getDropTarget() {
349: if (dropTarget == null) {
350: dropTarget = new DropTarget(list, view
351: .getAllowedDropActions(), this , false);
352: }
353:
354: return dropTarget;
355: }
356: }
|