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       */
  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      */
  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      */
 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      */
 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      */
 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      */
 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       */
 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
 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
 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 
 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       */
 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
 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
 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      */
 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
 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. */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 624     protected boolean wasIcon(JInternalFrame f) {
 625         return (f.getClientProperty(HAS_BEEN_ICONIFIED_PROPERTY) == Boolean.TRUE);
 626     }
 627 
 628 
 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 
 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 
 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 
 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      */
 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 }