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