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