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:     // Nothing to do here.
  95:   }
  96: 
  97:   /**
  98:    * This method is not normally called since the user will typically add the
  99:    * JInternalFrame to a Container. If this is called, it will try to
 100:    * determine the parent of the JInternalFrame and remove any icon that
 101:    * represents this JInternalFrame and add this JInternalFrame.
 102:    *
 103:    * @param frame The JInternalFrame to open.
 104:    */
 105:   public void openFrame(JInternalFrame frame)
 106:   {
 107:     Container c = frame.getParent();
 108:     if (c == null)
 109:       c = frame.getDesktopIcon().getParent();
 110:     if (c == null)
 111:       return;
 112: 
 113:     c.remove(frame.getDesktopIcon());
 114:     c.add(frame);
 115:     frame.setVisible(true);
 116:   }
 117: 
 118:   /**
 119:    * This method removes the JInternalFrame and JDesktopIcon (if one is
 120:    * present) from their parents.
 121:    *
 122:    * @param frame The JInternalFrame to close.
 123:    */
 124:   public void closeFrame(JInternalFrame frame)
 125:   {
 126:     Container c = frame.getParent();
 127:     if (c != null)
 128:       {
 129:         if (frame.isIcon())
 130:           c.remove(frame.getDesktopIcon());
 131:         else
 132:           c.remove(frame);
 133:         c.repaint();
 134:       }
 135:   }
 136: 
 137:   /**
 138:    * This method resizes the JInternalFrame to match its parent's bounds.
 139:    *
 140:    * @param frame The JInternalFrame to maximize.
 141:    */
 142:   public void maximizeFrame(JInternalFrame frame)
 143:   {
 144:     // Can't maximize from iconified state.
 145:     // It can only return to maximized state, but that would fall under
 146:     // deiconify.
 147:     if (frame.isIcon())
 148:       return;
 149:     frame.setNormalBounds(frame.getBounds());
 150: 
 151:     Container p = frame.getParent();
 152:     if (p != null)
 153:       {
 154:         Rectangle pBounds = p.getBounds();
 155:         Insets insets = p.getInsets();
 156:         pBounds.width -= insets.left + insets.right;
 157:         pBounds.height -= insets.top + insets.bottom;
 158: 
 159:         setBoundsForFrame(frame, 0, 0, pBounds.width, pBounds.height);
 160:       }
 161:     if (p instanceof JDesktopPane)
 162:       ((JDesktopPane) p).setSelectedFrame(frame);
 163:     else
 164:       {
 165:         try
 166:           {
 167:             frame.setSelected(true);
 168:           }
 169:         catch (PropertyVetoException e)
 170:           {
 171:             // Do nothing.
 172:           }
 173:       }
 174:   }
 175: 
 176:   /**
 177:    * This method restores the JInternalFrame's bounds to what they were
 178:    * previous to the setMaximize call.
 179:    *
 180:    * @param frame The JInternalFrame to minimize.
 181:    */
 182:   public void minimizeFrame(JInternalFrame frame)
 183:   {
 184:     Rectangle normalBounds = frame.getNormalBounds();
 185: 
 186:     JDesktopPane p = frame.getDesktopPane();
 187:     if (p != null)
 188:       p.setSelectedFrame(frame);
 189:     else
 190:       {
 191:         try
 192:           {
 193:             frame.setSelected(true);
 194:           }
 195:         catch (PropertyVetoException e)
 196:           {
 197:             // Do nothing.
 198:           }
 199:       }
 200: 
 201:     setBoundsForFrame(frame, normalBounds.x, normalBounds.y,
 202:                       normalBounds.width, normalBounds.height);
 203:   }
 204: 
 205:   /**
 206:    * This method removes the JInternalFrame from its parent and adds its
 207:    * JDesktopIcon representation.
 208:    *
 209:    * @param frame The JInternalFrame to iconify.
 210:    */
 211:   public void iconifyFrame(JInternalFrame frame)
 212:   {
 213:     JDesktopPane p = frame.getDesktopPane();
 214:     JDesktopIcon icon = frame.getDesktopIcon();
 215:     if (p != null && p.getSelectedFrame() == frame)
 216:       p.setSelectedFrame(null);
 217:     else
 218:       {
 219:         try
 220:           {
 221:             frame.setSelected(false);
 222:           }
 223:         catch (PropertyVetoException e)
 224:           {
 225:             // Do nothing if attempt is vetoed.
 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:         Rectangle b = frame.getBounds();
 246:         c.remove(frame);
 247:         c.repaint(b.x, b.y, b.width, b.height);
 248:       }
 249:   }
 250: 
 251:   /**
 252:    * This method removes the JInternalFrame's JDesktopIcon representation and
 253:    * adds the JInternalFrame back to its parent.
 254:    *
 255:    * @param frame The JInternalFrame to deiconify.
 256:    */
 257:   public void deiconifyFrame(JInternalFrame frame)
 258:   {
 259:     JDesktopIcon icon = frame.getDesktopIcon();
 260:     Container c = icon.getParent();
 261: 
 262:     removeIconFor(frame);
 263:     c.add(frame);
 264:     frame.setVisible(true);
 265: 
 266:     if (!frame.isSelected())
 267:       {
 268:         JDesktopPane p = frame.getDesktopPane();
 269:         if (p != null)
 270:           p.setSelectedFrame(frame);
 271:         else
 272:           {
 273:             try
 274:               {
 275:                 frame.setSelected(true);
 276:               }
 277:             catch (PropertyVetoException e)
 278:               {
 279:                 // Do nothing.
 280:               }
 281:           }
 282:       }
 283: 
 284:     c.invalidate();
 285:   }
 286: 
 287:   /**
 288:    * This method activates the JInternalFrame by moving it to the front and
 289:    * selecting it.
 290:    *
 291:    * @param frame The JInternalFrame to activate.
 292:    */
 293:   public void activateFrame(JInternalFrame frame)
 294:   {
 295:     JDesktopPane p = frame.getDesktopPane();
 296:     JInternalFrame active = null;
 297:     if (p != null)
 298:       active = p.getSelectedFrame();
 299:     if (active == null)
 300:       {
 301:         if (p != null)
 302:           {
 303:             p.setSelectedFrame(frame);
 304:           }
 305:       }
 306:     else if (active != frame)
 307:       {
 308:         if (active.isSelected())
 309:           {
 310:             try
 311:               {
 312:                 active.setSelected(false);
 313:               }
 314:             catch (PropertyVetoException ex)
 315:               {
 316:                 // Not allowed.
 317:               }
 318:           }
 319:         if (p != null)
 320:           {
 321:             p.setSelectedFrame(frame);
 322:           }
 323: 
 324:       }
 325:     frame.toFront();
 326:   }
 327: 
 328:   /**
 329:    * This method is called when the JInternalFrame loses focus.
 330:    *
 331:    * @param frame The JInternalFram to deactivate.
 332:    */
 333:   public void deactivateFrame(JInternalFrame frame)
 334:   {
 335:     JDesktopPane p = frame.getDesktopPane();
 336:     if (p != null)
 337:       {
 338:         if (p.getSelectedFrame() == frame)
 339:           p.setSelectedFrame(null);
 340:       }
 341:     else
 342:       {
 343:         try
 344:           {
 345:             frame.setSelected(false);
 346:           }
 347:         catch (PropertyVetoException e)
 348:           {
 349:             // Do nothing if attempt is vetoed.
 350:           }
 351:       }
 352:   }
 353: 
 354:   /**
 355:    * This method is called to indicate that the DesktopManager should prepare
 356:    * to drag the JInternalFrame. Any state information needed to drag the
 357:    * frame will be prepared now.
 358:    *
 359:    * @param component The JComponent to drag, usually a JInternalFrame.
 360:    */
 361:   public void beginDraggingFrame(JComponent component)
 362:   {
 363:     if (component instanceof JDesktopIcon)
 364:       pane = ((JDesktopIcon) component).getInternalFrame().getDesktopPane();
 365:     else
 366:       pane = ((JInternalFrame) component).getDesktopPane();
 367:     if (pane == null)
 368:       return;
 369: 
 370:     dragCache = component.getBounds();
 371: 
 372:     if (! (pane instanceof JDesktopPane))
 373:       currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
 374:     else
 375:       currentDragMode = ((JDesktopPane) pane).getDragMode();
 376:   }
 377: 
 378:   /**
 379:    * This method is called to drag the JInternalFrame to a new location.
 380:    *
 381:    * @param component The JComponent to drag, usually a JInternalFrame.
 382:    *
 383:    * @param newX The new x coordinate.
 384:    * @param newY The new y coordinate.
 385:    */
 386:   public void dragFrame(JComponent component, int newX, int newY)
 387:   {
 388:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 389:       {
 390:         // FIXME: Do outline drag mode painting.
 391:       }
 392:     else
 393:       {
 394:         Rectangle b = component.getBounds();
 395:         if (component instanceof JDesktopIcon)
 396:           component.setBounds(newX, newY, b.width, b.height);
 397:         else
 398:           setBoundsForFrame((JInternalFrame) component, newX, newY, b.width,
 399:                             b.height);
 400:       }
 401:   }
 402: 
 403:   /**
 404:    * This method indicates that the dragging is done. Any state information
 405:    * stored by the DesktopManager can be cleared.
 406:    *
 407:    * @param component The JComponent that has finished dragging.
 408:    */
 409:   public void endDraggingFrame(JComponent component)
 410:   {
 411:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 412:       {
 413:         setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
 414:                           dragCache.width, dragCache.height);
 415:         pane = null;
 416:         dragCache = null;
 417:         component.repaint();
 418:       }
 419:   }
 420: 
 421:   /**
 422:    * This method is called to indicate that the given JComponent will be
 423:    * resized. Any state information necessary to resize the JComponent will
 424:    * be prepared now.
 425:    *
 426:    * @param component The JComponent to resize, usually a JInternalFrame.
 427:    * @param direction The direction to drag in (a SwingConstant).
 428:    */
 429:   public void beginResizingFrame(JComponent component, int direction)
 430:   {
 431:     pane = ((JInternalFrame) component).getDesktopPane();
 432:     if (pane == null)
 433:       return;
 434: 
 435:     dragCache = component.getBounds();
 436:     if (! (pane instanceof JDesktopPane))
 437:       currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
 438:     else
 439:       currentDragMode = ((JDesktopPane) pane).getDragMode();
 440:   }
 441: 
 442:   /**
 443:    * This method resizes the give JComponent.
 444:    *
 445:    * @param component The JComponent to resize.
 446:    * @param newX The new x coordinate.
 447:    * @param newY The new y coordinate.
 448:    * @param newWidth The new width.
 449:    * @param newHeight The new height.
 450:    */
 451:   public void resizeFrame(JComponent component, int newX, int newY,
 452:                           int newWidth, int newHeight)
 453:   {
 454:     dragCache.setBounds(newX, newY, newWidth, newHeight);
 455: 
 456:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 457:       {
 458:         // FIXME: Do outline drag painting.
 459:       }
 460:     else
 461:       setBoundsForFrame(component, dragCache.x, dragCache.y, dragCache.width,
 462:                         dragCache.height);
 463:   }
 464: 
 465:   /**
 466:    * This method is called to indicate that the given JComponent has finished
 467:    * dragging. Any state information stored by the DesktopManager can be
 468:    * cleared.
 469:    *
 470:    * @param component The JComponent that finished resizing.
 471:    */
 472:   public void endResizingFrame(JComponent component)
 473:   {
 474:     if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
 475:       {
 476:         setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
 477:                           dragCache.width, dragCache.height);
 478:         pane = null;
 479:         dragCache = null;
 480:         component.repaint();
 481:       }
 482:   }
 483: 
 484:   /**
 485:    * This method calls setBounds with the given parameters and repaints the
 486:    * JComponent.
 487:    *
 488:    * @param component The JComponent to set bounds for.
 489:    * @param newX The new x coordinate.
 490:    * @param newY The new y coordinate.
 491:    * @param newWidth The new width.
 492:    * @param newHeight The new height.
 493:    */
 494:   public void setBoundsForFrame(JComponent component, int newX, int newY,
 495:                                 int newWidth, int newHeight)
 496:   {
 497:     component.setBounds(newX, newY, newWidth, newHeight);
 498:   }
 499: 
 500:   /**
 501:    * This is a helper method that removes the JDesktopIcon of the given
 502:    * JInternalFrame from the parent.
 503:    *
 504:    * @param frame The JInternalFrame to remove an icon for.
 505:    */
 506:   protected void removeIconFor(JInternalFrame frame)
 507:   {
 508:     JDesktopIcon icon = frame.getDesktopIcon();
 509:     Container c = icon.getParent();
 510:     if (c != null && icon != null)
 511:       {
 512:         Rectangle b = icon.getBounds();
 513:         c.remove(icon);
 514:         c.repaint(b.x, b.y, b.width, b.height);
 515:       }
 516:   }
 517: 
 518:   /**
 519:    * This method is called by iconifyFrame to determine the bounds of the
 520:    * JDesktopIcon for the given JInternalFrame.
 521:    *
 522:    * @param frame The JInternalFrame to find the bounds of its JDesktopIcon
 523:    *        for.
 524:    *
 525:    * @return The bounds of the JDesktopIcon.
 526:    */
 527:   protected Rectangle getBoundsForIconOf(JInternalFrame frame)
 528:   {
 529:     // IconRects has no order to it.
 530:     // The icon _must_ be placed in the first free slot (working from
 531:     // the bottom left corner)
 532:     // The icon also must not be placed where another icon is placed
 533:     // (regardless whether that frame is an icon currently or not)
 534:     JDesktopPane desktopPane = frame.getDesktopPane();
 535: 
 536:     if (desktopPane == null)
 537:       return frame.getDesktopIcon().getBounds();
 538: 
 539:     Rectangle paneBounds = desktopPane.getBounds();
 540:     Insets insets = desktopPane.getInsets();
 541:     Dimension pref = frame.getDesktopIcon().getPreferredSize();
 542: 
 543:     Component[] frames = desktopPane.getComponents();
 544: 
 545:     int count = 0;
 546:     for (int i = 0, j = 0; i < frames.length; i++)
 547:       if (frames[i] instanceof JDesktopIcon
 548:           || frames[i] instanceof JInternalFrame
 549:           && ((JInternalFrame) frames[i]).getWasIcon() && frames[i] != frame)
 550:         count++;
 551:     iconRects = new Rectangle[count];
 552:     for (int i = 0, j = 0; i < frames.length; i++)
 553:       if (frames[i] instanceof JDesktopIcon)
 554:         iconRects[--count] = frames[i].getBounds();
 555:       else if (frames[i] instanceof JInternalFrame
 556:                && ((JInternalFrame) frames[i]).getWasIcon()
 557:                && frames[i] != frame)
 558:         iconRects[--count] = ((JInternalFrame) frames[i])
 559:                                                  .getDesktopIcon().getBounds();
 560: 
 561:     int startingX = insets.left;
 562:     int startingY = paneBounds.height - insets.bottom - pref.height;
 563:     Rectangle ideal = new Rectangle(startingX, startingY, pref.width,
 564:                                     pref.height);
 565:     boolean clear = true;
 566: 
 567:     while (iconRects.length > 0)
 568:       {
 569:         clear = true;
 570:         for (int i = 0; i < iconRects.length; i++)
 571:           {
 572:             if (iconRects[i] != null && iconRects[i].intersects(ideal))
 573:               {
 574:                 clear = false;
 575:                 break;
 576:               }
 577:           }
 578:         if (clear)
 579:           return ideal;
 580: 
 581:         startingX += pref.width;
 582:         if (startingX + pref.width > paneBounds.width - insets.right)
 583:           {
 584:             startingX = insets.left;
 585:             startingY -= pref.height;
 586:           }
 587:         ideal.setBounds(startingX, startingY, pref.width, pref.height);
 588:       }
 589: 
 590:     return ideal;
 591:   }
 592: 
 593:   /**
 594:    * This method sets the bounds of the JInternalFrame right before the
 595:    * maximizeFrame call.
 596:    *
 597:    * @param frame The JInternalFrame being maximized.
 598:    * @param rect The normal bounds.
 599:    */
 600:   protected void setPreviousBounds(JInternalFrame frame, Rectangle rect)
 601:   {
 602:     frame.setNormalBounds(rect);
 603:   }
 604: 
 605:   /**
 606:    * This method returns the normal bounds of the JInternalFrame from before
 607:    * the maximize call.
 608:    *
 609:    * @param frame The JInternalFrame that is being restored.
 610:    *
 611:    * @return The previous bounds of the JInternalFrame.
 612:    */
 613:   protected Rectangle getPreviousBounds(JInternalFrame frame)
 614:   {
 615:     return frame.getNormalBounds();
 616:   }
 617: 
 618:   /**
 619:    * This method sets the value to true if the given JInternalFrame has been
 620:    * iconized and the bounds of its DesktopIcon are valid.
 621:    *
 622:    * @param frame The JInternalFrame for the JDesktopIcon.
 623:    * @param value True if the JInternalFrame has been iconized and the bounds
 624:    *        of the JDesktopIcon are valid.
 625:    */
 626:   protected void setWasIcon(JInternalFrame frame, Boolean value)
 627:   {
 628:     frame.setWasIcon(value.booleanValue(), WAS_ICON_ONCE_PROPERTY);
 629:   }
 630: 
 631:   /**
 632:    * This method returns true if the given JInternalFrame has been iconized
 633:    * and the bounds of its DesktopIcon are valid.
 634:    *
 635:    * @param frame The JInternalFrame for the JDesktopIcon.
 636:    *
 637:    * @return True if the given JInternalFrame has been iconized and the bounds
 638:    *         of its DesktopIcon are valid.
 639:    */
 640:   protected boolean wasIcon(JInternalFrame frame)
 641:   {
 642:     return frame.getWasIcon();
 643:   }
 644: }