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:
042: package org.netbeans.spi.palette;
043:
044: import java.awt.datatransfer.DataFlavor;
045: import java.awt.datatransfer.Transferable;
046: import java.awt.datatransfer.UnsupportedFlavorException;
047: import java.io.IOException;
048: import java.util.logging.Level;
049: import java.util.logging.Logger;
050: import org.netbeans.modules.palette.DefaultModel;
051: import org.openide.nodes.Index;
052: import org.openide.nodes.Node;
053: import org.openide.util.Lookup;
054: import org.openide.util.datatransfer.ExTransferable;
055: import org.openide.util.datatransfer.PasteType;
056:
057: /**
058: * <p>An abstract class implemented by palette clients to implement drag and drop
059: * of new items into the palette window and to customize the default Transferable
060: * instance of items being dragged from the palette window to editor area.</p>
061: *
062: * <p>Client's can support multiple DataFlavors that may help to enable/disable the drop
063: * when dragging an item over different editor area parts that allow only certain
064: * item types to be dropped into them.</p>
065: *
066: * @author S. Aubrecht
067: */
068: public abstract class DragAndDropHandler {
069:
070: private static DragAndDropHandler defaultHandler;
071:
072: static DragAndDropHandler getDefault() {
073: if (null == defaultHandler)
074: defaultHandler = new DefaultDragAndDropHandler();
075: return defaultHandler;
076: }
077:
078: /**
079: * Add your own custom DataFlavor as need to suppor drag-over a different
080: * parts of editor area.
081: *
082: * @param t Item's default Transferable.
083: * @param item Palette item's Lookup.
084: *
085: */
086: public abstract void customize(ExTransferable t, Lookup item);
087:
088: /**
089: * @param targetCategory Lookup of the category under the drop cursor.
090: * @param flavors Supported DataFlavors.
091: * @param dndAction Drop action type.
092: *
093: * @return True if the given category can accept the item being dragged.
094: */
095: public boolean canDrop(Lookup targetCategory, DataFlavor[] flavors,
096: int dndAction) {
097: for (int i = 0; i < flavors.length; i++) {
098: if (PaletteController.ITEM_DATA_FLAVOR.equals(flavors[i])) {
099: return true;
100: }
101: }
102: return false;
103: }
104:
105: /**
106: * Perform the drop operation and add the dragged item into the given category.
107: *
108: * @param targetCategory Lookup of the category that accepts the drop.
109: * @param item Transferable holding the item being dragged.
110: * @param dndAction Drag'n'drop action type.
111: * @param dropIndex Zero-based position where the dragged item should be dropped.
112: *
113: * @return True if the drop has been successful, false otherwise.
114: */
115: public boolean doDrop(Lookup targetCategory, Transferable item,
116: int dndAction, int dropIndex) {
117: Node categoryNode = (Node) targetCategory.lookup(Node.class);
118: try {
119: //first check if we're reordering items within the same category
120: if (item
121: .isDataFlavorSupported(PaletteController.ITEM_DATA_FLAVOR)) {
122: Lookup itemLookup = (Lookup) item
123: .getTransferData(PaletteController.ITEM_DATA_FLAVOR);
124: if (null != itemLookup) {
125: Node itemNode = (Node) itemLookup
126: .lookup(Node.class);
127: if (null != itemNode) {
128: Index order = (Index) categoryNode
129: .getCookie(Index.class);
130: if (null != order
131: && order.indexOf(itemNode) >= 0) {
132: //the drop item comes from the targetCategory so let's
133: //just change the order of items
134: return moveItem(targetCategory, itemLookup,
135: dropIndex);
136: }
137: }
138: }
139: }
140: PasteType paste = categoryNode.getDropType(item, dndAction,
141: dropIndex);
142: if (null != paste) {
143: Node[] itemsBefore = categoryNode.getChildren()
144: .getNodes(DefaultModel.canBlock());
145: paste.paste();
146: Node[] itemsAfter = categoryNode.getChildren()
147: .getNodes(DefaultModel.canBlock());
148:
149: if (itemsAfter.length == itemsBefore.length + 1) {
150: int currentIndex = -1;
151: Node newItem = null;
152: for (int i = itemsAfter.length - 1; i >= 0; i--) {
153: newItem = itemsAfter[i];
154: currentIndex = i;
155: for (int j = 0; j < itemsBefore.length; j++) {
156: if (newItem.equals(itemsBefore[j])) {
157: newItem = null;
158: break;
159: }
160: }
161: if (null != newItem) {
162: break;
163: }
164: }
165: if (null != newItem && dropIndex >= 0) {
166: if (currentIndex < dropIndex)
167: dropIndex++;
168: moveItem(targetCategory, newItem.getLookup(),
169: dropIndex);
170: }
171: }
172: return true;
173: }
174: } catch (IOException ioE) {
175: Logger.getLogger(DragAndDropHandler.class.getName()).log(
176: Level.INFO, null, ioE);
177: } catch (UnsupportedFlavorException e) {
178: Logger.getLogger(DragAndDropHandler.class.getName()).log(
179: Level.INFO, null, e);
180: }
181: return false;
182: }
183:
184: /**
185: * Move palette item to a new position in its current category.
186: *
187: * @param category Lookup of the category that contains the dragged item.
188: * @param itemToMove Lookup of the item that is going to be moved to a new position.
189: * @param moveToIndex Zero-based index to category's children where the item should move to.
190: *
191: * @return True if the move operation was successful.
192: */
193: private boolean moveItem(Lookup category, Lookup itemToMove,
194: int moveToIndex) {
195: Node categoryNode = (Node) category.lookup(Node.class);
196: if (null == categoryNode)
197: return false;
198: Node itemNode = (Node) itemToMove.lookup(Node.class);
199: if (null == itemNode)
200: return false;
201:
202: Index order = (Index) categoryNode.getCookie(Index.class);
203: if (null == order) {
204: return false;
205: }
206:
207: int sourceIndex = order.indexOf(itemNode);
208: if (sourceIndex < moveToIndex) {
209: moveToIndex--;
210: }
211: order.move(sourceIndex, moveToIndex);
212: return true;
213: }
214:
215: /**
216: * @param paletteRoot Lookup of palette's root node.
217: * @return True if it is possible to reorder categories by drag and drop operations.
218: */
219: public boolean canReorderCategories(Lookup paletteRoot) {
220: Node rootNode = (Node) paletteRoot.lookup(Node.class);
221: if (null != rootNode) {
222: return null != rootNode.getCookie(Index.class);
223: }
224: return false;
225: }
226:
227: /**
228: * Move the given category to a new position.
229: *
230: * @param category The lookup of category that is being dragged.
231: * @param moveToIndex Zero-based index to palette's root children Nodes
232: * where the category should move to.
233: * @return True if the move operation was successful.
234: */
235: public boolean moveCategory(Lookup category, int moveToIndex) {
236: Node categoryNode = (Node) category.lookup(Node.class);
237: if (null == categoryNode)
238: return false;
239: Node rootNode = categoryNode.getParentNode();
240: if (null == rootNode)
241: return false;
242:
243: Index order = (Index) rootNode.getCookie(Index.class);
244: if (null == order) {
245: return false;
246: }
247:
248: int sourceIndex = order.indexOf(categoryNode);
249: if (sourceIndex < moveToIndex) {
250: moveToIndex--;
251: }
252: order.move(sourceIndex, moveToIndex);
253: return true;
254: }
255:
256: private static final class DefaultDragAndDropHandler extends
257: DragAndDropHandler {
258: public void customize(ExTransferable t, Lookup item) {
259: //do nothing
260: }
261: }
262: }
|