KickJava   Java API By Example, From Geeks To Geeks.

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


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 package org.netbeans.core.windows.view.dnd;
21
22
23 import java.awt.TexturePaint JavaDoc;
24 import java.awt.image.BufferedImage JavaDoc;
25 import org.netbeans.core.windows.Constants;
26 import org.netbeans.core.windows.Debug;
27 import org.netbeans.core.windows.view.Controller;
28 import org.netbeans.swing.tabcontrol.plaf.EqualPolygon;
29
30 import javax.swing.*;
31 import java.awt.*;
32 import java.awt.dnd.*;
33 import java.awt.geom.AffineTransform JavaDoc;
34 import java.awt.geom.Area JavaDoc;
35 import java.awt.geom.GeneralPath JavaDoc;
36 import java.util.Set JavaDoc;
37
38
39 /**
40  * Glass pane which is used for <code>DefaultContainerImpl</code>
41  * as a component associated with <code>DropTarget</code> to be able
42  * to paint 'drag under' indications for that container.
43  *
44  *
45  * @author Peter Zavadsky
46  *
47  * @see java.awt.dnd.DropTarget
48  * @see org.netbeans.core.windows.DefaultContainerImpl
49  */

50 public final class DropTargetGlassPane extends JPanel implements DropTargetListener {
51
52     // XXX PENDING
53
private final Observer observer;
54     // XXX PENDING
55
private final Informer informer;
56     
57     private WindowDnDManager windowDragAndDrop;
58     
59     private static boolean isHardwareDoubleBuffer = false;
60     
61     /** Current location of cursor in over the glass pane,
62      * or <code>null</code> in the case there it is not above
63      * this component currently. */

64     private Point location;
65     
66     /** <code>TopComponentDroppable</code> used in paint to get indication
67      * rectangle. */

68     private TopComponentDroppable droppable;
69     
70     /** Debugging flag. */
71     private static final boolean DEBUG = Debug.isLoggable(DropTargetGlassPane.class);
72
73     
74
75     /** Creates non initialized <code>DropTargetGlassPane</code>. */
76     public DropTargetGlassPane(WindowDnDManager wdnd) {
77         this.observer = wdnd;
78         this.informer = wdnd;
79         windowDragAndDrop = wdnd;
80         isHardwareDoubleBuffer = !RepaintManager.currentManager(this).isDoubleBufferingEnabled();
81         
82         setOpaque(false);
83     }
84
85     
86     /** Called when started drag operation, to save the old visibility state. */
87     public void initialize() {
88         if(isVisible()) {
89             // For unselected internal frame the visibility could
90
// be already set, but due to a bug is needed to revalidate it.
91
revalidate();
92         } else {
93             setVisible(true);
94         }
95     }
96
97     /** Called when finished drag operation, to reset the old visibility state. */
98     public void uninitialize() {
99         if(location != null) {
100             // #22123. Not removed drop inidication.
101
dragFinished();
102         }
103
104         setVisible(false);
105     }
106     
107     /** Called when the drag operation performed over this drop target. */
108     void dragOver(Point location, TopComponentDroppable droppable) {
109         this.droppable = droppable;
110         if (dragRepaintManager == null) {
111             setDragRepaintManager (new DragRepaintManager(this));
112         }
113         setDragLocation (location);
114     }
115     
116     
117     private Point dragLocation = null;
118     private void setDragLocation (Point p) {
119         Point old = dragLocation;
120         dragLocation = p;
121         if (p != null && p.equals(old)) {
122             return;
123         } else if (p == null) {
124             //XXX clear?
125
return;
126         }
127         
128         if (droppable != null) {
129             Component c = droppable.getDropComponent();
130             
131             Shape s = droppable.getIndicationForLocation (
132                 SwingUtilities.convertPoint(this, p, c));
133             
134             EnhancedDragPainter painter = null;
135             if (droppable instanceof EnhancedDragPainter) {
136                 painter = (EnhancedDragPainter)droppable;
137             }
138             dragRepaintManager.setShapeAndTarget(s, c, painter);
139         } else {
140             dragRepaintManager.eraseLastIndication(null);
141         }
142         
143     }
144     
145
146     
147     /** Called when the drag operation exited from this drop target. */
148     private void dragExited() {
149         clear();
150     }
151     
152     private boolean guidedPaint = false;
153     
154     /** DragRepaintManager will call paint directly to quickly produce
155      * visual feedback. It will call this method when it is doing this,
156      * so the component knows it will not need to paint the drop indication;
157      * but it should paint it from a normal RepaintManager induced paint. */

158     private void setGuidedPaint (boolean val) {
159         guidedPaint = val;
160     }
161     
162     /** Determine if the current paint cycle is caused by standard repainting
163      * or the custom instant repaint logic in DragRepaintManager */

164     private boolean isGuidedPaint () {
165         return guidedPaint;
166     }
167     
168     /** Hacks the problem when exiting of drop target, sometimes the framework
169      * "forgets" to send drag exit event (when moved from the drop target too
170      * quickly??) thus the indication rectangle remains visible. Used to fix
171      * this problem. */

172     public void clearIndications() {
173         clear();
174     }
175
176     /** Called when changed drag action. */
177     private void dragActionChanged(Point location) {
178         setDragLocation(location);
179     }
180
181     /** Called when drag operation finished. */
182     private void dragFinished() {
183         clear();
184         setDragRepaintManager(null);
185     }
186     
187     private void setDragRepaintManager (DragRepaintManager drm) {
188         this.dragRepaintManager = drm;
189     }
190
191     private DragRepaintManager dragRepaintManager = null;
192
193     /** Clears glass pane. */
194     private void clear() {
195         this.droppable = null;
196         
197         if (dragRepaintManager != null) {
198             dragRepaintManager.setShapeAndTarget(null, null, null);
199         }
200         setDragLocation(null);
201     }
202
203     /** Overrides superclass method, to indicate the 'drag under' gesture
204      * in the case there is cursor drag operation in progress and cursor
205      * is above this drop target. */

206     public void paintComponent(Graphics g) {
207         super.paintComponent(g);
208
209         if (!isGuidedPaint() && dragRepaintManager != null) {
210             dragRepaintManager.paintCurrentIndication ((Graphics2D) g);
211         }
212     }
213     
214     // PENDING Take the color from UI Defaults
215
private static final Color FILL_COLOR = new Color( 200, 200, 200, 120 );
216     
217     
218     // >> DropTargetListener implementation >>
219
/** Implements <code>DropTargetListener</code> method.
220      * accepts/rejects the drag operation if move or copy operation
221      * is specified. */

222     public void dragEnter(DropTargetDragEvent evt) {
223         if(DEBUG) {
224             debugLog(""); // NOI18N
225
debugLog("dragEnter"); // NO18N
226
}
227         
228         int dropAction = evt.getDropAction();
229         // Mask action NONE to MOVE one.
230
if(dropAction == DnDConstants.ACTION_NONE) {
231             dropAction = DnDConstants.ACTION_MOVE;
232         }
233         
234         if((dropAction & DnDConstants.ACTION_COPY_OR_MOVE) > 0) {
235             evt.acceptDrag(dropAction);
236         } else {
237             evt.rejectDrag();
238         }
239     }
240
241     /** Implements <code>DropTargetListener</code> method.
242      * Unsets the glass pane to show 'drag under' gestures. */

243     public void dragExit(DropTargetEvent evt) {
244         if(DEBUG) {
245             debugLog(""); // NOI18N
246
debugLog("dragExit"); // NO18N
247
}
248         
249         Component c = evt.getDropTargetContext().getComponent();
250         if(c == this) {
251             this.dragExited();
252         }
253     }
254     
255     /** Implements <code>DropTargetListener</code> method.
256      * Informs the glass pane about the location of dragged cursor above
257      * the component. */

258     public void dragOver(DropTargetDragEvent evt) {
259         if(DEBUG) {
260             debugLog(""); // NOI18N
261
debugLog("dragOver"); // NOI18N
262
}
263         
264         if (dragRepaintManager == null) {
265             setDragRepaintManager (new DragRepaintManager(this));
266         }
267         
268         // XXX Eliminate bug, see dragExitedHack.
269
observer.setLastDropTarget(this);
270     }
271
272     /** Implements <code>DropTargetListener</code> method.
273      * When changed the drag action accepts/rejects the drag operation
274      * appropriatelly */

275     public void dropActionChanged(DropTargetDragEvent evt) {
276         if(DEBUG) {
277             debugLog(""); // NOI18N
278
debugLog("dropActionChanged"); // NOI18N
279
}
280         
281         int dropAction = evt.getDropAction();
282         boolean acceptDrag;
283         
284         if((dropAction == DnDConstants.ACTION_MOVE)
285         || (dropAction == DnDConstants.ACTION_COPY
286             && informer.isCopyOperationPossible())) {
287                 
288             acceptDrag = true;
289         } else {
290             acceptDrag = false;
291         }
292
293         if(acceptDrag) {
294             evt.acceptDrag(dropAction);
295         } else {
296             evt.rejectDrag();
297         }
298         
299         Component c = evt.getDropTargetContext().getComponent();
300         if(c == this) {
301             this.dragActionChanged(acceptDrag ? evt.getLocation() : null);
302         }
303     }
304
305     /** Implements <code>DropTargetListener</code> method.
306      * Perfoms the actual drop operation. */

307     public void drop(DropTargetDropEvent evt) {
308         if(DEBUG) {
309             debugLog(""); // NOI18N
310
debugLog("drop"); // NOI18N
311
}
312         
313         // Inform glass pane about finished drag operation.
314
Component c = evt.getDropTargetContext().getComponent();
315         if(c == this) {
316             this.dragFinished();
317         }
318
319         int dropAction = evt.getDropAction();
320         if(dropAction != DnDConstants.ACTION_MOVE
321         && dropAction != DnDConstants.ACTION_COPY) {
322             // Not supported dnd operation.
323
evt.rejectDrop();
324             return;
325         }
326         
327         // Accepts drop operation.
328
evt.acceptDrop(dropAction);
329         
330         boolean success = false;
331
332         try {
333             Point location = evt.getLocation();
334
335             // Checks whetger it is in around center panel area.
336
// In that case the drop will be tried later.
337
// PENDING unify it.
338
SwingUtilities.convertPointToScreen(location, c);
339             if(WindowDnDManager.isAroundCenterPanel(location)) {
340                 return;
341             }
342
343             success = windowDragAndDrop.tryPerformDrop(
344                     informer.getController(), informer.getFloatingFrames(),
345                     location, dropAction, evt.getTransferable());
346         } finally {
347             // Complete the drop operation.
348
// XXX #21917.
349
observer.setDropSuccess(success);
350             evt.dropComplete(false);
351             //evt.dropComplete(success);
352
}
353     }
354     // >> DropTargetListener implementation >>
355

356
357
358     private static void debugLog(String JavaDoc message) {
359         Debug.log(DropTargetGlassPane.class, message);
360     }
361     
362     
363     // XXX
364
/** Glass pane uses this interface to inform about changes. */
365     interface Observer {
366         public void setDropSuccess(boolean success);
367         public void setLastDropTarget(DropTargetGlassPane glassPane);
368     } // End of Observer.
369

370     // XXX
371
interface Informer {
372         public boolean isCopyOperationPossible();
373         public Controller getController();
374         public Set JavaDoc<Component> getFloatingFrames();
375     }
376     
377     /** A scratch rectangle to save allocating one for every pixel the mouse
378      * is dragged */

379     private static final Rectangle scratch = new Rectangle();
380     /** Encapsulates the painting logic associated with dragging, and provides
381      * optimized repainting services. Will paint the tab indication onto the
382      * glass pane directly in real time. The trigger method is setShapeAndTarget(),
383      * which will trigger painting the indication on the current target, and
384      * erasing the previous one if necessary. Note it is important that the
385      * Shape objects passed honor the equals() method properly.
386      */

387     private static class DragRepaintManager {
388         private Shape shape = null;
389         private Component lastDropComponent;
390         private EnhancedDragPainter lastEnhanced;
391         DropTargetGlassPane pane;
392         private Graphics2D g = null;
393         
394         public DragRepaintManager (DropTargetGlassPane pane) {
395             this.pane = pane;
396             if (isHardwareDoubleBuffer && !Boolean.getBoolean("nb.winsys.mac.no.double.buffer")) { //NOI18N
397
//Only way to avoid screen corruption on OS-X
398
RepaintManager.currentManager(pane).setDoubleBufferingEnabled(true);
399             }
400         }
401         
402         protected void finalize() {
403             if (g != null) g.dispose();
404             if (isHardwareDoubleBuffer && !Boolean.getBoolean("nb.winsys.mac.no.double.buffer")) { //NOI18N
405
RepaintManager.currentManager(pane).setDoubleBufferingEnabled(false);
406             }
407         }
408         
409         private Graphics2D getGraphics() {
410             if (g == null) {
411                 g = (Graphics2D) pane.getGraphics();
412             }
413             return g;
414         }
415         
416         public void clear() {
417             lastDropComponent = null;
418             lastEnhanced = null;
419         }
420         
421         public void setShapeAndTarget (Shape s, Component c, EnhancedDragPainter enh) {
422             Shape old = shape;
423             if (old != null && s != null) {
424                 if (!shape.equals(s)) {
425                     shape = s;
426                     shapeChange (old, shape, c, enh);
427                 }
428             } else if ((old == null) != (s == null)) {
429                 shape = s;
430                 shapeChange (old, s, c, enh);
431             }
432         }
433         
434         public void paintCurrentIndication (Graphics2D g) {
435             if (shape != null) {
436                 paintShapeOnGlassPane (shape, g);
437             }
438         }
439         
440         public void eraseLastIndication (Graphics2D g) {
441             if (shape == null) {
442                 return;
443             }
444             eraseShape(g);
445         }
446         
447         private void shapeChange (Shape old, Shape nue, Component c, EnhancedDragPainter enhanced) {
448             if (old != null) {
449                 eraseShape(g);
450             }
451             lastDropComponent = c;
452             lastEnhanced = enhanced;
453             if (nue != null) {
454                 paintShapeOnGlassPane (nue, g);
455             }
456         }
457         
458         private void eraseShape(Graphics2D g) {
459             if (g == null) {
460                 g = getGraphics();
461             }
462             if (g == null) {
463                 return;
464             }
465             pane.setGuidedPaint(true);
466             try {
467                 JComponent toPaint;
468                 toPaint = (JComponent) ((JComponent)lastDropComponent).getRootPane();
469                 toPaint.paint (g);
470                 //Clear the clip - at least on OS-X not doing so can interfere
471
//with pending paint events, even though it shouldn't
472
g.setClip (null);
473             } finally {
474                 pane.setGuidedPaint(false);
475             }
476             if (isHardwareDoubleBuffer) {
477                 Toolkit.getDefaultToolkit().sync();
478             }
479         }
480         
481         private void paintShapeOnGlassPane (Shape s, Graphics2D g) {
482             if (g == null) {
483                 g = getGraphics();
484             }
485             if (g == null) {
486                 return;
487             }
488             pane.setGuidedPaint(true);
489             try {
490                 JComponent toPaint;
491                 //If we are erasing, we want to paint the root pane so any
492
//pixels outside the bounds of the component are repainted
493
toPaint = (JComponent) lastDropComponent;
494                 // only set the clip when really want to paint it, not when erasing..
495
if (lastEnhanced != null) {
496                     lastEnhanced.additionalDragPaint(g);
497                 }
498                 Shape clip = getClipForIndication (s, true, lastDropComponent);
499                 g.setClip (clip);
500                 paintShape (s, g);
501
502                 //Clear the clip - at least on OS-X not doing so can interfere
503
//with pending paint events, even though it shouldn't
504
g.setClip (null);
505             } finally {
506                 pane.setGuidedPaint(false);
507             }
508             if (isHardwareDoubleBuffer) {
509                 Toolkit.getDefaultToolkit().sync();
510             }
511         }
512         
513         private void paintShape (Shape s, Graphics2D g) {
514             Color oldColor = g.getColor();
515             Stroke oldStroke = g.getStroke();
516             Paint JavaDoc oldPaint = g.getPaint();
517             g.setColor(Color.red);
518     
519             g.setStroke(createIndicationStroke());
520             g.setPaint(createPaint());
521             Color fillColor = Constants.SWITCH_DROP_INDICATION_FADE ? FILL_COLOR : null;
522             if(s instanceof Rectangle) {
523                 drawIndicationRectangle(g, (Rectangle)s, lastDropComponent, fillColor);
524             } else if(s instanceof GeneralPath JavaDoc) {
525                 drawIndicationGeneralPath(g, (GeneralPath JavaDoc)s, lastDropComponent, fillColor);
526             } else if (s instanceof Polygon) {
527                 drawIndicationPolygon (g, (Polygon) s, lastDropComponent, fillColor);
528             }
529             g.setColor(oldColor);
530             g.setStroke(oldStroke);
531             g.setPaint(oldPaint);
532         }
533         
534         /** Creates indication pen stroke. Utility method. */
535         private Stroke createIndicationStroke() {
536             return new BasicStroke(3);
537         }
538         
539         
540         
541         
542         private Shape getClipForIndication (Shape indication, boolean translate, Component target) {
543             Shape clip;
544             if (indication instanceof Rectangle) {
545                 scratch.setBounds((Rectangle) indication);
546                 scratch.x -= 3;
547                 scratch.y -= 3;
548                 scratch.width +=6;
549                 scratch.height +=6;
550                 Area JavaDoc a = new Area JavaDoc(scratch);
551                 scratch.setBounds ((Rectangle) indication);
552                 scratch.x += 4;
553                 scratch.y += 4;
554                 scratch.width -=8;
555                 scratch.height -= 8;
556                 a.subtract(new Area JavaDoc(scratch));
557                 if (translate) {
558                     Point p = new Point (0,0);
559                     
560                     p = SwingUtilities.convertPoint(lastDropComponent, p,
561                         pane);
562                     AffineTransform JavaDoc at = AffineTransform.getTranslateInstance(p.x, p.y);
563                     a.transform(at);
564                 }
565                 clip = a;
566             } else {
567                 if (indication instanceof Polygon) {
568                     if (translate) {
569                         indication = getTransformedPath ((Polygon) indication, target);
570                     }
571                 } else if (indication instanceof GeneralPath JavaDoc) {
572                     if (translate) {
573                         indication = getTransformedPath ((GeneralPath JavaDoc)indication, target);
574                     }
575                 } else {
576                     //who knows what it is...
577
return null;
578                 }
579                 clip = new BasicStroke (5).createStrokedShape(indication);
580             }
581             return clip;
582         }
583         
584         private Polygon getTransformedPath (Polygon path, Component source) {
585             Polygon result = new EqualPolygon (path.xpoints, path.ypoints, path.npoints);
586             //XXX shrink the polgon here
587
Point point = new Point(0, 0);
588             point = SwingUtilities.convertPoint(source, point, pane);
589             result.translate (point.x, point.y);
590             return result;
591         }
592
593         /** We do some munging of GeneralPaths before painting them. That logic
594          * is encapsulated here so it can also be used by the code that generates
595          * a clip shape */

596         private GeneralPath JavaDoc getTransformedPath (GeneralPath JavaDoc path, Component source) {
597             Point point = new Point(0, 0);
598             point = SwingUtilities.convertPoint(source, point, pane);
599             path = (GeneralPath JavaDoc) ((GeneralPath JavaDoc) path).clone();
600             path.transform(new AffineTransform JavaDoc(1D, 0D, 0D, 1D, point.x, point.y));
601             return path;
602         }
603
604         private void drawIndicationPolygon(Graphics2D g, Polygon p, Component source, Color fillColor ) {
605             if (g == null) {
606                 g = getGraphics();
607             }
608             Point point = new Point (0,0);
609             point = SwingUtilities.convertPoint(source, point, pane);
610             p = new EqualPolygon (p.xpoints, p.ypoints, p.npoints);
611             p.translate (point.x, point.y);
612             g.drawPolygon (p);
613             if ( fillColor != null ) {
614                 g.setColor( fillColor );
615                 g.fillPolygon (p);
616             }
617         }
618         
619         private void drawIndicationRectangle(Graphics2D g, Rectangle r, Component source, Color fillColor ) {
620             if (g == null) {
621                 g = getGraphics();
622             }
623             r = SwingUtilities.convertRectangle(source, r, pane);
624             // XXX Shrinks the rectangle to take into account the width of pen stroke.
625

626             g.drawRect(r.x+1, r.y+1, r.width-2, r.height-2);
627             if ( fillColor != null ) {
628                 g.setColor(fillColor);
629                 g.fillRect(r.x+1, r.y+1, r.width-2, r.height-2);
630             }
631         }
632
633         private void drawIndicationGeneralPath(Graphics2D g, GeneralPath JavaDoc path, Component source, Color fillColor ) {
634             if (g == null) {
635                 g = getGraphics();
636             }
637             // Convert path to this coordinates.
638
path = getTransformedPath (path, source);
639
640             g.draw(path);
641             if ( fillColor != null ) {
642                 g.setColor( fillColor );
643                 g.fill(path);
644             }
645         }
646         
647         private TexturePaint JavaDoc createPaint() {
648             BufferedImage JavaDoc image = new BufferedImage JavaDoc(2,2,BufferedImage.TYPE_INT_ARGB);
649             Graphics2D g2 = image.createGraphics();
650             g2.setColor(new Color(255, 90, 0));
651             g2.fillRect(0,0,1,1);
652             g2.fillRect(1,1,1,1);
653             g2.setColor(new Color(255, 90, 0, 0));
654             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
655             g2.fillRect(1,0,1,1);
656             g2.fillRect(0,1,1,1);
657             return new TexturePaint JavaDoc(image, new Rectangle(0,0,2,2));
658         }
659     }
660
661 }
662
Popular Tags