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