Source for javax.swing.DefaultDesktopManager

   1: /* DefaultDesktopManager.java --
   2:    Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing;
  40: 
  41: import java.awt.Component;
  42: import java.awt.Container;
  43: import java.awt.Dimension;
  44: import java.awt.Insets;
  45: import java.awt.Rectangle;
  46: import java.beans.PropertyVetoException;
  47: import java.io.Serializable;
  48: 
  49: import javax.swing.JInternalFrame.JDesktopIcon;
  50: 
  51: /**
  52:  * The default implementation of DesktopManager for
  53:  * Swing. It implements the basic beaviours for JInternalFrames in arbitrary
  54:  * parents. The methods provided by the class are not meant to be called by
  55:  * the user, instead, the JInternalFrame methods will call these methods.
  56:  */
  57: public class DefaultDesktopManager implements DesktopManager, Serializable
  58: {
  59:   /** DOCUMENT ME! */
  60:   private static final long serialVersionUID = 4657624909838017887L;
  61: 
  62:   /** The property change event fired when the wasIcon property changes. */
  63:   static final String WAS_ICON_ONCE_PROPERTY = "wasIconOnce";
  64: 
  65:   /**
  66:    * The method of dragging used by the JDesktopPane that parents the
  67:    * JInternalFrame that is being dragged.
  68:    */
  69:   private int currentDragMode = 0;
  70: 
  71:   /**
  72:    * The cache of the bounds used to draw the outline rectangle when
  73:    * OUTLINE_DRAG_MODE is used.
  74:    */
  75:   private transient Rectangle dragCache = new Rectangle();
  76: 
  77:   /**
  78:    * A cached JDesktopPane that is stored when the JInternalFrame is initially
  79:    * dragged.
  80:    */
  81:   private transient Container pane;
  82: 
  83:   /**
  84:    * An array of Rectangles that holds the bounds of the JDesktopIcons in the
  85:    * JDesktopPane when looking for where to place a new icon.
  86:    */
  87:   private transient Rectangle[] iconRects;
  88: 
  89:   /**
  90:    * This creates a new DefaultDesktopManager object.
  91:    */
  92:   public DefaultDesktopManager()
  93:   {
  94:   }
  95: 
  96:   /**
  97:    * This method is not normally called since the user will typically add the
  98:    * JInternalFrame to a Container. If this is called, it will try to
  99:    * determine the parent of the JInternalFrame and remove any icon that
 100:    * represents this JInternalFrame and add this JInternalFrame.
 101:    *
 102:    * @param frame The JInternalFrame to open.
 103:    */
 104:   public void openFrame(JInternalFrame frame)
 105:   {
 106:     Container c = frame.getParent();
 107:     if (c == null)
 108:       c = frame.getDesktopIcon().getParent();
 109:     if (c == null)
 110:       return;
 111: 
 112:     c.remove(frame.getDesktopIcon());
 113:     c.add(frame);
 114:     frame.setVisible(true);
 115:   }
 116: 
 117:   /**
 118:    * This method removes the JInternalFrame and JDesktopIcon (if one is
 119:    * present) from their parents.
 120:    *
 121:    * @param frame The JInternalFrame to close.
 122:    */
 123:   public void closeFrame(JInternalFrame frame)
 124:   {
 125:     Container c = frame.getParent();
 126:     frame.doDefaultCloseAction();
 127: 
 128:     if (c != null)
 129:       {
 130:     if (frame.isIcon())
 131:       c.remove(frame.getDesktopIcon());
 132:     else
 133:       c.remove(frame);
 134:     c.repaint();
 135:       }
 136:   }
 137: 
 138:   /**
 139:    * This method resizes the JInternalFrame to match its parent's bounds.
 140:    *
 141:    * @param frame The JInternalFrame to maximize.
 142:    */
 143:   public void maximizeFrame(JInternalFrame frame)
 144:   {
 145:     // Can't maximize from iconified state.
 146:     // It can only return to maximized state, but that would fall under
 147:     // deiconify.
 148:     if (frame.isIcon())
 149:       return;
 150:     frame.setNormalBounds(frame.getBounds());
 151: 
 152:     Container p = frame.getParent();
 153:     if (p != null)
 154:       {
 155:     Rectangle pBounds = p.getBounds();
 156:     Insets insets = p.getInsets();
 157:     pBounds.width -= insets.left + insets.right;
 158:     pBounds.height -= insets.top + insets.bottom;
 159: 
 160:     setBoundsForFrame(frame, 0, 0, pBounds.width, pBounds.height);
 161:       }
 162:     if (p instanceof JDesktopPane)
 163:       ((JDesktopPane) p).setSelectedFrame(frame);
 164:     else
 165:       {
 166:     try
 167:       {
 168:         frame.setSelected(true);
 169:       }
 170:     catch (PropertyVetoException e)
 171:       {
 172:         // Do nothing.
 173:       }
 174:       }
 175:   }
 176: 
 177:   /**
 178:    * This method restores the JInternalFrame's bounds to what they were
 179:    * previous to the setMaximize call.
 180:    *
 181:    * @param frame The JInternalFrame to minimize.
 182:    */
 183:   public void minimizeFrame(JInternalFrame frame)
 184:   {
 185:     Rectangle normalBounds = frame.getNormalBounds();
 186: 
 187:     JDesktopPane p = frame.getDesktopPane();
 188:     if (p != null)
 189:       p.setSelectedFrame(frame);
 190:     else
 191:       {
 192:         try
 193:           {
 194:             frame.setSelected(true);
 195:           }
 196:         catch (PropertyVetoException e)
 197:           {
 198:             // Do nothing.
 199:           }
 200:       }
 201: 
 202:     setBoundsForFrame(frame, normalBounds.x, normalBounds.y,
 203:                       normalBounds.width, normalBounds.height);
 204:   }
 205: 
 206:   /**
 207:    * This method removes the JInternalFrame from its parent and adds its
 208:    * JDesktopIcon representation.
 209:    *
 210:    * @param frame The JInternalFrame to iconify.
 211:    */
 212:   public void iconifyFrame(JInternalFrame frame)
 213:   {
 214:     JDesktopPane p = frame.getDesktopPane();
 215:     JDesktopIcon icon = frame.getDesktopIcon();
 216:     if (p != null && p.getSelectedFrame() == frame)
 217:       p.setSelectedFrame(null);
 218:     else
 219:       {
 220:         try
 221:           {
 222:             frame.setSelected(false);
 223:           }
 224:         catch (PropertyVetoException e)
 225:           {
 226:           }
 227:       }
 228: 
 229:     Container c = frame.getParent();
 230: 
 231:     if (!wasIcon(frame))
 232:       {
 233:         Rectangle r = getBoundsForIconOf(frame);
 234:         icon.setBounds(r);
 235:         setWasIcon(frame, Boolean.TRUE);
 236:       }
 237: 
 238:     if (c != null)
 239:       {
 240:         if (icon != null)
 241:           {
 242:             c.add(icon);
 243:             icon.setVisible(true);
 244:           }
 245:         c.remove(frame);
 246:       }
 247:   }
 248: 
 249:   /**
 250:    * This method removes the JInternalFrame's JDesktopIcon representation and
 251:    * adds the JInternalFrame back to its parent.
 252:    *
 253:    * @param frame The JInternalFrame to deiconify.
 254:    */
 255:   public void deiconifyFrame(JInternalFrame frame)
 256:   {
 257:     JDesktopIcon icon = frame.getDesktopIcon();
 258:     Container c = icon.getParent();
 259: 
 260:     removeIconFor(frame);
 261:     c.add(frame);
 262:     frame.setVisible(true);
 263: 
 264:     if (!frame.isSelected())
 265:       {
 266:         JDesktopPane p = frame.getDesktopPane();
 267:         if (p != null)
 268:           p.setSelectedFrame(frame);
 269:         else
 270:           {
 271:             try
 272:               {
 273:                 frame.setSelected(true);
 274:               }
 275:             catch (PropertyVetoException e)
 276:               {
 277:                 // Do nothing.
 278:               }
 279:           }
 280:       }
 281: 
 282:     c.invalidate();
 283:   }
 284: 
 285:   /**
 286:    * This method activates the JInternalFrame by moving it to the front and
 287:    * selecting it.
 288:    *
 289:    * @param frame The JInternalFrame to activate.
 290:    */
 291:   public void activateFrame(JInternalFrame frame)
 292:   {
 293:     JDesktopPane p = frame.getDesktopPane();
 294: 
 295:     if (p != null)
 296:       p.setSelectedFrame(frame);
 297:     else
 298:       {
 299:         try
 300:           {
 301:             frame.setSelected(true);
 302:           }
 303:         catch (PropertyVetoException e)
 304:           {
 305:           }
 306:       }
 307: 
 308:     frame.toFront();
 309:   }
 310: 
 311:   /**
 312:    * This method is called when the JInternalFrame loses focus.
 313:    *
 314:    * @param frame The JInternalFram to deactivate.
 315:    */
 316:   public void deactivateFrame(JInternalFrame frame)
 317:   {
 318:     JDesktopPane p = frame.getDesktopPane();
 319:     if (p != null)
 320:       {
 321:         if (p.getSelectedFrame() == frame)
 322:           p.setSelectedFrame(null);
 323:       }
 324:     else
 325:       {
 326:         try
 327:           {
 328:             frame.setSelected(false);
 329:           }
 330:         catch (PropertyVetoException e)
 331:           {
 332:           }
 333:       }
 334:   }
 335: 
 336:   /**
 337:    * This method is called to indicate that the DesktopManager should prepare
 338:    * to drag the JInternalFrame. Any state information needed to drag the
 339:    * frame will be prepared now.
 340:    *
 341:    * @param component The JComponent to drag, usually a JInternalFrame.
 342:    */
 343:   public void beginDraggingFrame(JComponent component)
 344:   {
 345:     if (component instanceof JDesktopIcon)
 346:       pane = ((JDesktopIcon) component).getInternalFrame().getDesktopPane();
 347:     else
 348:       pane = ((JInternalFrame) component).getDesktopPane();
 349:     if (pane == null)
 350:       return;
 351: 
 352:     dragCache = component.getBounds();
 353: 
 354:     if (! (pane instanceof JDesktopPane))
 355:       currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
 356:     else
 357:       currentDragMode = ((JDesktopPane) pane).getDragMode();
 358:   }
 359: 
 360:   /**
 361:    * This method is called to drag the JInternalFrame to a new location.
 362:    *
 363:    * @param component The JComponent to drag, usually a JInternalFrame.
 364:    *
 365:    * @param newX The new x coordinate.
 366:    * @param newY The new y coordinate.
 367:    */
 368:   public void dragFrame(JComponent component, int newX, int newY)
 369:   {
 370:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 371:       {
 372:         // FIXME: Do outline drag mode painting.
 373:       }
 374:     else
 375:       {
 376:         Rectangle b = component.getBounds();
 377:         if (component instanceof JDesktopIcon)
 378:           component.setBounds(newX, newY, b.width, b.height);
 379:         else
 380:           setBoundsForFrame((JInternalFrame) component, newX, newY, b.width,
 381:                             b.height);
 382:       }
 383:   }
 384: 
 385:   /**
 386:    * This method indicates that the dragging is done. Any state information
 387:    * stored by the DesktopManager can be cleared.
 388:    *
 389:    * @param component The JComponent that has finished dragging.
 390:    */
 391:   public void endDraggingFrame(JComponent component)
 392:   {
 393:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 394:       {
 395:         setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
 396:                           dragCache.width, dragCache.height);
 397:         pane = null;
 398:         dragCache = null;
 399:       }
 400:     component.repaint();
 401:   }
 402: 
 403:   /**
 404:    * This method is called to indicate that the given JComponent will be
 405:    * resized. Any state information necessary to resize the JComponent will
 406:    * be prepared now.
 407:    *
 408:    * @param component The JComponent to resize, usually a JInternalFrame.
 409:    * @param direction The direction to drag in (a SwingConstant).
 410:    */
 411:   public void beginResizingFrame(JComponent component, int direction)
 412:   {
 413:     pane = ((JInternalFrame) component).getDesktopPane();
 414:     if (pane == null)
 415:       return;
 416: 
 417:     dragCache = component.getBounds();
 418:     if (! (pane instanceof JDesktopPane))
 419:       currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
 420:     else
 421:       currentDragMode = ((JDesktopPane) pane).getDragMode();
 422:   }
 423: 
 424:   /**
 425:    * This method resizes the give JComponent.
 426:    *
 427:    * @param component The JComponent to resize.
 428:    * @param newX The new x coordinate.
 429:    * @param newY The new y coordinate.
 430:    * @param newWidth The new width.
 431:    * @param newHeight The new height.
 432:    */
 433:   public void resizeFrame(JComponent component, int newX, int newY,
 434:                           int newWidth, int newHeight)
 435:   {
 436:     dragCache.setBounds(newX, newY, newWidth, newHeight);
 437: 
 438:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 439:       {
 440:         // FIXME: Do outline drag painting.
 441:       }
 442:     else
 443:       setBoundsForFrame(component, dragCache.x, dragCache.y, dragCache.width,
 444:                         dragCache.height);
 445:   }
 446: 
 447:   /**
 448:    * This method is called to indicate that the given JComponent has finished
 449:    * dragging. Any state information stored by the DesktopManager can be
 450:    * cleared.
 451:    *
 452:    * @param component The JComponent that finished resizing.
 453:    */
 454:   public void endResizingFrame(JComponent component)
 455:   {
 456:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 457:       {
 458:         setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
 459:                           dragCache.width, dragCache.height);
 460:         pane = null;
 461:         dragCache = null;
 462:       }
 463:     component.repaint();
 464:   }
 465: 
 466:   /**
 467:    * This method calls setBounds with the given parameters and repaints the
 468:    * JComponent.
 469:    *
 470:    * @param component The JComponent to set bounds for.
 471:    * @param newX The new x coordinate.
 472:    * @param newY The new y coordinate.
 473:    * @param newWidth The new width.
 474:    * @param newHeight The new height.
 475:    */
 476:   public void setBoundsForFrame(JComponent component, int newX, int newY,
 477:                                 int newWidth, int newHeight)
 478:   {
 479:     component.setBounds(newX, newY, newWidth, newHeight);
 480:     component.revalidate();
 481: 
 482:     // If not null, I'd rather repaint the parent
 483:     if (component.getParent() != null)
 484:       component.getParent().repaint();
 485:     else
 486:       component.repaint();
 487:   }
 488: 
 489:   /**
 490:    * This is a helper method that removes the JDesktopIcon of the given
 491:    * JInternalFrame from the parent.
 492:    *
 493:    * @param frame The JInternalFrame to remove an icon for.
 494:    */
 495:   protected void removeIconFor(JInternalFrame frame)
 496:   {
 497:     JDesktopIcon icon = frame.getDesktopIcon();
 498:     Container c = icon.getParent();
 499:     if (c != null && icon != null)
 500:       c.remove(icon);
 501:   }
 502: 
 503:   /**
 504:    * This method is called by iconifyFrame to determine the bounds of the
 505:    * JDesktopIcon for the given JInternalFrame.
 506:    *
 507:    * @param frame The JInternalFrame to find the bounds of its JDesktopIcon
 508:    *        for.
 509:    *
 510:    * @return The bounds of the JDesktopIcon.
 511:    */
 512:   protected Rectangle getBoundsForIconOf(JInternalFrame frame)
 513:   {
 514:     // IconRects has no order to it.
 515:     // The icon _must_ be placed in the first free slot (working from 
 516:     // the bottom left corner)
 517:     // The icon also must not be placed where another icon is placed 
 518:     // (regardless whether that frame is an icon currently or not)
 519:     JDesktopPane desktopPane = frame.getDesktopPane();
 520: 
 521:     if (desktopPane == null)
 522:       return frame.getDesktopIcon().getBounds();
 523: 
 524:     Rectangle paneBounds = desktopPane.getBounds();
 525:     Insets insets = desktopPane.getInsets();
 526:     Dimension pref = frame.getDesktopIcon().getPreferredSize();
 527: 
 528:     Component[] frames = desktopPane.getComponents();
 529: 
 530:     int count = 0;
 531:     for (int i = 0, j = 0; i < frames.length; i++)
 532:       if (frames[i] instanceof JDesktopIcon
 533:           || frames[i] instanceof JInternalFrame
 534:           && ((JInternalFrame) frames[i]).getWasIcon() && frames[i] != frame)
 535:     count++;
 536:     iconRects = new Rectangle[count];
 537:     for (int i = 0, j = 0; i < frames.length; i++)
 538:       if (frames[i] instanceof JDesktopIcon)
 539:         iconRects[--count] = frames[i].getBounds();
 540:       else if (frames[i] instanceof JInternalFrame
 541:                && ((JInternalFrame) frames[i]).getWasIcon()
 542:                && frames[i] != frame)
 543:         iconRects[--count] = ((JInternalFrame) frames[i])
 544:                                                  .getDesktopIcon().getBounds();
 545: 
 546:     int startingX = insets.left;
 547:     int startingY = paneBounds.height - insets.bottom - pref.height;
 548:     Rectangle ideal = new Rectangle(startingX, startingY, pref.width,
 549:                                     pref.height);
 550:     boolean clear = true;
 551: 
 552:     while (iconRects.length > 0)
 553:       {
 554:         clear = true;
 555:         for (int i = 0; i < iconRects.length; i++)
 556:           {
 557:             if (iconRects[i] != null && iconRects[i].intersects(ideal))
 558:               {
 559:                 clear = false;
 560:                 break;
 561:               }
 562:           }
 563:         if (clear)
 564:           return ideal;
 565: 
 566:         startingX += pref.width;
 567:         if (startingX + pref.width > paneBounds.width - insets.right)
 568:           {
 569:             startingX = insets.left;
 570:             startingY -= pref.height;
 571:           }
 572:         ideal.setBounds(startingX, startingY, pref.width, pref.height);
 573:       }
 574: 
 575:     return ideal;
 576:   }
 577: 
 578:   /**
 579:    * This method sets the bounds of the JInternalFrame right before the
 580:    * maximizeFrame call.
 581:    *
 582:    * @param frame The JInternalFrame being maximized.
 583:    * @param rect The normal bounds.
 584:    */
 585:   protected void setPreviousBounds(JInternalFrame frame, Rectangle rect)
 586:   {
 587:     frame.setNormalBounds(rect);
 588:   }
 589: 
 590:   /**
 591:    * This method returns the normal bounds of the JInternalFrame from before
 592:    * the maximize call.
 593:    *
 594:    * @param frame The JInternalFrame that is being restored.
 595:    *
 596:    * @return The previous bounds of the JInternalFrame.
 597:    */
 598:   protected Rectangle getPreviousBounds(JInternalFrame frame)
 599:   {
 600:     return frame.getNormalBounds();
 601:   }
 602: 
 603:   /**
 604:    * This method sets the value to true if the given JInternalFrame has been
 605:    * iconized and the bounds of its DesktopIcon are valid.
 606:    *
 607:    * @param frame The JInternalFrame for the JDesktopIcon.
 608:    * @param value True if the JInternalFrame has been iconized and the bounds
 609:    *        of the JDesktopIcon are valid.
 610:    */
 611:   protected void setWasIcon(JInternalFrame frame, Boolean value)
 612:   {
 613:     frame.setWasIcon(value.booleanValue(), WAS_ICON_ONCE_PROPERTY);
 614:   }
 615: 
 616:   /**
 617:    * This method returns true if the given JInternalFrame has been iconized
 618:    * and the bounds of its DesktopIcon are valid.
 619:    *
 620:    * @param frame The JInternalFrame for the JDesktopIcon.
 621:    *
 622:    * @return True if the given JInternalFrame has been iconized and the bounds
 623:    *         of its DesktopIcon are valid.
 624:    */
 625:   protected boolean wasIcon(JInternalFrame frame)
 626:   {
 627:     return frame.getWasIcon();
 628:   }
 629: }