1 /*
   2  * Copyright (c) 1997, 2006, 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 java.awt.*;
  30 import java.beans.PropertyVetoException;
  31 import java.beans.PropertyChangeEvent;
  32 import javax.swing.border.Border;
  33 import java.awt.event.ComponentListener;
  34 import java.awt.event.ComponentAdapter;
  35 import java.awt.event.ComponentEvent;
  36 
  37 /** This is an implementation of the <code>DesktopManager</code>.
  38   * It currently implements the basic behaviors for managing
  39   * <code>JInternalFrame</code>s in an arbitrary parent.
  40   * <code>JInternalFrame</code>s that are not children of a
  41   * <code>JDesktop</code> will use this component
  42   * to handle their desktop-like actions.
  43   * <p>This class provides a policy for the various JInternalFrame methods,
  44   * it is not meant to be called directly rather the various JInternalFrame
  45   * methods will call into the DesktopManager.</p>
  46   * @see JDesktopPane
  47   * @see JInternalFrame
  48   * @author David Kloba
  49   * @author Steve Wilson
  50   */
  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             setWasIcon(f, Boolean.TRUE);
 179         }
 180 
 181         if (c == null || d == null) {
 182             return;
 183         }
 184 
 185         if (c instanceof JLayeredPane) {
 186             JLayeredPane lp = (JLayeredPane)c;
 187             int layer = lp.getLayer(f);
 188             lp.putLayer(desktopIcon, layer);
 189         }
 190 
 191         // If we are maximized we already have the normal bounds recorded
 192         // don't try to re-record them, otherwise we incorrectly set the
 193         // normal bounds to maximized state.
 194         if (!f.isMaximum()) {
 195             f.setNormalBounds(f.getBounds());
 196         }
 197         d.setComponentOrderCheckingEnabled(false);
 198         c.remove(f);
 199         c.add(desktopIcon);
 200         d.setComponentOrderCheckingEnabled(true);
 201         c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
 202         if (findNext) {
 203             if (d.selectFrame(true) == null) {
 204                 // The icon is the last frame.
 205                 f.restoreSubcomponentFocus();
 206             }
 207         }
 208     }
 209 
 210     /**
 211      * Removes the desktopIcon from its parent and adds its frame
 212      * to the parent.
 213      * @param f the <code>JInternalFrame</code> to be de-iconified
 214      */
 215     public void deiconifyFrame(JInternalFrame f) {
 216         JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
 217         Container c = desktopIcon.getParent();
 218         JDesktopPane d = f.getDesktopPane();
 219         if (c != null && d != null) {
 220             c.add(f);
 221             // If the frame is to be restored to a maximized state make
 222             // sure it still fills the whole desktop.
 223             if (f.isMaximum()) {
 224                 Rectangle desktopBounds = c.getBounds();
 225                 if (f.getWidth() != desktopBounds.width ||
 226                         f.getHeight() != desktopBounds.height) {
 227                     setBoundsForFrame(f, 0, 0,
 228                         desktopBounds.width, desktopBounds.height);
 229                 }
 230             }
 231             removeIconFor(f);
 232             if (f.isSelected()) {
 233                 f.moveToFront();
 234                 f.restoreSubcomponentFocus();
 235             }
 236             else {
 237                 try {
 238                     f.setSelected(true);
 239                 } catch (PropertyVetoException e2) {}
 240 
 241             }
 242         }
 243     }
 244 
 245     /** This will activate <b>f</b> moving it to the front. It will
 246       * set the current active frame's (if any)
 247       * <code>IS_SELECTED_PROPERTY</code> to <code>false</code>.
 248       * There can be only one active frame across all Layers.
 249       * @param f the <code>JInternalFrame</code> to be activated
 250       */
 251     public void activateFrame(JInternalFrame f) {
 252         Container p = f.getParent();
 253         Component[] c;
 254         JDesktopPane d = f.getDesktopPane();
 255         JInternalFrame currentlyActiveFrame =
 256           (d == null) ? null : d.getSelectedFrame();
 257         // fix for bug: 4162443
 258         if(p == null) {
 259             // If the frame is not in parent, its icon maybe, check it
 260             p = f.getDesktopIcon().getParent();
 261             if(p == null)
 262                 return;
 263         }
 264         // we only need to keep track of the currentActive InternalFrame, if any
 265         if (currentlyActiveFrame == null){
 266           if (d != null) { d.setSelectedFrame(f);}
 267         } else if (currentlyActiveFrame != f) {
 268           // if not the same frame as the current active
 269           // we deactivate the current
 270           if (currentlyActiveFrame.isSelected()) {
 271             try {
 272               currentlyActiveFrame.setSelected(false);
 273             }
 274             catch(PropertyVetoException e2) {}
 275           }
 276           if (d != null) { d.setSelectedFrame(f);}
 277         }
 278         f.moveToFront();
 279     }
 280 
 281     // implements javax.swing.DesktopManager
 282     public void deactivateFrame(JInternalFrame f) {
 283       JDesktopPane d = f.getDesktopPane();
 284       JInternalFrame currentlyActiveFrame =
 285           (d == null) ? null : d.getSelectedFrame();
 286       if (currentlyActiveFrame == f)
 287         d.setSelectedFrame(null);
 288     }
 289 
 290     // implements javax.swing.DesktopManager
 291     public void beginDraggingFrame(JComponent f) {
 292         setupDragMode(f);
 293 
 294         if (dragMode == FASTER_DRAG_MODE) {
 295           Component desktop = f.getParent();
 296           floatingItems = findFloatingItems(f);
 297           currentBounds = f.getBounds();
 298           if (desktop instanceof JComponent) {
 299               desktopBounds = ((JComponent)desktop).getVisibleRect();
 300           }
 301           else {
 302               desktopBounds = desktop.getBounds();
 303               desktopBounds.x = desktopBounds.y = 0;
 304           }
 305           desktopGraphics = JComponent.safelyGetGraphics(desktop);
 306           ((JInternalFrame)f).isDragging = true;
 307           didDrag = false;
 308         }
 309 
 310     }
 311 
 312     private void setupDragMode(JComponent f) {
 313         JDesktopPane p = getDesktopPane(f);
 314         Container parent = f.getParent();
 315         dragMode = DEFAULT_DRAG_MODE;
 316         if (p != null) {
 317             String mode = (String)p.getClientProperty("JDesktopPane.dragMode");
 318             if (mode != null && mode.equals("outline")) {
 319                 dragMode = OUTLINE_DRAG_MODE;
 320             } else if (mode != null && mode.equals("faster")
 321                     && f instanceof JInternalFrame
 322                     && ((JInternalFrame)f).isOpaque() &&
 323                        (parent == null || parent.isOpaque())) {
 324                 dragMode = FASTER_DRAG_MODE;
 325             } else {
 326                 if (p.getDragMode() == JDesktopPane.OUTLINE_DRAG_MODE ) {
 327                     dragMode = OUTLINE_DRAG_MODE;
 328                 } else if ( p.getDragMode() == JDesktopPane.LIVE_DRAG_MODE
 329                         && f instanceof JInternalFrame
 330                         && ((JInternalFrame)f).isOpaque()) {
 331                     dragMode = FASTER_DRAG_MODE;
 332                 } else {
 333                     dragMode = DEFAULT_DRAG_MODE;
 334                 }
 335             }
 336         }
 337     }
 338 
 339     private transient Point currentLoc = null;
 340 
 341     /**
 342       * Moves the visible location of the frame being dragged
 343       * to the location specified.  The means by which this occurs can vary depending
 344       * on the dragging algorithm being used.  The actual logical location of the frame
 345       * might not change until <code>endDraggingFrame</code> is called.
 346       */
 347     public void dragFrame(JComponent f, int newX, int newY) {
 348 
 349         if (dragMode == OUTLINE_DRAG_MODE) {
 350             JDesktopPane desktopPane = getDesktopPane(f);
 351             if (desktopPane != null){
 352               Graphics g = JComponent.safelyGetGraphics(desktopPane);
 353 
 354               g.setXORMode(Color.white);
 355               if (currentLoc != null) {
 356                 g.drawRect(currentLoc.x, currentLoc.y,
 357                         f.getWidth()-1, f.getHeight()-1);
 358               }
 359               g.drawRect( newX, newY, f.getWidth()-1, f.getHeight()-1);
 360               currentLoc = new Point (newX, newY);
 361               g.dispose();
 362             }
 363         } else if (dragMode == FASTER_DRAG_MODE) {
 364           dragFrameFaster(f, newX, newY);
 365         } else {
 366             setBoundsForFrame(f, newX, newY, f.getWidth(), f.getHeight());
 367         }
 368     }
 369 
 370     // implements javax.swing.DesktopManager
 371     public void endDraggingFrame(JComponent f) {
 372         if ( dragMode == OUTLINE_DRAG_MODE && currentLoc != null) {
 373             setBoundsForFrame(f, currentLoc.x, currentLoc.y, f.getWidth(), f.getHeight() );
 374             currentLoc = null;
 375         } else if (dragMode == FASTER_DRAG_MODE) {
 376             currentBounds = null;
 377             if (desktopGraphics != null) {
 378                 desktopGraphics.dispose();
 379                 desktopGraphics = null;
 380             }
 381             desktopBounds = null;
 382             ((JInternalFrame)f).isDragging = false;
 383         }
 384     }
 385 
 386     // implements javax.swing.DesktopManager
 387     public void beginResizingFrame(JComponent f, int direction) {
 388         setupDragMode(f);
 389     }
 390 
 391     /**
 392      * Calls <code>setBoundsForFrame</code> with the new values.
 393      * @param f the component to be resized
 394      * @param newX the new x-coordinate
 395      * @param newY the new y-coordinate
 396      * @param newWidth the new width
 397      * @param newHeight the new height
 398      */
 399     public void resizeFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
 400 
 401         if ( dragMode == DEFAULT_DRAG_MODE || dragMode == FASTER_DRAG_MODE ) {
 402             setBoundsForFrame(f, newX, newY, newWidth, newHeight);
 403         } else {
 404             JDesktopPane desktopPane = getDesktopPane(f);
 405             if (desktopPane != null){
 406               Graphics g = JComponent.safelyGetGraphics(desktopPane);
 407 
 408               g.setXORMode(Color.white);
 409               if (currentBounds != null) {
 410                 g.drawRect( currentBounds.x, currentBounds.y, currentBounds.width-1, currentBounds.height-1);
 411               }
 412               g.drawRect( newX, newY, newWidth-1, newHeight-1);
 413               currentBounds = new Rectangle (newX, newY, newWidth, newHeight);
 414               g.setPaintMode();
 415               g.dispose();
 416             }
 417         }
 418 
 419     }
 420 
 421     // implements javax.swing.DesktopManager
 422     public void endResizingFrame(JComponent f) {
 423         if ( dragMode == OUTLINE_DRAG_MODE && currentBounds != null) {
 424             setBoundsForFrame(f, currentBounds.x, currentBounds.y, currentBounds.width, currentBounds.height );
 425             currentBounds = null;
 426         }
 427     }
 428 
 429 
 430     /** This moves the <code>JComponent</code> and repaints the damaged areas. */
 431     public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
 432         boolean didResize = (f.getWidth() != newWidth || f.getHeight() != newHeight);
 433         f.setBounds(newX, newY, newWidth, newHeight);
 434         if(didResize) {
 435             f.validate();
 436         }
 437     }
 438 
 439     /** Convenience method to remove the desktopIcon of <b>f</b> is necessary. */
 440     protected void removeIconFor(JInternalFrame f) {
 441         JInternalFrame.JDesktopIcon di = f.getDesktopIcon();
 442         Container c = di.getParent();
 443         if(c != null) {
 444             c.remove(di);
 445             c.repaint(di.getX(), di.getY(), di.getWidth(), di.getHeight());
 446         }
 447     }
 448 
 449     /** The iconifyFrame() code calls this to determine the proper bounds
 450       * for the desktopIcon.
 451       */
 452 
 453     protected Rectangle getBoundsForIconOf(JInternalFrame f) {
 454       //
 455       // Get the icon for this internal frame and its preferred size
 456       //
 457 
 458       JInternalFrame.JDesktopIcon icon = f.getDesktopIcon();
 459       Dimension prefSize = icon.getPreferredSize();
 460       //
 461       // Get the parent bounds and child components.
 462       //
 463 
 464       Container c = f.getParent();
 465       if (c == null) {
 466           c = f.getDesktopIcon().getParent();
 467       }
 468 
 469       if (c == null) {
 470         /* the frame has not yet been added to the parent; how about (0,0) ?*/
 471         return new Rectangle(0, 0, prefSize.width, prefSize.height);
 472       }
 473 
 474       Rectangle parentBounds = c.getBounds();
 475       Component [] components = c.getComponents();
 476 
 477 
 478       //
 479       // Iterate through valid default icon locations and return the
 480       // first one that does not intersect any other icons.
 481       //
 482 
 483       Rectangle availableRectangle = null;
 484       JInternalFrame.JDesktopIcon currentIcon = null;
 485 
 486       int x = 0;
 487       int y = parentBounds.height - prefSize.height;
 488       int w = prefSize.width;
 489       int h = prefSize.height;
 490 
 491       boolean found = false;
 492 
 493       while (!found) {
 494 
 495         availableRectangle = new Rectangle(x,y,w,h);
 496 
 497         found = true;
 498 
 499         for ( int i=0; i<components.length; i++ ) {
 500 
 501           //
 502           // Get the icon for this component
 503           //
 504 
 505           if ( components[i] instanceof JInternalFrame ) {
 506             currentIcon = ((JInternalFrame)components[i]).getDesktopIcon();
 507           }
 508           else if ( components[i] instanceof JInternalFrame.JDesktopIcon ){
 509             currentIcon = (JInternalFrame.JDesktopIcon)components[i];
 510           } else
 511             /* found a child that's neither an internal frame nor
 512                an icon. I don't believe this should happen, but at
 513                present it does and causes a null pointer exception.
 514                Even when that gets fixed, this code protects against
 515                the npe. hania */
 516             continue;
 517 
 518           //
 519           // If this icon intersects the current location, get next location.
 520           //
 521 
 522           if ( !currentIcon.equals(icon) ) {
 523             if ( availableRectangle.intersects(currentIcon.getBounds()) ) {
 524               found = false;
 525               break;
 526             }
 527           }
 528         }
 529 
 530         if (currentIcon == null)
 531           /* didn't find any useful children above. This probably shouldn't
 532            happen, but this check protects against an npe if it ever does
 533            (and it's happening now) */
 534           return availableRectangle;
 535 
 536         x += currentIcon.getBounds().width;
 537 
 538         if ( x + w > parentBounds.width ) {
 539           x = 0;
 540           y -= h;
 541         }
 542       }
 543 
 544       return(availableRectangle);
 545     }
 546 
 547     /**
 548      * Stores the bounds of the component just before a maximize call.
 549      * @param f the component about to be resized
 550      * @param r the normal bounds to be saved away
 551      */
 552     protected void setPreviousBounds(JInternalFrame f, Rectangle r)     {
 553         f.setNormalBounds(r);
 554     }
 555 
 556     /**
 557      * Gets the normal bounds of the component prior to the component
 558      * being maximized.
 559      * @param f the <code>JInternalFrame</code> of interest
 560      * @return the normal bounds of the component
 561      */
 562     protected Rectangle getPreviousBounds(JInternalFrame f)     {
 563         return f.getNormalBounds();
 564     }
 565 
 566     /**
 567      * Sets that the component has been iconized and the bounds of the
 568      * <code>desktopIcon</code> are valid.
 569      */
 570     protected void setWasIcon(JInternalFrame f, Boolean value)  {
 571         if (value != null) {
 572             f.putClientProperty(HAS_BEEN_ICONIFIED_PROPERTY, value);
 573         }
 574     }
 575 
 576     /**
 577      * Returns <code>true</code> if the component has been iconized
 578      * and the bounds of the <code>desktopIcon</code> are valid,
 579      * otherwise returns <code>false</code>.
 580      *
 581      * @param f the <code>JInternalFrame</code> of interest
 582      * @return <code>true</code> if the component has been iconized;
 583      *    otherwise returns <code>false</code>
 584      */
 585     protected boolean wasIcon(JInternalFrame f) {
 586         return (f.getClientProperty(HAS_BEEN_ICONIFIED_PROPERTY) == Boolean.TRUE);
 587     }
 588 
 589 
 590     JDesktopPane getDesktopPane( JComponent frame ) {
 591         JDesktopPane pane = null;
 592         Component c = frame.getParent();
 593 
 594         // Find the JDesktopPane
 595         while ( pane == null ) {
 596             if ( c instanceof JDesktopPane ) {
 597                 pane = (JDesktopPane)c;
 598             }
 599             else if ( c == null ) {
 600                 break;
 601             }
 602             else {
 603                 c = c.getParent();
 604             }
 605         }
 606 
 607         return pane;
 608     }
 609 
 610 
 611   // =========== stuff for faster frame dragging ===================
 612 
 613    private void dragFrameFaster(JComponent f, int newX, int newY) {
 614 
 615       Rectangle previousBounds = new Rectangle(currentBounds.x,
 616                                                currentBounds.y,
 617                                                currentBounds.width,
 618                                                currentBounds.height);
 619 
 620    // move the frame
 621       currentBounds.x = newX;
 622       currentBounds.y = newY;
 623 
 624       if (didDrag) {
 625           // Only initiate cleanup if we have actually done a drag.
 626           emergencyCleanup(f);
 627       }
 628       else {
 629           didDrag = true;
 630           // We reset the danger field as until now we haven't actually
 631           // moved the internal frame so we don't need to initiate repaint.
 632           ((JInternalFrame)f).danger = false;
 633       }
 634 
 635       boolean floaterCollision = isFloaterCollision(previousBounds, currentBounds);
 636 
 637     // System.out.println(previousBounds);
 638       JComponent parent = (JComponent)f.getParent();
 639       Rectangle visBounds = previousBounds.intersection(desktopBounds);
 640     //  System.out.println(previousBounds);
 641 
 642 
 643      // System.out.println(visBounds);
 644 
 645       RepaintManager currentManager = RepaintManager.currentManager(f);
 646 
 647       currentManager.beginPaint();
 648       try {
 649           if(!floaterCollision) {
 650               currentManager.copyArea(parent, desktopGraphics, visBounds.x,
 651                                       visBounds.y,
 652                                       visBounds.width,
 653                                       visBounds.height,
 654                                       newX - previousBounds.x,
 655                                       newY - previousBounds.y,
 656                                       true);
 657           }
 658 
 659           f.setBounds(currentBounds);
 660 
 661           if(floaterCollision) {
 662               // since we couldn't blit we just redraw as fast as possible
 663               // the isDragging mucking is to avoid activating emergency
 664               // cleanup
 665               ((JInternalFrame)f).isDragging = false;
 666               parent.paintImmediately(currentBounds);
 667               ((JInternalFrame)f).isDragging = true;
 668           }
 669 
 670           // fake out the repaint manager.  We'll take care of everything
 671 
 672           currentManager.markCompletelyClean(parent);
 673           currentManager.markCompletelyClean(f);
 674 
 675           // compute the minimal newly exposed area
 676           // if the rects intersect then we use computeDifference.  Otherwise
 677           // we'll repaint the entire previous bounds
 678           Rectangle[] dirtyRects = null;
 679           if ( previousBounds.intersects(currentBounds) ) {
 680               dirtyRects = SwingUtilities.computeDifference(previousBounds,
 681                                                             currentBounds);
 682           } else {
 683               dirtyRects = new Rectangle[1];
 684               dirtyRects[0] = previousBounds;
 685               //  System.out.println("no intersection");
 686           };
 687 
 688           // Fix the damage
 689           for (int i = 0; i < dirtyRects.length; i++) {
 690               parent.paintImmediately(dirtyRects[i]);
 691           }
 692 
 693           // new areas of blit were exposed
 694           if ( !(visBounds.equals(previousBounds)) ) {
 695               dirtyRects = SwingUtilities.computeDifference(previousBounds,
 696                                                             desktopBounds);
 697               for (int i = 0; i < dirtyRects.length; i++) {
 698                   dirtyRects[i].x += newX - previousBounds.x;
 699                   dirtyRects[i].y += newY - previousBounds.y;
 700                   ((JInternalFrame)f).isDragging = false;
 701 
 702                   parent.paintImmediately(dirtyRects[i]);
 703                   ((JInternalFrame)f).isDragging = true;
 704 
 705                   // System.out.println(dirtyRects[i]);
 706               }
 707 
 708           }
 709       } finally {
 710           currentManager.endPaint();
 711       }
 712    }
 713 
 714    private boolean isFloaterCollision(Rectangle moveFrom, Rectangle moveTo) {
 715       if (floatingItems.length == 0) {
 716         // System.out.println("no floaters");
 717          return false;
 718       }
 719 
 720       for (int i = 0; i < floatingItems.length; i++) {
 721          boolean intersectsFrom = moveFrom.intersects(floatingItems[i]);
 722          if (intersectsFrom) {
 723             return true;
 724          }
 725          boolean intersectsTo = moveTo.intersects(floatingItems[i]);
 726          if (intersectsTo) {
 727             return true;
 728          }
 729       }
 730 
 731       return false;
 732    }
 733 
 734    private Rectangle[] findFloatingItems(JComponent f) {
 735       Container desktop = f.getParent();
 736       Component[] children = desktop.getComponents();
 737       int i = 0;
 738       for (i = 0; i < children.length; i++) {
 739          if (children[i] == f) {
 740             break;
 741          }
 742       }
 743       // System.out.println(i);
 744       Rectangle[] floaters = new Rectangle[i];
 745       for (i = 0; i < floaters.length; i++) {
 746          floaters[i] = children[i].getBounds();
 747       }
 748 
 749       return floaters;
 750    }
 751 
 752    /**
 753      * This method is here to clean up problems associated
 754      * with a race condition which can occur when the full contents
 755      * of a copyArea's source argument is not available onscreen.
 756      * This uses brute force to clean up in case of possible damage
 757      */
 758    private void emergencyCleanup(final JComponent f) {
 759 
 760         if ( ((JInternalFrame)f).danger ) {
 761 
 762            SwingUtilities.invokeLater( new Runnable(){
 763                                        public void run(){
 764 
 765                                        ((JInternalFrame)f).isDragging = false;
 766                                        f.paintImmediately(0,0,
 767                                                           f.getWidth(),
 768                                                           f.getHeight());
 769 
 770                                         //finalFrame.repaint();
 771                                         ((JInternalFrame)f).isDragging = true;
 772                                         // System.out.println("repair complete");
 773                                        }});
 774 
 775              ((JInternalFrame)f).danger = false;
 776         }
 777 
 778    }
 779 
 780 
 781 }