1 /*
2  * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package javax.swing;
27 
28 import java.awt.Color;
29 import java.awt.Component;
30 import java.awt.Container;
31 import java.awt.Dimension;
32 import java.awt.Graphics;
33 import java.awt.Point;
34 import java.awt.Rectangle;
35 import java.awt.Toolkit;
36 import java.awt.Window;
37 import java.beans.PropertyVetoException;
38 
39 import sun.awt.AWTAccessor;
40 import sun.awt.SunToolkit;
41 
42 /** This is an implementation of the <code>DesktopManager</code>.
43   * It currently implements the basic behaviors for managing
44   * <code>JInternalFrame</code>s in an arbitrary parent.
45   * <code>JInternalFrame</code>s that are not children of a
46   * <code>JDesktop</code> will use this component
47   * to handle their desktop-like actions.
48   * <p>This class provides a policy for the various JInternalFrame methods,
49   * it is not meant to be called directly rather the various JInternalFrame
50   * methods will call into the DesktopManager.</p>
51   * @see JDesktopPane
52   * @see JInternalFrame
53   * @author David Kloba
54   * @author Steve Wilson
55   * @since 1.2
56   */
57 @SuppressWarnings("serial") // No Interesting Non-Transient State
58 public class DefaultDesktopManager implements DesktopManager, java.io.Serializable {
59     static final String HAS_BEEN_ICONIFIED_PROPERTY = "wasIconOnce";
60 
61     static final int DEFAULT_DRAG_MODE = 0;
62     static final int OUTLINE_DRAG_MODE = 1;
63     static final int FASTER_DRAG_MODE = 2;
64 
65     int dragMode = DEFAULT_DRAG_MODE;
66 
67     private transient Rectangle currentBounds = null;
68     private transient Graphics desktopGraphics = null;
69     private transient Rectangle desktopBounds = null;
70     private transient Rectangle[] floatingItems = {};
71 
72     /**
73      * Set to true when the user actually drags a frame vs clicks on it
74      * to start the drag operation.  This is only used when dragging with
75      * FASTER_DRAG_MODE.
76      */
77     private transient boolean didDrag;
78 
79     /** Normally this method will not be called. If it is, it
80       * tries to determine the appropriate parent from the desktopIcon of the frame.
81       * Will remove the desktopIcon from its parent if it successfully adds the frame.
82       */
openFrame(JInternalFrame f)83     public void openFrame(JInternalFrame f) {
84         if(f.getDesktopIcon().getParent() != null) {
85             f.getDesktopIcon().getParent().add(f);
86             removeIconFor(f);
87         }
88     }
89 
90     /**
91      * Removes the frame, and, if necessary, the
92      * <code>desktopIcon</code>, from its parent.
93      * @param f the <code>JInternalFrame</code> to be removed
94      */
closeFrame(JInternalFrame f)95     public void closeFrame(JInternalFrame f) {
96         JDesktopPane d = f.getDesktopPane();
97         if (d == null) {
98             return;
99         }
100         boolean findNext = f.isSelected();
101         Container c = f.getParent();
102         JInternalFrame nextFrame = null;
103         if (findNext) {
104             nextFrame = d.getNextFrame(f);
105             try { f.setSelected(false); } catch (PropertyVetoException e2) { }
106         }
107         if(c != null) {
108             c.remove(f); // Removes the focus.
109             c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
110         }
111         removeIconFor(f);
112         if(f.getNormalBounds() != null)
113             f.setNormalBounds(null);
114         if(wasIcon(f))
115             setWasIcon(f, null);
116         if (nextFrame != null) {
117             try { nextFrame.setSelected(true); }
118             catch (PropertyVetoException e2) { }
119         } else if (findNext && d.getComponentCount() == 0) {
120             // It was selected and was the last component on the desktop.
121             d.requestFocus();
122         }
123     }
124 
125     /**
126      * Resizes the frame to fill its parents bounds.
127      * @param f the frame to be resized
128      */
maximizeFrame(JInternalFrame f)129     public void maximizeFrame(JInternalFrame f) {
130         if (f.isIcon()) {
131             try {
132                 // In turn calls deiconifyFrame in the desktop manager.
133                 // That method will handle the maximization of the frame.
134                 f.setIcon(false);
135             } catch (PropertyVetoException e2) {
136             }
137         } else {
138             Container c = f.getParent();
139             if (c == null) {
140                 return;
141             }
142             f.setNormalBounds(f.getBounds());
143             Rectangle desktopBounds = c.getBounds();
144             setBoundsForFrame(f, 0, 0,
145                 desktopBounds.width, desktopBounds.height);
146         }
147 
148         // Set the maximized frame as selected.
149         try {
150             f.setSelected(true);
151         } catch (PropertyVetoException e2) {
152         }
153     }
154 
155     /**
156      * Restores the frame back to its size and position prior
157      * to a <code>maximizeFrame</code> call.
158      * @param f the <code>JInternalFrame</code> to be restored
159      */
minimizeFrame(JInternalFrame f)160     public void minimizeFrame(JInternalFrame f) {
161         // If the frame was an icon restore it back to an icon.
162         if (f.isIcon()) {
163             iconifyFrame(f);
164             return;
165         }
166 
167         if ((f.getNormalBounds()) != null) {
168             Rectangle r = f.getNormalBounds();
169             f.setNormalBounds(null);
170             try { f.setSelected(true); } catch (PropertyVetoException e2) { }
171             setBoundsForFrame(f, r.x, r.y, r.width, r.height);
172         }
173     }
174 
175     /**
176      * Removes the frame from its parent and adds its
177      * <code>desktopIcon</code> to the parent.
178      * @param f the <code>JInternalFrame</code> to be iconified
179      */
iconifyFrame(JInternalFrame f)180     public void iconifyFrame(JInternalFrame f) {
181         JInternalFrame.JDesktopIcon desktopIcon;
182         Container c = f.getParent();
183         JDesktopPane d = f.getDesktopPane();
184         boolean findNext = f.isSelected();
185         desktopIcon = f.getDesktopIcon();
186         if(!wasIcon(f)) {
187             Rectangle r = getBoundsForIconOf(f);
188             desktopIcon.setBounds(r.x, r.y, r.width, r.height);
189             // we must validate the hierarchy to not break the hw/lw mixing
190             desktopIcon.revalidate();
191             setWasIcon(f, Boolean.TRUE);
192         }
193 
194         if (c == null || d == null) {
195             return;
196         }
197 
198         if (c instanceof JLayeredPane) {
199             JLayeredPane lp = (JLayeredPane)c;
200             int layer = JLayeredPane.getLayer(f);
201             JLayeredPane.putLayer(desktopIcon, layer);
202         }
203         d.setComponentOrderCheckingEnabled(true);
204         c.remove(f);
205         c.add(desktopIcon);
206         if (findNext) {
207             if (d.selectFrame(true) == null) {
208                 // The icon is the last frame.
209                 f.restoreSubcomponentFocus();
210             }
211         }
212         c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
213     }
214 
215     /**
216      * Removes the desktopIcon from its parent and adds its frame
217      * to the parent.
218      * @param f the <code>JInternalFrame</code> to be de-iconified
219      */
deiconifyFrame(JInternalFrame f)220     public void deiconifyFrame(JInternalFrame f) {
221         JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
222         Container c = desktopIcon.getParent();
223         JDesktopPane d = f.getDesktopPane();
224         if (c != null && d != null) {
225             c.add(f);
226             // If the frame is to be restored to a maximized state make
227             // sure it still fills the whole desktop.
228             if (f.isMaximum()) {
229                 Rectangle desktopBounds = c.getBounds();
230                 if (f.getWidth() != desktopBounds.width ||
231                         f.getHeight() != desktopBounds.height) {
232                     setBoundsForFrame(f, 0, 0,
233                         desktopBounds.width, desktopBounds.height);
234                 }
235             }
236             removeIconFor(f);
237             if (f.isSelected()) {
238                 f.moveToFront();
239                 f.restoreSubcomponentFocus();
240             }
241             else {
242                 try {
243                     f.setSelected(true);
244                 } catch (PropertyVetoException e2) {}
245 
246             }
247         }
248     }
249 
250     /** This will activate <b>f</b> moving it to the front. It will
251       * set the current active frame's (if any)
252       * <code>IS_SELECTED_PROPERTY</code> to <code>false</code>.
253       * There can be only one active frame across all Layers.
254       * @param f the <code>JInternalFrame</code> to be activated
255       */
activateFrame(JInternalFrame f)256     public void activateFrame(JInternalFrame f) {
257         Container p = f.getParent();
258         Component[] c;
259         JDesktopPane d = f.getDesktopPane();
260         JInternalFrame currentlyActiveFrame =
261           (d == null) ? null : d.getSelectedFrame();
262         // fix for bug: 4162443
263         if(p == null) {
264             // If the frame is not in parent, its icon maybe, check it
265             p = f.getDesktopIcon().getParent();
266             if(p == null)
267                 return;
268         }
269         // we only need to keep track of the currentActive InternalFrame, if any
270         if (currentlyActiveFrame == null){
271           if (d != null) { d.setSelectedFrame(f);}
272         } else if (currentlyActiveFrame != f) {
273           // if not the same frame as the current active
274           // we deactivate the current
275           if (currentlyActiveFrame.isSelected()) {
276             try {
277               currentlyActiveFrame.setSelected(false);
278             }
279             catch(PropertyVetoException e2) {}
280           }
281           if (d != null) { d.setSelectedFrame(f);}
282         }
283         f.moveToFront();
284     }
285 
286     // implements javax.swing.DesktopManager
deactivateFrame(JInternalFrame f)287     public void deactivateFrame(JInternalFrame f) {
288       JDesktopPane d = f.getDesktopPane();
289       JInternalFrame currentlyActiveFrame =
290           (d == null) ? null : d.getSelectedFrame();
291       if (currentlyActiveFrame == f)
292         d.setSelectedFrame(null);
293     }
294 
295     // implements javax.swing.DesktopManager
beginDraggingFrame(JComponent f)296     public void beginDraggingFrame(JComponent f) {
297         setupDragMode(f);
298 
299         if (dragMode == FASTER_DRAG_MODE) {
300           Component desktop = f.getParent();
301           floatingItems = findFloatingItems(f);
302           currentBounds = f.getBounds();
303           if (desktop instanceof JComponent) {
304               desktopBounds = ((JComponent)desktop).getVisibleRect();
305           }
306           else {
307               desktopBounds = desktop.getBounds();
308               desktopBounds.x = desktopBounds.y = 0;
309           }
310           desktopGraphics = JComponent.safelyGetGraphics(desktop);
311           ((JInternalFrame)f).isDragging = true;
312           didDrag = false;
313         }
314 
315     }
316 
setupDragMode(JComponent f)317     private void setupDragMode(JComponent f) {
318         JDesktopPane p = getDesktopPane(f);
319         Container parent = f.getParent();
320         dragMode = DEFAULT_DRAG_MODE;
321         if (p != null) {
322             String mode = (String)p.getClientProperty("JDesktopPane.dragMode");
323             Window window = SwingUtilities.getWindowAncestor(f);
324             if (window != null && !window.isOpaque()) {
325                 dragMode = DEFAULT_DRAG_MODE;
326             } else if (mode != null && mode.equals("outline")) {
327                 dragMode = OUTLINE_DRAG_MODE;
328             } else if (mode != null && mode.equals("faster")
329                     && f instanceof JInternalFrame
330                     && ((JInternalFrame)f).isOpaque() &&
331                        (parent == null || parent.isOpaque())) {
332                 dragMode = FASTER_DRAG_MODE;
333             } else {
334                 if (p.getDragMode() == JDesktopPane.OUTLINE_DRAG_MODE ) {
335                     dragMode = OUTLINE_DRAG_MODE;
336                 } else if ( p.getDragMode() == JDesktopPane.LIVE_DRAG_MODE
337                         && f instanceof JInternalFrame
338                         && ((JInternalFrame)f).isOpaque()) {
339                     dragMode = FASTER_DRAG_MODE;
340                 } else {
341                     dragMode = DEFAULT_DRAG_MODE;
342                 }
343             }
344         }
345     }
346 
347     private transient Point currentLoc = null;
348 
349     /**
350       * Moves the visible location of the frame being dragged
351       * to the location specified.  The means by which this occurs can vary depending
352       * on the dragging algorithm being used.  The actual logical location of the frame
353       * might not change until <code>endDraggingFrame</code> is called.
354       */
dragFrame(JComponent f, int newX, int newY)355     public void dragFrame(JComponent f, int newX, int newY) {
356 
357         if (dragMode == OUTLINE_DRAG_MODE) {
358             JDesktopPane desktopPane = getDesktopPane(f);
359             if (desktopPane != null){
360               Graphics g = JComponent.safelyGetGraphics(desktopPane);
361 
362               g.setXORMode(Color.white);
363               if (currentLoc != null) {
364                 g.drawRect(currentLoc.x, currentLoc.y,
365                         f.getWidth()-1, f.getHeight()-1);
366               }
367               g.drawRect( newX, newY, f.getWidth()-1, f.getHeight()-1);
368               /* Work around for 6635462: XOR mode may cause a SurfaceLost on first use.
369               * Swing doesn't expect that its XOR drawRect did
370               * not complete, so believes that on re-entering at
371               * the next update location, that there is an XOR rect
372               * to draw out at "currentLoc". But in fact
373               * its now got a new clean surface without that rect,
374               * so drawing it "out" in fact draws it on, leaving garbage.
375               * So only update/set currentLoc if the draw completed.
376               */
377               sun.java2d.SurfaceData sData =
378                   ((sun.java2d.SunGraphics2D)g).getSurfaceData();
379 
380               if (!sData.isSurfaceLost()) {
381                   currentLoc = new Point (newX, newY);
382               }
383 ;
384               g.dispose();
385             }
386         } else if (dragMode == FASTER_DRAG_MODE) {
387             dragFrameFaster(f, newX, newY);
388         } else {
389             setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight());
390         }
391     }
392 
393     // implements javax.swing.DesktopManager
endDraggingFrame(JComponent f)394     public void endDraggingFrame(JComponent f) {
395         if ( dragMode == OUTLINE_DRAG_MODE && currentLoc != null) {
396             setBoundsForFrame(f, currentLoc.x, currentLoc.y, f.getWidth(), f.getHeight() );
397             currentLoc = null;
398         } else if (dragMode == FASTER_DRAG_MODE) {
399             currentBounds = null;
400             if (desktopGraphics != null) {
401                 desktopGraphics.dispose();
402                 desktopGraphics = null;
403             }
404             desktopBounds = null;
405             ((JInternalFrame)f).isDragging = false;
406         }
407     }
408 
409     // implements javax.swing.DesktopManager
beginResizingFrame(JComponent f, int direction)410     public void beginResizingFrame(JComponent f, int direction) {
411         setupDragMode(f);
412     }
413 
414     /**
415      * Calls <code>setBoundsForFrame</code> with the new values.
416      * @param f the component to be resized
417      * @param newX the new x-coordinate
418      * @param newY the new y-coordinate
419      * @param newWidth the new width
420      * @param newHeight the new height
421      */
resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight)422     public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
423 
424         if ( dragMode == DEFAULT_DRAG_MODE || dragMode == FASTER_DRAG_MODE ) {
425             setBoundsForFrame(f, newX, newY, newWidth, newHeight);
426         } else {
427             JDesktopPane desktopPane = getDesktopPane(f);
428             if (desktopPane != null){
429               Graphics g = JComponent.safelyGetGraphics(desktopPane);
430 
431               g.setXORMode(Color.white);
432               if (currentBounds != null) {
433                 g.drawRect( currentBounds.x, currentBounds.y, currentBounds.width-1, currentBounds.height-1);
434               }
435               g.drawRect( newX, newY, newWidth-1, newHeight-1);
436 
437               // Work around for 6635462, see comment in dragFrame()
438               sun.java2d.SurfaceData sData =
439                   ((sun.java2d.SunGraphics2D)g).getSurfaceData();
440               if (!sData.isSurfaceLost()) {
441                   currentBounds = new Rectangle (newX, newY, newWidth, newHeight);
442               }
443 
444               g.setPaintMode();
445               g.dispose();
446             }
447         }
448 
449     }
450 
451     // implements javax.swing.DesktopManager
endResizingFrame(JComponent f)452     public void endResizingFrame(JComponent f) {
453         if ( dragMode == OUTLINE_DRAG_MODE && currentBounds != null) {
454             setBoundsForFrame(f, currentBounds.x, currentBounds.y, currentBounds.width, currentBounds.height );
455             currentBounds = null;
456         }
457     }
458 
459 
460     /** This moves the <code>JComponent</code> and repaints the damaged areas. */
setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight)461     public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
462         f.setBounds(newX, newY, newWidth, newHeight);
463         // we must validate the hierarchy to not break the hw/lw mixing
464         f.revalidate();
465     }
466 
467     /**
468      * Convenience method to remove the desktopIcon of <b>f</b> is necessary.
469      *
470      * @param f the {@code JInternalFrame} for which to remove the
471      *          {@code desktopIcon}
472      */
removeIconFor(JInternalFrame f)473     protected void removeIconFor(JInternalFrame f) {
474         JInternalFrame.JDesktopIcon di = f.getDesktopIcon();
475         Container c = di.getParent();
476         if(c != null) {
477             c.remove(di);
478             c.repaint(di.getX(), di.getY(), di.getWidth(), di.getHeight());
479         }
480     }
481 
482     /**
483      * The {@code iconifyFrame()} code calls this to determine the proper bounds
484      * for the desktopIcon.
485      *
486      * @param f the {@code JInternalFrame} of interest
487      * @return a {@code Rectangle} containing bounds for the {@code desktopIcon}
488      */
getBoundsForIconOf(JInternalFrame f)489     protected Rectangle getBoundsForIconOf(JInternalFrame f) {
490       //
491       // Get the icon for this internal frame and its preferred size
492       //
493 
494       JInternalFrame.JDesktopIcon icon = f.getDesktopIcon();
495       Dimension prefSize = icon.getPreferredSize();
496       //
497       // Get the parent bounds and child components.
498       //
499 
500       Container c = f.getParent();
501       if (c == null) {
502           c = f.getDesktopIcon().getParent();
503       }
504 
505       if (c == null) {
506         /* the frame has not yet been added to the parent; how about (0,0) ?*/
507         return new Rectangle(0, 0, prefSize.width, prefSize.height);
508       }
509 
510       Rectangle parentBounds = c.getBounds();
511       Component [] components = c.getComponents();
512 
513 
514       //
515       // Iterate through valid default icon locations and return the
516       // first one that does not intersect any other icons.
517       //
518 
519       Rectangle availableRectangle = null;
520       JInternalFrame.JDesktopIcon currentIcon = null;
521 
522       int x = 0;
523       int y = parentBounds.height - prefSize.height;
524       int w = prefSize.width;
525       int h = prefSize.height;
526 
527       boolean found = false;
528 
529       while (!found) {
530 
531         availableRectangle = new Rectangle(x,y,w,h);
532 
533         found = true;
534 
535         for ( int i=0; i<components.length; i++ ) {
536 
537           //
538           // Get the icon for this component
539           //
540 
541           if ( components[i] instanceof JInternalFrame ) {
542             currentIcon = ((JInternalFrame)components[i]).getDesktopIcon();
543           }
544           else if ( components[i] instanceof JInternalFrame.JDesktopIcon ){
545             currentIcon = (JInternalFrame.JDesktopIcon)components[i];
546           } else
547             /* found a child that's neither an internal frame nor
548                an icon. I don't believe this should happen, but at
549                present it does and causes a null pointer exception.
550                Even when that gets fixed, this code protects against
551                the npe. hania */
552             continue;
553 
554           //
555           // If this icon intersects the current location, get next location.
556           //
557 
558           if ( !currentIcon.equals(icon) ) {
559             if ( availableRectangle.intersects(currentIcon.getBounds()) ) {
560               found = false;
561               break;
562             }
563           }
564         }
565 
566         if (currentIcon == null)
567           /* didn't find any useful children above. This probably shouldn't
568            happen, but this check protects against an npe if it ever does
569            (and it's happening now) */
570           return availableRectangle;
571 
572         x += currentIcon.getBounds().width;
573 
574         if ( x + w > parentBounds.width ) {
575           x = 0;
576           y -= h;
577         }
578       }
579 
580       return(availableRectangle);
581     }
582 
583     /**
584      * Stores the bounds of the component just before a maximize call.
585      * @param f the component about to be resized
586      * @param r the normal bounds to be saved away
587      */
setPreviousBounds(JInternalFrame f, Rectangle r)588     protected void setPreviousBounds(JInternalFrame f, Rectangle r)     {
589         f.setNormalBounds(r);
590     }
591 
592     /**
593      * Gets the normal bounds of the component prior to the component
594      * being maximized.
595      * @param f the <code>JInternalFrame</code> of interest
596      * @return the normal bounds of the component
597      */
getPreviousBounds(JInternalFrame f)598     protected Rectangle getPreviousBounds(JInternalFrame f)     {
599         return f.getNormalBounds();
600     }
601 
602     /**
603      * Sets that the component has been iconized and the bounds of the
604      * <code>desktopIcon</code> are valid.
605      *
606      * @param f     the {@code JInternalFrame} of interest
607      * @param value a {@code Boolean} signifying if component has been iconized
608      */
setWasIcon(JInternalFrame f, Boolean value)609     protected void setWasIcon(JInternalFrame f, Boolean value)  {
610         if (value != null) {
611             f.putClientProperty(HAS_BEEN_ICONIFIED_PROPERTY, value);
612         }
613     }
614 
615     /**
616      * Returns <code>true</code> if the component has been iconized
617      * and the bounds of the <code>desktopIcon</code> are valid,
618      * otherwise returns <code>false</code>.
619      *
620      * @param f the <code>JInternalFrame</code> of interest
621      * @return <code>true</code> if the component has been iconized;
622      *    otherwise returns <code>false</code>
623      */
wasIcon(JInternalFrame f)624     protected boolean wasIcon(JInternalFrame f) {
625         return (f.getClientProperty(HAS_BEEN_ICONIFIED_PROPERTY) == Boolean.TRUE);
626     }
627 
628 
getDesktopPane( JComponent frame )629     JDesktopPane getDesktopPane( JComponent frame ) {
630         JDesktopPane pane = null;
631         Component c = frame.getParent();
632 
633         // Find the JDesktopPane
634         while ( pane == null ) {
635             if ( c instanceof JDesktopPane ) {
636                 pane = (JDesktopPane)c;
637             }
638             else if ( c == null ) {
639                 break;
640             }
641             else {
642                 c = c.getParent();
643             }
644         }
645 
646         return pane;
647     }
648 
649 
650   // =========== stuff for faster frame dragging ===================
651 
dragFrameFaster(JComponent f, int newX, int newY)652    private void dragFrameFaster(JComponent f, int newX, int newY) {
653 
654       Rectangle previousBounds = new Rectangle(currentBounds.x,
655                                                currentBounds.y,
656                                                currentBounds.width,
657                                                currentBounds.height);
658 
659    // move the frame
660       currentBounds.x = newX;
661       currentBounds.y = newY;
662 
663       if (didDrag) {
664           // Only initiate cleanup if we have actually done a drag.
665           emergencyCleanup(f);
666       }
667       else {
668           didDrag = true;
669           // We reset the danger field as until now we haven't actually
670           // moved the internal frame so we don't need to initiate repaint.
671           ((JInternalFrame)f).danger = false;
672       }
673 
674       boolean floaterCollision = isFloaterCollision(previousBounds, currentBounds);
675 
676       JComponent parent = (JComponent)f.getParent();
677       Rectangle visBounds = previousBounds.intersection(desktopBounds);
678 
679       RepaintManager currentManager = RepaintManager.currentManager(f);
680 
681       currentManager.beginPaint();
682       try {
683           if(!floaterCollision) {
684               currentManager.copyArea(parent, desktopGraphics, visBounds.x,
685                                       visBounds.y,
686                                       visBounds.width,
687                                       visBounds.height,
688                                       newX - previousBounds.x,
689                                       newY - previousBounds.y,
690                                       true);
691           }
692 
693           f.setBounds(currentBounds);
694 
695           if (!floaterCollision) {
696               Rectangle r = currentBounds;
697               currentManager.notifyRepaintPerformed(parent, r.x, r.y, r.width, r.height);
698           }
699 
700           if(floaterCollision) {
701               // since we couldn't blit we just redraw as fast as possible
702               // the isDragging mucking is to avoid activating emergency
703               // cleanup
704               ((JInternalFrame)f).isDragging = false;
705               parent.paintImmediately(currentBounds);
706               ((JInternalFrame)f).isDragging = true;
707           }
708 
709           // fake out the repaint manager.  We'll take care of everything
710 
711           currentManager.markCompletelyClean(parent);
712           currentManager.markCompletelyClean(f);
713 
714           // compute the minimal newly exposed area
715           // if the rects intersect then we use computeDifference.  Otherwise
716           // we'll repaint the entire previous bounds
717           Rectangle[] dirtyRects = null;
718           if ( previousBounds.intersects(currentBounds) ) {
719               dirtyRects = SwingUtilities.computeDifference(previousBounds,
720                                                             currentBounds);
721           } else {
722               dirtyRects = new Rectangle[1];
723               dirtyRects[0] = previousBounds;
724           };
725 
726           // Fix the damage
727           for (int i = 0; i < dirtyRects.length; i++) {
728               parent.paintImmediately(dirtyRects[i]);
729               Rectangle r = dirtyRects[i];
730               currentManager.notifyRepaintPerformed(parent, r.x, r.y, r.width, r.height);
731           }
732 
733           // new areas of blit were exposed
734           if ( !(visBounds.equals(previousBounds)) ) {
735               dirtyRects = SwingUtilities.computeDifference(previousBounds,
736                                                             desktopBounds);
737               for (int i = 0; i < dirtyRects.length; i++) {
738                   dirtyRects[i].x += newX - previousBounds.x;
739                   dirtyRects[i].y += newY - previousBounds.y;
740                   ((JInternalFrame)f).isDragging = false;
741                   parent.paintImmediately(dirtyRects[i]);
742                   ((JInternalFrame)f).isDragging = true;
743                   Rectangle r = dirtyRects[i];
744                   currentManager.notifyRepaintPerformed(parent, r.x, r.y, r.width, r.height);
745               }
746 
747           }
748       } finally {
749           currentManager.endPaint();
750       }
751 
752       // update window if it's non-opaque
753       Window topLevel = SwingUtilities.getWindowAncestor(f);
754       Toolkit tk = Toolkit.getDefaultToolkit();
755       if (!topLevel.isOpaque() &&
756           (tk instanceof SunToolkit) &&
757           ((SunToolkit)tk).needUpdateWindow())
758       {
759           AWTAccessor.getWindowAccessor().updateWindow(topLevel);
760       }
761    }
762 
isFloaterCollision(Rectangle moveFrom, Rectangle moveTo)763    private boolean isFloaterCollision(Rectangle moveFrom, Rectangle moveTo) {
764       if (floatingItems.length == 0) {
765         // System.out.println("no floaters");
766          return false;
767       }
768 
769       for (int i = 0; i < floatingItems.length; i++) {
770          boolean intersectsFrom = moveFrom.intersects(floatingItems[i]);
771          if (intersectsFrom) {
772             return true;
773          }
774          boolean intersectsTo = moveTo.intersects(floatingItems[i]);
775          if (intersectsTo) {
776             return true;
777          }
778       }
779 
780       return false;
781    }
782 
findFloatingItems(JComponent f)783    private Rectangle[] findFloatingItems(JComponent f) {
784       Container desktop = f.getParent();
785       Component[] children = desktop.getComponents();
786       int i = 0;
787       for (i = 0; i < children.length; i++) {
788          if (children[i] == f) {
789             break;
790          }
791       }
792       // System.out.println(i);
793       Rectangle[] floaters = new Rectangle[i];
794       for (i = 0; i < floaters.length; i++) {
795          floaters[i] = children[i].getBounds();
796       }
797 
798       return floaters;
799    }
800 
801    /**
802      * This method is here to clean up problems associated
803      * with a race condition which can occur when the full contents
804      * of a copyArea's source argument is not available onscreen.
805      * This uses brute force to clean up in case of possible damage
806      */
emergencyCleanup(final JComponent f)807    private void emergencyCleanup(final JComponent f) {
808 
809         if ( ((JInternalFrame)f).danger ) {
810 
811            SwingUtilities.invokeLater( new Runnable(){
812                                        public void run(){
813 
814                                        ((JInternalFrame)f).isDragging = false;
815                                        f.paintImmediately(0,0,
816                                                           f.getWidth(),
817                                                           f.getHeight());
818 
819                                         //finalFrame.repaint();
820                                         ((JInternalFrame)f).isDragging = true;
821                                         // System.out.println("repair complete");
822                                        }});
823 
824              ((JInternalFrame)f).danger = false;
825         }
826 
827    }
828 
829 
830 }
831