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.modules.palette.ui;
043:
044: import java.awt.Component;
045: import java.awt.Point;
046: import java.awt.Rectangle;
047: import java.awt.datatransfer.Transferable;
048: import java.awt.dnd.DnDConstants;
049: import java.awt.dnd.DragGestureEvent;
050: import java.awt.dnd.DragGestureListener;
051: import java.awt.dnd.DragGestureRecognizer;
052: import java.awt.dnd.DragSource;
053: import java.awt.dnd.DragSourceAdapter;
054: import java.awt.dnd.DragSourceDropEvent;
055: import java.awt.dnd.DragSourceListener;
056: import java.awt.dnd.DropTarget;
057: import java.awt.dnd.DropTargetDragEvent;
058: import java.awt.dnd.DropTargetDropEvent;
059: import java.awt.dnd.DropTargetEvent;
060: import java.awt.dnd.DropTargetListener;
061: import java.awt.dnd.InvalidDnDOperationException;
062: import java.awt.event.ActionEvent;
063: import java.awt.event.ActionListener;
064: import java.awt.geom.Line2D;
065: import java.util.ArrayList;
066: import java.util.HashSet;
067: import java.util.Iterator;
068: import java.util.Set;
069: import java.util.logging.Level;
070: import java.util.logging.Logger;
071: import javax.swing.SwingUtilities;
072: import javax.swing.Timer;
073: import org.netbeans.modules.palette.Category;
074: import org.netbeans.modules.palette.Item;
075:
076: /**
077: * Drag and drop support for palette items and categories.
078: *
079: * @author S. Aubrecht
080: */
081: public class DnDSupport implements DragGestureListener,
082: DropTargetListener {
083:
084: final static private int DELAY_TIME_FOR_EXPAND = 1000;
085:
086: private Set<DragGestureRecognizer> recognizers = new HashSet<DragGestureRecognizer>(
087: 5);
088: private Set<DropTarget> dropTargets = new HashSet<DropTarget>(5);
089:
090: private Category draggingCategory;
091: private Item draggingItem;
092: private CategoryList dragSourceCategoryList;
093: private Item targetItem;
094:
095: private boolean dropBefore;
096:
097: private DragSourceListener dragSourceListener;
098:
099: private DropGlassPane dropPane;
100:
101: private PalettePanel palette;
102:
103: private Timer timer;
104:
105: private static final Logger ERR = Logger
106: .getLogger("org.netbeans.modules.palette"); // NOI18N
107:
108: /** Creates a new instance of DnDSupport */
109: public DnDSupport(PalettePanel palette) {
110: this .palette = palette;
111: }
112:
113: void add(CategoryDescriptor descriptor) {
114: CategoryList list = descriptor.getList();
115: list.setTransferHandler(null);
116: list.setDragEnabled(false);
117: recognizers.add(DragSource.getDefaultDragSource()
118: .createDefaultDragGestureRecognizer(list,
119: DnDConstants.ACTION_MOVE, this ));
120: dropTargets.add(new DropTarget(list, this ));
121:
122: CategoryButton button = descriptor.getButton();
123: button.setTransferHandler(null);
124: recognizers.add(DragSource.getDefaultDragSource()
125: .createDefaultDragGestureRecognizer(button,
126: DnDConstants.ACTION_MOVE, this ));
127: dropTargets.add(new DropTarget(button, this ));
128: }
129:
130: void remove(CategoryDescriptor descriptor) {
131: ArrayList<DragGestureRecognizer> recognizersToRemove = new ArrayList<DragGestureRecognizer>(
132: 2);
133: for (Iterator<DragGestureRecognizer> i = recognizers.iterator(); i
134: .hasNext();) {
135: DragGestureRecognizer dgr = i.next();
136: if (dgr.getComponent() == descriptor.getButton()
137: || dgr.getComponent() == descriptor.getList()) {
138: recognizersToRemove.add(dgr);
139: dgr.removeDragGestureListener(this );
140: }
141: }
142: recognizers.removeAll(recognizersToRemove);
143:
144: ArrayList<DropTarget> dropTargetsToRemove = new ArrayList<DropTarget>(
145: 2);
146: for (Iterator<DropTarget> i = dropTargets.iterator(); i
147: .hasNext();) {
148: DropTarget dt = i.next();
149: if (dt.getComponent() == descriptor.getButton()
150: || dt.getComponent() == descriptor.getList()) {
151: dropTargetsToRemove.add(dt);
152: dt.removeDropTargetListener(this );
153: }
154: }
155: dropTargets.removeAll(dropTargetsToRemove);
156: }
157:
158: public void dragGestureRecognized(DragGestureEvent dge) {
159: Transferable t = null;
160:
161: if (dge.getComponent() instanceof CategoryButton) {
162: //trying to drag a palette category
163: CategoryButton button = (CategoryButton) dge.getComponent();
164: draggingCategory = button.getCategory();
165: t = draggingCategory.getTransferable();
166:
167: } else if (dge.getComponent() instanceof CategoryList) {
168: //trying to drag a palette item
169: CategoryList list = (CategoryList) dge.getComponent();
170: int selIndex = list.locationToIndex(dge.getDragOrigin());
171: draggingItem = list.getItemAt(selIndex);
172: if (null == draggingItem) {
173: return;
174: }
175: t = draggingItem.drag();
176: dragSourceCategoryList = list;
177: }
178: if (null != t) {
179: dge.getDragSource().addDragSourceListener(
180: getDragSourceListener());
181: try {
182: dge.startDrag(null, t);
183: } catch (InvalidDnDOperationException idndE) {
184: //attempt to fix #110670
185: try {
186: dge.startDrag(null, t);
187: } catch (InvalidDnDOperationException e) {
188: ERR.log(Level.INFO, idndE.getMessage(), e);
189: }
190: }
191: }
192: }
193:
194: public void drop(DropTargetDropEvent dtde) {
195: Component target = dtde.getDropTargetContext().getComponent();
196: Category targetCategory = null;
197: if (target instanceof CategoryList) {
198: targetCategory = ((CategoryList) target).getCategory();
199: } else if (target instanceof CategoryButton) {
200: targetCategory = ((CategoryButton) target).getCategory();
201: }
202: if (null != draggingCategory) {
203: //dragging a category to reorder
204: boolean res = false;
205: if (null != targetCategory
206: && (target instanceof CategoryButton)) {
207: res = palette.getModel().moveCategory(draggingCategory,
208: targetCategory, dropBefore);
209: }
210: dtde.dropComplete(res);
211: } else {
212: //dragging an item to reorder or move to a different category
213: //or dragging something from outside the palette to create a new item
214: dtde.acceptDrop(dtde.getDropAction());
215: boolean res = false;
216: if (null != targetCategory) {
217: Transferable t;
218: if (null != draggingItem) {
219: //internal drag'n'drop - an item is being moved from a different category
220: t = draggingItem.cut();
221: } else {
222: //a new item is being dropped to the palette from e.g. editor area
223: t = dtde.getTransferable();
224: }
225: res = targetCategory.dropItem(t, dtde.getDropAction(),
226: targetItem, dropBefore);
227: }
228: dtde.dropComplete(res);
229: }
230: cleanupAfterDnD();
231: }
232:
233: public void dragExit(DropTargetEvent dte) {
234: removeDropLine();
235: if (DropGlassPane.isOriginalPaneStored()) {
236: DropGlassPane.putBackOriginal();
237: }
238: removeTimer();
239: }
240:
241: public void dropActionChanged(DropTargetDragEvent dtde) {
242: }
243:
244: public void dragOver(DropTargetDragEvent dtde) {
245: checkStoredGlassPane();
246:
247: doDragOver(dtde);
248: }
249:
250: public void dragEnter(DropTargetDragEvent dtde) {
251: checkStoredGlassPane();
252:
253: Component target = dtde.getDropTargetContext().getComponent();
254: if (target instanceof CategoryButton
255: && null == draggingCategory) {
256: final CategoryButton button = (CategoryButton) target;
257: if (!button.isSelected()
258: && (null == timer || !timer.isRunning())) {
259: removeTimer();
260: timer = new Timer(DELAY_TIME_FOR_EXPAND,
261: new ActionListener() {
262: final public void actionPerformed(
263: ActionEvent e) {
264: button.setExpanded(true);
265: }
266: });
267: timer.setRepeats(false);
268: timer.start();
269: }
270: }
271: doDragOver(dtde);
272: }
273:
274: /** Removes timer and all listeners. */
275: private void removeTimer() {
276: if (timer != null) {
277: ActionListener[] l = (ActionListener[]) timer
278: .getListeners(ActionListener.class);
279:
280: for (int i = 0; i < l.length; i++) {
281: timer.removeActionListener(l[i]);
282: }
283:
284: timer.stop();
285: timer = null;
286: }
287: }
288:
289: private void doDragOver(DropTargetDragEvent dtde) {
290: Component target = dtde.getDropTargetContext().getComponent();
291: if (null != draggingCategory) {
292: //a whole category is being dragged within the palette panel
293: Category targetCategory = null;
294: if (target instanceof CategoryButton) {
295: CategoryButton button = (CategoryButton) target;
296: targetCategory = button.getCategory();
297: }
298: if (null == targetCategory
299: || !palette.getModel().canReorderCategories()) {
300: dtde.rejectDrag();
301: removeDropLine();
302: return;
303: }
304: dropBefore = dtde.getLocation().y < (target.getHeight() / 2);
305: Point p1 = target.getLocation();
306: Point p2 = target.getLocation();
307: p2.x += target.getWidth();
308: if (!dropBefore) {
309: p1.y += target.getHeight();
310: p2.y += target.getHeight();
311: }
312: p1 = SwingUtilities.convertPoint(target, p1, palette
313: .getRootPane());
314: p2 = SwingUtilities.convertPoint(target, p2, palette
315: .getRootPane());
316: Line2D line = new Line2D.Double(p1.x, p1.y, p2.x, p2.y);
317: dropPane.setDropLine(line);
318: } else {
319: //dragging an existing item to reorder
320: //or somebody is trying to drop a new item from outside the palette panel
321: Category targetCategory = null;
322: if (target instanceof CategoryList) {
323: CategoryList list = (CategoryList) target;
324: targetCategory = list.getCategory();
325: } else if (target instanceof CategoryButton) {
326: CategoryButton button = (CategoryButton) target;
327: targetCategory = button.getCategory();
328: }
329: if (null != targetCategory && targetCategory.dragOver(dtde)) {
330: dtde.acceptDrag(dtde.getDropAction());
331: } else {
332: dtde.rejectDrag();
333: removeDropLine();
334: targetItem = null;
335: return;
336: }
337:
338: if (target instanceof CategoryList) {
339: CategoryList list = (CategoryList) target;
340: int dropIndex = list
341: .locationToIndex(dtde.getLocation());
342: if (dropIndex < 0) {
343: dropPane.setDropLine(null);
344: targetItem = null;
345: } else {
346: boolean verticalDropBar = list.getColumnCount() > 1;
347: Rectangle rect = list.getCellBounds(dropIndex,
348: dropIndex);
349: if (verticalDropBar)
350: dropBefore = dtde.getLocation().x < (rect.x + rect.width / 2);
351: else
352: dropBefore = dtde.getLocation().y < (rect.y + rect.height / 2);
353: Point p1 = rect.getLocation();
354: Point p2 = rect.getLocation();
355: if (verticalDropBar) {
356: p2.y += rect.height;
357: if (!dropBefore) {
358: p1.x += rect.width;
359: p2.x += rect.width;
360: }
361: } else {
362: p2.x += rect.width;
363: if (!dropBefore) {
364: p1.y += rect.height;
365: p2.y += rect.height;
366: }
367: }
368: p1 = SwingUtilities.convertPoint(list, p1, palette
369: .getRootPane());
370: p2 = SwingUtilities.convertPoint(list, p2, palette
371: .getRootPane());
372: Line2D line = new Line2D.Double(p1.x, p1.y, p2.x,
373: p2.y);
374: dropPane.setDropLine(line);
375: targetItem = (Item) list.getModel().getElementAt(
376: dropIndex);
377: }
378: } else {
379: targetItem = null;
380: dropBefore = false;
381: }
382: }
383: //parent.repaint();
384: }
385:
386: private DragSourceListener getDragSourceListener() {
387: if (null == dragSourceListener) {
388: dragSourceListener = new DragSourceAdapter() {
389: public void dragDropEnd(DragSourceDropEvent dsde) {
390: dsde.getDragSourceContext().getDragSource()
391: .removeDragSourceListener(this );
392: cleanupAfterDnD();
393: }
394: };
395: }
396: return dragSourceListener;
397: }
398:
399: private void cleanupAfterDnD() {
400: draggingItem = null;
401: draggingCategory = null;
402: targetItem = null;
403: if (null != dragSourceCategoryList) {
404: dragSourceCategoryList.resetRollover();
405: }
406: dragSourceCategoryList = null;
407: removeDropLine();
408: if (DropGlassPane.isOriginalPaneStored()) {
409: DropGlassPane.putBackOriginal();
410: }
411: removeTimer();
412: }
413:
414: private void checkStoredGlassPane() {
415: // remember current glass pane to set back at end of dragging over this compoment
416: if (!DropGlassPane.isOriginalPaneStored()) {
417: Component comp = palette.getRootPane().getGlassPane();
418: DropGlassPane.setOriginalPane(palette, comp, comp
419: .isVisible());
420:
421: // set glass pane for paint selection line
422: dropPane = DropGlassPane.getDefault(palette);
423: palette.getRootPane().setGlassPane(dropPane);
424: dropPane.revalidate();
425: dropPane.validate();
426: dropPane.setVisible(true);
427: }
428: }
429:
430: private void removeDropLine() {
431: if (null != dropPane)
432: dropPane.setDropLine(null);
433: }
434: }
|