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.core.windows.view.dnd;
043:
044: import java.awt.TexturePaint;
045: import java.awt.image.BufferedImage;
046: import org.netbeans.core.windows.Constants;
047: import org.netbeans.core.windows.Debug;
048: import org.netbeans.core.windows.view.Controller;
049:
050: import javax.swing.*;
051: import java.awt.*;
052: import java.awt.dnd.*;
053: import java.awt.geom.AffineTransform;
054: import java.util.Set;
055:
056: /**
057: * Glass pane which is used for <code>DefaultContainerImpl</code>
058: * as a component associated with <code>DropTarget</code> to be able
059: * to paint 'drag under' indications for that container.
060: *
061: *
062: * @author Peter Zavadsky
063: *
064: * @see java.awt.dnd.DropTarget
065: * @see org.netbeans.core.windows.DefaultContainerImpl
066: */
067: public final class DropTargetGlassPane extends JPanel implements
068: DropTargetListener {
069:
070: // XXX PENDING
071: private final Observer observer;
072: // XXX PENDING
073: private final Informer informer;
074:
075: private WindowDnDManager windowDragAndDrop;
076:
077: /** Current location of cursor in over the glass pane,
078: * or <code>null</code> in the case there it is not above
079: * this component currently. */
080: private Point location;
081:
082: /** <code>TopComponentDroppable</code> used in paint to get indication
083: * rectangle. */
084: private TopComponentDroppable droppable;
085:
086: /** Debugging flag. */
087: private static final boolean DEBUG = Debug
088: .isLoggable(DropTargetGlassPane.class);
089:
090: /** Creates non initialized <code>DropTargetGlassPane</code>. */
091: public DropTargetGlassPane(WindowDnDManager wdnd) {
092: this .observer = wdnd;
093: this .informer = wdnd;
094: windowDragAndDrop = wdnd;
095:
096: setOpaque(false);
097: }
098:
099: /** Called when started drag operation, to save the old visibility state. */
100: public void initialize() {
101: if (isVisible()) {
102: // For unselected internal frame the visibility could
103: // be already set, but due to a bug is needed to revalidate it.
104: revalidate();
105: } else {
106: setVisible(true);
107: }
108: }
109:
110: /** Called when finished drag operation, to reset the old visibility state. */
111: public void uninitialize() {
112: if (location != null) {
113: // #22123. Not removed drop inidication.
114: dragFinished();
115: }
116:
117: setVisible(false);
118: }
119:
120: /** Called when the drag operation performed over this drop target. */
121: void dragOver(Point location, TopComponentDroppable droppable) {
122: this .droppable = droppable;
123: setDragLocation(location);
124: }
125:
126: private Point dragLocation = null;
127:
128: private void setDragLocation(Point p) {
129: Point old = dragLocation;
130: dragLocation = p;
131: if (p != null && p.equals(old)) {
132: return;
133: } else if (p == null) {
134: //XXX clear?
135: return;
136: }
137:
138: if (droppable != null) {
139: Rectangle repaintRectangle = null;
140: if (null != currentDropIndication) {
141: repaintRectangle = currentDropIndication.getBounds();
142: repaintRectangle = SwingUtilities.convertRectangle(
143: componentUnderCursor, repaintRectangle, this );
144:
145: if (null != currentPainter) {
146: Rectangle rect = currentPainter.getPaintArea();
147: if (null != rect)
148: repaintRectangle.add(rect);
149: }
150: }
151: Component c = droppable.getDropComponent();
152:
153: Shape s = droppable.getIndicationForLocation(SwingUtilities
154: .convertPoint(this , p, c));
155: if (null != s && s.equals(currentDropIndication)) {
156: return;
157: }
158:
159: if (droppable instanceof EnhancedDragPainter) {
160: currentPainter = (EnhancedDragPainter) droppable;
161: } else {
162: currentPainter = null;
163: }
164: currentDropIndication = s;
165: componentUnderCursor = c;
166: if (null != currentDropIndication) {
167: Rectangle rect = currentDropIndication.getBounds();
168: rect = SwingUtilities.convertRectangle(c, rect, this );
169: if (null != repaintRectangle)
170: repaintRectangle.add(rect);
171: else
172: repaintRectangle = rect;
173:
174: if (null != currentPainter) {
175: rect = currentPainter.getPaintArea();
176: if (null != rect)
177: repaintRectangle.add(rect);
178: }
179: }
180: if (null != repaintRectangle) {
181: repaintRectangle.grow(2, 2);
182: repaint(repaintRectangle);
183: }
184: } else {
185: if (null != currentDropIndication) {
186: Rectangle repaintRect = currentDropIndication
187: .getBounds();
188: currentDropIndication = null;
189: if (null != currentPainter) {
190: Rectangle rect = currentPainter.getPaintArea();
191: if (null != rect)
192: repaintRect = repaintRect.union(rect);
193: currentPainter = null;
194: }
195: repaint(repaintRect);
196: }
197: }
198:
199: }
200:
201: /** Called when the drag operation exited from this drop target. */
202: private void dragExited() {
203: clear();
204: }
205:
206: /** Hacks the problem when exiting of drop target, sometimes the framework
207: * "forgets" to send drag exit event (when moved from the drop target too
208: * quickly??) thus the indication rectangle remains visible. Used to fix
209: * this problem. */
210: public void clearIndications() {
211: currentDropIndication = null;
212: currentPainter = null;
213: componentUnderCursor = null;
214: repaint();
215: clear();
216: }
217:
218: /** Called when changed drag action. */
219: private void dragActionChanged(Point location) {
220: setDragLocation(location);
221: }
222:
223: /** Called when drag operation finished. */
224: private void dragFinished() {
225: clear();
226: }
227:
228: /** Clears glass pane. */
229: private void clear() {
230: this .droppable = null;
231:
232: setDragLocation(null);
233: }
234:
235: private Shape currentDropIndication;
236: private EnhancedDragPainter currentPainter;
237: private Component componentUnderCursor;
238:
239: @Override
240: public void paint(Graphics g) {
241: if (null != currentDropIndication) {
242: Graphics2D g2d = (Graphics2D) g.create();
243:
244: if (null != currentPainter)
245: currentPainter.additionalDragPaint(g2d);
246:
247: Color c = UIManager.getColor("Panel.dropTargetGlassPane");
248: if (c == null) {
249: c = Color.red;
250: }
251: g2d.setColor(c);
252:
253: Point p = new Point(0, 0);
254:
255: p = SwingUtilities.convertPoint(componentUnderCursor, p,
256: this );
257: AffineTransform at = AffineTransform.getTranslateInstance(
258: p.x, p.y);
259: g2d.transform(at);
260:
261: g2d.setStroke(getIndicationStroke());
262: g2d.setPaint(getIndicationPaint());
263: Color fillColor = Constants.SWITCH_DROP_INDICATION_FADE ? FILL_COLOR
264: : null;
265: g2d.draw(currentDropIndication);
266: if (null != fillColor)
267: g2d.fill(currentDropIndication);
268: g2d.dispose();
269: }
270: }
271:
272: private TexturePaint texturePaint;
273:
274: private TexturePaint getIndicationPaint() {
275: if (null == texturePaint) {
276: BufferedImage image = new BufferedImage(2, 2,
277: BufferedImage.TYPE_INT_ARGB);
278: Graphics2D g2 = image.createGraphics();
279: Color c = UIManager.getColor("Panel.dropTargetGlassPane");
280: if (c == null) {
281: c = new Color(255, 90, 0);
282: }
283: g2.setColor(c);
284: g2.fillRect(0, 0, 1, 1);
285: g2.fillRect(1, 1, 1, 1);
286: g2.setColor(new Color(c.getRed(), c.getGreen(),
287: c.getBlue(), 0));
288: g2.setComposite(AlphaComposite.getInstance(
289: AlphaComposite.SRC_OVER, 0.75f));
290: g2.fillRect(1, 0, 1, 1);
291: g2.fillRect(0, 1, 1, 1);
292: texturePaint = new TexturePaint(image, new Rectangle(0, 0,
293: 2, 2));
294: }
295: return texturePaint;
296: }
297:
298: private Stroke stroke;
299:
300: private Stroke getIndicationStroke() {
301: if (null == stroke)
302: stroke = new BasicStroke(3);
303: return stroke;
304: }
305:
306: // PENDING Take the color from UI Defaults
307: private static final Color FILL_COLOR = new Color(200, 200, 200,
308: 120);
309:
310: // >> DropTargetListener implementation >>
311: /** Implements <code>DropTargetListener</code> method.
312: * accepts/rejects the drag operation if move or copy operation
313: * is specified. */
314: public void dragEnter(DropTargetDragEvent evt) {
315: if (DEBUG) {
316: debugLog(""); // NOI18N
317: debugLog("dragEnter"); // NO18N
318: }
319:
320: int dropAction = evt.getDropAction();
321: // Mask action NONE to MOVE one.
322: if (dropAction == DnDConstants.ACTION_NONE) {
323: dropAction = DnDConstants.ACTION_MOVE;
324: }
325:
326: if ((dropAction & DnDConstants.ACTION_COPY_OR_MOVE) > 0) {
327: evt.acceptDrag(dropAction);
328: } else {
329: evt.rejectDrag();
330: }
331: }
332:
333: /** Implements <code>DropTargetListener</code> method.
334: * Unsets the glass pane to show 'drag under' gestures. */
335: public void dragExit(DropTargetEvent evt) {
336: if (DEBUG) {
337: debugLog(""); // NOI18N
338: debugLog("dragExit"); // NO18N
339: }
340:
341: Component c = evt.getDropTargetContext().getComponent();
342: if (c == this ) {
343: this .dragExited();
344: }
345: }
346:
347: /** Implements <code>DropTargetListener</code> method.
348: * Informs the glass pane about the location of dragged cursor above
349: * the component. */
350: public void dragOver(DropTargetDragEvent evt) {
351: if (DEBUG) {
352: debugLog(""); // NOI18N
353: debugLog("dragOver"); // NOI18N
354: }
355:
356: // XXX Eliminate bug, see dragExitedHack.
357: observer.setLastDropTarget(this );
358: }
359:
360: /** Implements <code>DropTargetListener</code> method.
361: * When changed the drag action accepts/rejects the drag operation
362: * appropriatelly */
363: public void dropActionChanged(DropTargetDragEvent evt) {
364: if (DEBUG) {
365: debugLog(""); // NOI18N
366: debugLog("dropActionChanged"); // NOI18N
367: }
368:
369: int dropAction = evt.getDropAction();
370: boolean acceptDrag;
371:
372: if ((dropAction == DnDConstants.ACTION_MOVE)
373: || (dropAction == DnDConstants.ACTION_COPY && informer
374: .isCopyOperationPossible())) {
375:
376: acceptDrag = true;
377: } else {
378: acceptDrag = false;
379: }
380:
381: if (acceptDrag) {
382: evt.acceptDrag(dropAction);
383: } else {
384: evt.rejectDrag();
385: }
386:
387: Component c = evt.getDropTargetContext().getComponent();
388: if (c == this ) {
389: this .dragActionChanged(acceptDrag ? evt.getLocation()
390: : null);
391: }
392: }
393:
394: /** Implements <code>DropTargetListener</code> method.
395: * Perfoms the actual drop operation. */
396: public void drop(DropTargetDropEvent evt) {
397: if (DEBUG) {
398: debugLog(""); // NOI18N
399: debugLog("drop"); // NOI18N
400: }
401:
402: // Inform glass pane about finished drag operation.
403: Component c = evt.getDropTargetContext().getComponent();
404: if (c == this ) {
405: this .dragFinished();
406: }
407:
408: int dropAction = evt.getDropAction();
409: if (dropAction != DnDConstants.ACTION_MOVE
410: && dropAction != DnDConstants.ACTION_COPY) {
411: // Not supported dnd operation.
412: evt.rejectDrop();
413: return;
414: }
415:
416: // Accepts drop operation.
417: evt.acceptDrop(dropAction);
418:
419: boolean success = false;
420:
421: try {
422: Point loc = evt.getLocation();
423: // Checks whetger it is in around center panel area.
424: // In that case the drop will be tried later.
425: // PENDING unify it.
426: SwingUtilities.convertPointToScreen(loc, c);
427: if (WindowDnDManager.isAroundCenterPanel(loc)) {
428: return;
429: }
430:
431: success = windowDragAndDrop.tryPerformDrop(informer
432: .getController(), informer.getFloatingFrames(),
433: loc, dropAction, evt.getTransferable());
434: } finally {
435: // Complete the drop operation.
436: // XXX #21917.
437: observer.setDropSuccess(success);
438: evt.dropComplete(false);
439: //evt.dropComplete(success);
440: }
441: }
442:
443: // >> DropTargetListener implementation >>
444:
445: private static void debugLog(String message) {
446: Debug.log(DropTargetGlassPane.class, message);
447: }
448:
449: // XXX
450: /** Glass pane uses this interface to inform about changes. */
451: interface Observer {
452: public void setDropSuccess(boolean success);
453:
454: public void setLastDropTarget(DropTargetGlassPane glassPane);
455: } // End of Observer.
456:
457: // XXX
458: interface Informer {
459: public boolean isCopyOperationPossible();
460:
461: public Controller getController();
462:
463: public Set<Component> getFloatingFrames();
464: }
465:
466: }
|