Source for javax.swing.plaf.metal.MetalRootPaneUI

   1: /* MetalRootPaneUI.java
   2:    Copyright (C) 2005, 2006 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.plaf.metal;
  40: 
  41: import java.awt.Color;
  42: import java.awt.Component;
  43: import java.awt.Container;
  44: import java.awt.Dimension;
  45: import java.awt.Frame;
  46: import java.awt.Graphics;
  47: import java.awt.Insets;
  48: import java.awt.LayoutManager;
  49: import java.awt.LayoutManager2;
  50: import java.awt.Point;
  51: import java.awt.Rectangle;
  52: import java.awt.Window;
  53: import java.awt.event.ActionEvent;
  54: import java.awt.event.MouseEvent;
  55: import java.beans.PropertyChangeEvent;
  56: 
  57: import javax.swing.AbstractAction;
  58: import javax.swing.Action;
  59: import javax.swing.Icon;
  60: import javax.swing.JButton;
  61: import javax.swing.JComponent;
  62: import javax.swing.JDialog;
  63: import javax.swing.JFrame;
  64: import javax.swing.JLabel;
  65: import javax.swing.JLayeredPane;
  66: import javax.swing.JMenu;
  67: import javax.swing.JMenuBar;
  68: import javax.swing.JRootPane;
  69: import javax.swing.SwingConstants;
  70: import javax.swing.SwingUtilities;
  71: import javax.swing.UIManager;
  72: import javax.swing.border.AbstractBorder;
  73: import javax.swing.event.MouseInputAdapter;
  74: import javax.swing.plaf.ComponentUI;
  75: import javax.swing.plaf.basic.BasicRootPaneUI;
  76: 
  77: /**
  78:  * A UI delegate for the {@link JRootPane} component. This implementation
  79:  * supports the JRootPane <code>windowDecorationStyle</code> property.
  80:  *
  81:  * @author Roman Kennke (kennke@aicas.com)
  82:  *
  83:  * @since 1.4
  84:  */
  85: public class MetalRootPaneUI
  86:   extends BasicRootPaneUI
  87: {
  88: 
  89:   /**
  90:    * The border that is used on JRootPane when the windowDecorationStyle
  91:    * property of the JRootPane is set to a different value than NONE.
  92:    *
  93:    * @author Roman Kennke (kennke@aicas.com)
  94:    */
  95:   private static class MetalFrameBorder
  96:     extends AbstractBorder
  97:   {
  98:     /**
  99:      * Returns the border insets.
 100:      *
 101:      * @param c the component
 102:      * @param newInsets the insets to be filled with the return value, may be
 103:      *        <code>null</code> in which case a new object is created
 104:      *
 105:      * @return the border insets
 106:      */
 107:     public Insets getBorderInsets(Component c, Insets newInsets)
 108:     {
 109:       if (newInsets == null)
 110:         newInsets = new Insets(5, 5, 5, 5);
 111:       else
 112:         {
 113:           newInsets.top = 5;
 114:           newInsets.left = 5;
 115:           newInsets.bottom = 5;
 116:           newInsets.right = 5;
 117:         }
 118:       return newInsets;
 119:     }
 120: 
 121:     /**
 122:      * Returns the border insets.
 123:      *
 124:      * @param c the component
 125:      *
 126:      * @return the border insets
 127:      */
 128:     public Insets getBorderInsets(Component c)
 129:     {
 130:       return getBorderInsets(c, null);
 131:     }
 132: 
 133:     /**
 134:      * Paints the border for the specified component.
 135:      *
 136:      * @param c  the component
 137:      * @param g  the graphics device
 138:      * @param x  the x-coordinate
 139:      * @param y  the y-coordinate
 140:      * @param w  the width
 141:      * @param h  the height
 142:      */
 143:     public void paintBorder(Component c, Graphics g, int x, int y, int w,
 144:                             int h)
 145:     {
 146:       JRootPane f = (JRootPane) c;
 147:       Window frame = SwingUtilities.getWindowAncestor(f);
 148:       if (frame.isActive())
 149:         g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
 150:       else
 151:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 152: 
 153:       // Fill the border background.
 154:       g.fillRect(x, y, w, 5);
 155:       g.fillRect(x, y, 5, h);
 156:       g.fillRect(x + w - 5, y, 5, h);
 157:       g.fillRect(x, y + h - 5, w, 5);
 158: 
 159:       // Draw a dot in each corner.
 160:       g.setColor(MetalLookAndFeel.getControl());
 161:       g.fillRect(x, y, 1, 1);
 162:       g.fillRect(x + w - 1, y, 1, 1);
 163:       g.fillRect(x + w - 1, y + h - 1, 1, 1);
 164:       g.fillRect(x, y + h - 1, 1, 1);
 165: 
 166:       // Draw the lines.
 167:       g.setColor(MetalLookAndFeel.getBlack());
 168:       g.drawLine(x + 14, y + 2, x + w - 15, y + 2);
 169:       g.drawLine(x + 14, y + h - 3, x + w - 15, y + h - 3);
 170:       g.drawLine(x + 2, y + 14, x + 2, y + h - 15);
 171:       g.drawLine(x + w - 3, y + 14, x + w - 3, y + h - 15);
 172: 
 173:       // Draw the line highlights.
 174:       if (frame.isActive())
 175:         g.setColor(MetalLookAndFeel.getPrimaryControlShadow());
 176:       else
 177:         g.setColor(MetalLookAndFeel.getControlShadow());
 178:       g.drawLine(x + 15, y + 3, x + w - 14, y + 3);
 179:       g.drawLine(x + 15, y + h - 2, x + w - 14, y + h - 2);
 180:       g.drawLine(x + 3, y + 15, x + 3, y + h - 14);
 181:       g.drawLine(x + w - 2, y + 15, x + w - 2, y + h - 14);
 182:     }
 183:   }
 184: 
 185:   /**
 186:    * The component that renders the title bar for frames. This duplicates
 187:    * most of {@link MetalInternalFrameTitlePane}. It is not reasonably possible
 188:    * to reuse that class because that is bound to the JInternalFrame and we
 189:    * need to handle JFrames/JRootPanes here.
 190:    *
 191:    * @author Roman Kennke (kennke@aicas.com)
 192:    */
 193:   private static class MetalTitlePane extends JComponent
 194:   {
 195: 
 196:     /**
 197:      * Handles dragging of the title pane and moves the window accordingly.
 198:      */
 199:     private class MouseHandler
 200:       extends MouseInputAdapter
 201:     {
 202:       /**
 203:        * The point where the dragging started.
 204:        */
 205:       Point lastDragLocation;
 206: 
 207:       /**
 208:        * Receives notification when the mouse gets pressed on the title pane.
 209:        * This updates the lastDragLocation.
 210:        *
 211:        * @param ev the mouse event
 212:        */
 213:       public void mousePressed(MouseEvent ev)
 214:       {
 215:         lastDragLocation = ev.getPoint();
 216:       }
 217: 
 218:       /**
 219:        * Receives notification when the mouse is dragged on the title pane.
 220:        * This will move the nearest window accordingly.
 221:        *
 222:        * @param ev the mouse event
 223:        */
 224:       public void mouseDragged(MouseEvent ev)
 225:       {
 226:         Point dragLocation = ev.getPoint();
 227:         int deltaX = dragLocation.x - lastDragLocation.x;
 228:         int deltaY = dragLocation.y - lastDragLocation.y;
 229:         Window window = SwingUtilities.getWindowAncestor(rootPane);
 230:         Point loc = window.getLocation();
 231:         window.setLocation(loc.x + deltaX, loc.y + deltaY);
 232:         // Note that we do not update the lastDragLocation. This is because
 233:         // we move the underlying window while dragging the component, which
 234:         // results in having the same lastDragLocation under the mouse while
 235:         // dragging.
 236:       }
 237:     }
 238: 
 239:     /**
 240:      * The Action responsible for closing the JInternalFrame.
 241:      */
 242:     private class CloseAction extends AbstractAction
 243:     {
 244:       /**
 245:        * Creates a new action.
 246:        */
 247:       public CloseAction()
 248:       {
 249:         super("Close");
 250:       }
 251: 
 252:       /**
 253:        * This method is called when something closes the frame.
 254:        *
 255:        * @param e the ActionEvent
 256:        */
 257:       public void actionPerformed(ActionEvent e)
 258:       {
 259:         Window frame = SwingUtilities.getWindowAncestor(rootPane);
 260:         if (frame instanceof JFrame)
 261:           {
 262:             JFrame jframe = (JFrame) frame;
 263:             switch (jframe.getDefaultCloseOperation())
 264:             {
 265:               case JFrame.EXIT_ON_CLOSE:
 266:                 jframe.setVisible(false);
 267:                 jframe.dispose();
 268:                 System.exit(0);
 269:                 break;
 270:               case JFrame.DISPOSE_ON_CLOSE:
 271:                 jframe.setVisible(false);
 272:                 jframe.dispose();
 273:                 break;
 274:               case JFrame.HIDE_ON_CLOSE:
 275:                 jframe.setVisible(false);
 276:                 break;
 277:               case JFrame.DO_NOTHING_ON_CLOSE:
 278:               default:
 279:                   break;
 280:             }
 281:           }
 282:         else if (frame instanceof JDialog)
 283:           {
 284:             JDialog jdialog = (JDialog) frame;
 285:             switch (jdialog.getDefaultCloseOperation())
 286:             {
 287:               case JFrame.DISPOSE_ON_CLOSE:
 288:                 jdialog.setVisible(false);
 289:                 jdialog.dispose();
 290:                 break;
 291:               case JFrame.HIDE_ON_CLOSE:
 292:                 jdialog.setVisible(false);
 293:                 break;
 294:               case JFrame.DO_NOTHING_ON_CLOSE:
 295:               default:
 296:                   break;
 297:             }
 298:           }
 299:       }
 300:     }
 301: 
 302:     /**
 303:      * This action is performed when the iconify button is pressed.
 304:      */
 305:     private class IconifyAction
 306:       extends AbstractAction
 307:     {
 308: 
 309:       public void actionPerformed(ActionEvent event)
 310:       {
 311:         Window w = SwingUtilities.getWindowAncestor(rootPane);
 312:         if (w instanceof Frame)
 313:           {
 314:             Frame f = (Frame) w;
 315:             int state = f.getExtendedState();
 316:             f.setExtendedState(Frame.ICONIFIED);
 317:           }
 318:       }
 319: 
 320:     }
 321: 
 322:     /**
 323:      * This action is performed when the maximize button is pressed.
 324:      */
 325:     private class MaximizeAction
 326:       extends AbstractAction
 327:     {
 328: 
 329:       public void actionPerformed(ActionEvent event)
 330:       {
 331:         Window w = SwingUtilities.getWindowAncestor(rootPane);
 332:         if (w instanceof Frame)
 333:           {
 334:             Frame f = (Frame) w;
 335:             int state = f.getExtendedState();
 336:             f.setExtendedState(Frame.MAXIMIZED_BOTH);
 337:           }
 338:       }
 339:     }
 340: 
 341:     /**
 342:      * This helper class is used to create the minimize, maximize and close
 343:      * buttons in the top right corner of the Title Pane. These buttons are
 344:      * special since they cannot be given focus and have no border.
 345:      */
 346:     private class PaneButton extends JButton
 347:     {
 348:       /**
 349:        * Creates a new PaneButton object with the given Action.
 350:        *
 351:        * @param a The Action that the button uses.
 352:        */
 353:       public PaneButton(Action a)
 354:       {
 355:         super(a);
 356:         setMargin(new Insets(0, 0, 0, 0));
 357:       }
 358: 
 359:       /**
 360:        * This method returns true if the Component can be focused.
 361:        *
 362:        * @return false.
 363:        */
 364:       public boolean isFocusable()
 365:       {
 366:         // These buttons cannot be given focus.
 367:         return false;
 368:       }
 369: 
 370:     }
 371: 
 372:     /**
 373:      * The layout for the JRootPane when the <code>windowDecorationStyle</code>
 374:      * property is set. In addition to the usual JRootPane.RootLayout behaviour
 375:      * this lays out the titlePane.
 376:      *
 377:      * @author Roman Kennke (kennke@aicas.com)
 378:      */
 379:     private class MetalTitlePaneLayout implements LayoutManager
 380:     {
 381:       /**
 382:        * Creates a new <code>TitlePaneLayout</code> object.
 383:        */
 384:       public MetalTitlePaneLayout()
 385:       {
 386:         // Do nothing.
 387:       }
 388: 
 389:       /**
 390:        * Adds a Component to the Container.
 391:        *
 392:        * @param name The name to reference the added Component by.
 393:        * @param c The Component to add.
 394:        */
 395:       public void addLayoutComponent(String name, Component c)
 396:       {
 397:         // Do nothing.
 398:       }
 399: 
 400:       /**
 401:        * This method is called to lay out the children of the Title Pane.
 402:        *
 403:        * @param c The Container to lay out.
 404:        */
 405:       public void layoutContainer(Container c)
 406:       {
 407: 
 408:         Dimension size = c.getSize();
 409:         Insets insets = c.getInsets();
 410:         int width = size.width - insets.left - insets.right;
 411:         int height = size.height - insets.top - insets.bottom;
 412: 
 413:         int loc = width - insets.right - 1;
 414:         int top = insets.top + 2;
 415:         int buttonHeight = height - 4;
 416:         if (closeButton.isVisible())
 417:           {
 418:             int buttonWidth = closeIcon.getIconWidth();
 419:             loc -= buttonWidth + 2;
 420:             closeButton.setBounds(loc, top, buttonWidth, buttonHeight);
 421:             loc -= 6;
 422:           }
 423: 
 424:         if (maxButton.isVisible())
 425:           {
 426:             int buttonWidth = maxIcon.getIconWidth();
 427:             loc -= buttonWidth + 4;
 428:             maxButton.setBounds(loc, top, buttonWidth, buttonHeight);
 429:           }
 430: 
 431:         if (iconButton.isVisible())
 432:           {
 433:             int buttonWidth = minIcon.getIconWidth();
 434:             loc -= buttonWidth + 4;
 435:             iconButton.setBounds(loc, top, buttonWidth, buttonHeight);
 436:             loc -= 2;
 437:           }
 438: 
 439:         Dimension titlePreferredSize = title.getPreferredSize();
 440:         title.setBounds(insets.left + 5, insets.top,
 441:                 Math.min(titlePreferredSize.width, loc - insets.left - 10),
 442:                 height);
 443: 
 444:       }
 445: 
 446:       /**
 447:        * This method returns the minimum size of the given Container given the
 448:        * children that it has.
 449:        *
 450:        * @param c The Container to get a minimum size for.
 451:        *
 452:        * @return The minimum size of the Container.
 453:        */
 454:       public Dimension minimumLayoutSize(Container c)
 455:       {
 456:         return preferredLayoutSize(c);
 457:       }
 458: 
 459:       /**
 460:        * Returns the preferred size of the given Container taking
 461:        * into account the children that it has.
 462:        *
 463:        * @param c The Container to lay out.
 464:        *
 465:        * @return The preferred size of the Container.
 466:        */
 467:       public Dimension preferredLayoutSize(Container c)
 468:       {
 469:         return new Dimension(22, 22);
 470:       }
 471: 
 472:       /**
 473:        * Removes a Component from the Container.
 474:        *
 475:        * @param c The Component to remove.
 476:        */
 477:       public void removeLayoutComponent(Component c)
 478:       {
 479:         // Nothing to do here.
 480:       }
 481:     }
 482: 
 483:     JRootPane rootPane;
 484: 
 485:     /** The button that closes the JInternalFrame. */
 486:     JButton closeButton;
 487: 
 488:     /** The button that iconifies the JInternalFrame. */
 489:     JButton iconButton;
 490: 
 491:     /** The button that maximizes the JInternalFrame. */
 492:     JButton maxButton;
 493: 
 494:     Icon minIcon;
 495: 
 496:     /** The icon displayed in the maximize button. */
 497:     Icon maxIcon;
 498: 
 499:     /** The icon displayed in the iconify button. */
 500:     private Icon iconIcon;
 501: 
 502:     /** The icon displayed in the close button. */
 503:     Icon closeIcon;
 504: 
 505:     /**
 506:      * The background color of the TitlePane when the JInternalFrame is not
 507:      * selected.
 508:      */
 509:     private Color notSelectedTitleColor;
 510: 
 511:     /**
 512:      * The background color of the TitlePane when the JInternalFrame is
 513:      * selected.
 514:      */
 515:     private Color selectedTitleColor;
 516: 
 517:     /**
 518:      * The label used to display the title. This label is not added to the
 519:      * TitlePane.
 520:      */
 521:     JLabel title;
 522: 
 523:     /** The action associated with closing the JInternalFrame. */
 524:     private Action closeAction;
 525: 
 526:     /** The action associated with iconifying the JInternalFrame. */
 527:     private Action iconifyAction;
 528: 
 529:     /** The action associated with maximizing the JInternalFrame. */
 530:     private Action maximizeAction;
 531: 
 532:     /** The JMenuBar that is located at the top left of the Title Pane. */
 533:     private JMenuBar menuBar;
 534: 
 535:     /** The JMenu inside the menuBar. */
 536:     protected JMenu windowMenu;
 537: 
 538:     MetalTitlePane(JRootPane rp)
 539:     {
 540:       rootPane = rp;
 541:       setLayout(createLayout());
 542:       title = new JLabel();
 543:       title.setHorizontalAlignment(SwingConstants.LEFT);
 544:       title.setHorizontalTextPosition(SwingConstants.LEFT);
 545:       title.setOpaque(false);
 546:       installTitlePane();
 547:     }
 548: 
 549:     protected LayoutManager createLayout()
 550:     {
 551:       return new MetalTitlePaneLayout();
 552:     }
 553: 
 554:     /**
 555:      * This method installs the TitlePane onto the JInternalFrameTitlePane. It
 556:      * also creates any children components that need to be created and adds
 557:      * listeners to the appropriate components.
 558:      */
 559:     protected void installTitlePane()
 560:     {
 561:       installDefaults();
 562:       installListeners();
 563:       createActions();
 564:       assembleSystemMenu();
 565:       createButtons();
 566:       setButtonIcons();
 567:       addSubComponents();
 568:       enableActions();
 569:     }
 570: 
 571:     private void enableActions()
 572:     {
 573:       // TODO: Implement this.
 574:     }
 575: 
 576:     private void addSubComponents()
 577:     {
 578:       add(menuBar);
 579:       add(closeButton);
 580:       add(iconButton);
 581:       add(maxButton);
 582:     }
 583: 
 584:     private void installListeners()
 585:     {
 586:       MouseInputAdapter mouseHandler = new MouseHandler();
 587:       addMouseListener(mouseHandler);
 588:       addMouseMotionListener(mouseHandler);
 589:     }
 590: 
 591:     private void createActions()
 592:     {
 593:       closeAction = new CloseAction();
 594:       iconifyAction = new IconifyAction();
 595:       maximizeAction = new MaximizeAction();
 596:     }
 597: 
 598:     private void assembleSystemMenu()
 599:     {
 600:       menuBar = createSystemMenuBar();
 601:       windowMenu = createSystemMenu();
 602:       menuBar.add(windowMenu);
 603:       addSystemMenuItems(windowMenu);
 604:       enableActions();
 605:     }
 606: 
 607:     protected JMenuBar createSystemMenuBar()
 608:     {
 609:       if (menuBar == null)
 610:         menuBar = new JMenuBar();
 611:       menuBar.removeAll();
 612:       return menuBar;
 613:     }
 614: 
 615:     protected JMenu createSystemMenu()
 616:     {
 617:       if (windowMenu == null)
 618:         windowMenu = new JMenu();
 619:       windowMenu.removeAll();
 620:       return windowMenu;
 621:     }
 622: 
 623:     private void addSystemMenuItems(JMenu menu)
 624:     {
 625:       // TODO: Implement this.
 626:     }
 627: 
 628:     protected void createButtons()
 629:     {
 630:       closeButton = new PaneButton(closeAction);
 631:       closeButton.setText(null);
 632:       iconButton = new PaneButton(iconifyAction);
 633:       iconButton.setText(null);
 634:       maxButton = new PaneButton(maximizeAction);
 635:       maxButton.setText(null);
 636:       closeButton.setBorderPainted(false);
 637:       closeButton.setContentAreaFilled(false);
 638:       iconButton.setBorderPainted(false);
 639:       iconButton.setContentAreaFilled(false);
 640:       maxButton.setBorderPainted(false);
 641:       maxButton.setContentAreaFilled(false);
 642:     }
 643: 
 644:     protected void setButtonIcons()
 645:     {
 646:       if (closeIcon != null && closeButton != null)
 647:         closeButton.setIcon(closeIcon);
 648:       if (iconIcon != null && iconButton != null)
 649:         iconButton.setIcon(iconIcon);
 650:       if (maxIcon != null && maxButton != null)
 651:         maxButton.setIcon(maxIcon);
 652:     }
 653: 
 654:     /**
 655:      * Paints a representation of the current state of the internal frame.
 656:      *
 657:      * @param g  the graphics device.
 658:      */
 659:     public void paintComponent(Graphics g)
 660:     {
 661:       Window frame = SwingUtilities.getWindowAncestor(rootPane);
 662:       Color savedColor = g.getColor();
 663:       paintTitleBackground(g);
 664:       paintChildren(g);
 665:       Dimension d = getSize();
 666:       if (frame.isActive())
 667:         g.setColor(MetalLookAndFeel.getPrimaryControlDarkShadow());
 668:       else
 669:         g.setColor(MetalLookAndFeel.getControlDarkShadow());
 670: 
 671:       // put a dot in each of the top corners
 672:       g.drawLine(0, 0, 0, 0);
 673:       g.drawLine(d.width - 1, 0, d.width - 1, 0);
 674: 
 675:       g.drawLine(0, d.height - 1, d.width - 1, d.height - 1);
 676: 
 677:       // draw the metal pattern
 678:       if (UIManager.get("InternalFrame.activeTitleGradient") != null
 679:           && frame.isActive())
 680:         {
 681:           MetalUtils.paintGradient(g, 0, 0, getWidth(), getHeight(),
 682:                                    SwingConstants.VERTICAL,
 683:           "InternalFrame.activeTitleGradient");
 684:         }
 685: 
 686:       Rectangle b = title.getBounds();
 687:       int startX = b.x + b.width + 5;
 688:       int endX = startX;
 689:       if (iconButton.isVisible())
 690:         endX = Math.max(iconButton.getX(), endX);
 691:       else if (maxButton.isVisible())
 692:         endX = Math.max(maxButton.getX(), endX);
 693:       else if (closeButton.isVisible())
 694:         endX = Math.max(closeButton.getX(), endX);
 695:       endX -= 7;
 696:       if (endX > startX)
 697:         MetalUtils.fillMetalPattern(this, g, startX, 3, endX - startX, getHeight() - 6, Color.white, Color.gray);
 698:       g.setColor(savedColor);
 699:     }
 700: 
 701:     /**
 702:      * This method paints the TitlePane's background.
 703:      *
 704:      * @param g The Graphics object to paint with.
 705:      */
 706:     protected void paintTitleBackground(Graphics g)
 707:     {
 708:       Window frame = SwingUtilities.getWindowAncestor(rootPane);
 709: 
 710:       if (!isOpaque())
 711:         return;
 712: 
 713:       Color saved = g.getColor();
 714:       Dimension dims = getSize();
 715: 
 716:       Color bg = getBackground();
 717:       if (frame.isActive())
 718:         bg = selectedTitleColor;
 719:       else
 720:         bg = notSelectedTitleColor;
 721:       g.setColor(bg);
 722:       g.fillRect(0, 0, dims.width, dims.height);
 723:       g.setColor(saved);
 724:     }
 725: 
 726:     /**
 727:      * This method installs the defaults determined by the look and feel.
 728:      */
 729:     private void installDefaults()
 730:     {
 731:       title.setFont(UIManager.getFont("InternalFrame.titleFont"));
 732:       selectedTitleColor = UIManager.getColor("InternalFrame.activeTitleBackground");
 733:       notSelectedTitleColor = UIManager.getColor("InternalFrame.inactiveTitleBackground");
 734:       closeIcon = UIManager.getIcon("InternalFrame.closeIcon");
 735:       iconIcon = UIManager.getIcon("InternalFrame.iconifyIcon");
 736:       maxIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
 737:       minIcon = MetalIconFactory.getInternalFrameAltMaximizeIcon(16);
 738:       Frame frame = (Frame) SwingUtilities.getWindowAncestor(rootPane);
 739:       title = new JLabel(frame.getTitle(),
 740:               MetalIconFactory.getInternalFrameDefaultMenuIcon(),
 741:               SwingConstants.LEFT);
 742:     }
 743:   }
 744: 
 745:   private static class MetalRootLayout
 746:     implements LayoutManager2
 747:   {
 748: 
 749:     /**
 750:      * The cached layout info for the glass pane.
 751:      */
 752:     private Rectangle glassPaneBounds;
 753: 
 754:     /**
 755:      * The cached layout info for the layered pane.
 756:      */
 757:     private Rectangle layeredPaneBounds;
 758: 
 759:     /**
 760:      * The cached layout info for the content pane.
 761:      */
 762:     private Rectangle contentPaneBounds;
 763: 
 764:     /**
 765:      * The cached layout info for the menu bar.
 766:      */
 767:     private Rectangle menuBarBounds;
 768: 
 769:     /**
 770:      * The cached layout info for the title pane.
 771:      */
 772:     private Rectangle titlePaneBounds;
 773: 
 774:     /**
 775:      * The cached preferred size.
 776:      */
 777:     private Dimension prefSize;
 778: 
 779:     /**
 780:      * The title pane for l&f decorated frames.
 781:      */
 782:     private MetalTitlePane titlePane;
 783: 
 784:     /**
 785:      * Creates a new MetalRootLayout.
 786:      *
 787:      * @param tp the title pane
 788:      */
 789:     MetalRootLayout(MetalTitlePane tp)
 790:     {
 791:       titlePane = tp;
 792:     }
 793: 
 794:     public void addLayoutComponent(Component component, Object constraints)
 795:     {
 796:       // Nothing to do here.
 797:     }
 798: 
 799:     public Dimension maximumLayoutSize(Container target)
 800:     {
 801:       return preferredLayoutSize(target);
 802:     }
 803: 
 804:     public float getLayoutAlignmentX(Container target)
 805:     {
 806:       return 0.0F;
 807:     }
 808: 
 809:     public float getLayoutAlignmentY(Container target)
 810:     {
 811:       return 0.0F;
 812:     }
 813: 
 814:     public void invalidateLayout(Container target)
 815:     {
 816:       synchronized (this)
 817:       {
 818:         glassPaneBounds = null;
 819:         layeredPaneBounds = null;
 820:         contentPaneBounds = null;
 821:         menuBarBounds = null;
 822:         titlePaneBounds = null;
 823:         prefSize = null;
 824:       }
 825:     }
 826: 
 827:     public void addLayoutComponent(String name, Component component)
 828:     {
 829:       // Nothing to do here.
 830:     }
 831: 
 832:     public void removeLayoutComponent(Component component)
 833:     {
 834:       // TODO Auto-generated method stub
 835: 
 836:     }
 837: 
 838:     public Dimension preferredLayoutSize(Container parent)
 839:     {
 840:       JRootPane rp = (JRootPane) parent;
 841:       JLayeredPane layeredPane = rp.getLayeredPane();
 842:       Component contentPane = rp.getContentPane();
 843:       Component menuBar = rp.getJMenuBar();
 844: 
 845:       // We must synchronize here, otherwise we cannot guarantee that the
 846:       // prefSize is still non-null when returning.
 847:       synchronized (this)
 848:         {
 849:           if (prefSize == null)
 850:             {
 851:               Insets i = parent.getInsets();
 852:               prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
 853:               Dimension contentPrefSize = contentPane.getPreferredSize();
 854:               prefSize.width += contentPrefSize.width;
 855:               prefSize.height += contentPrefSize.height
 856:                                  + titlePane.getPreferredSize().height;
 857:               if (menuBar != null)
 858:                 {
 859:                   Dimension menuBarSize = menuBar.getPreferredSize();
 860:                   if (menuBarSize.width > contentPrefSize.width)
 861:                     prefSize.width += menuBarSize.width - contentPrefSize.width;
 862:                   prefSize.height += menuBarSize.height;
 863:                 }
 864:             }
 865:           // Return a copy here so the cached value won't get trashed by some
 866:           // other component.
 867:           return new Dimension(prefSize);
 868:       }
 869:     }
 870: 
 871:     public Dimension minimumLayoutSize(Container parent)
 872:     {
 873:       return preferredLayoutSize(parent);
 874:     }
 875: 
 876:     public void layoutContainer(Container parent)
 877:     {
 878:       JRootPane rp = (JRootPane) parent;
 879:       JLayeredPane layeredPane = rp.getLayeredPane();
 880:       Component contentPane = rp.getContentPane();
 881:       Component menuBar = rp.getJMenuBar();
 882:       Component glassPane = rp.getGlassPane();
 883: 
 884:       if (glassPaneBounds == null || layeredPaneBounds == null
 885:           || contentPaneBounds == null || menuBarBounds == null)
 886:         {
 887:           Insets i = rp.getInsets();
 888:           int containerWidth = parent.getBounds().width - i.left - i.right;
 889:           int containerHeight = parent.getBounds().height - i.top - i.bottom;
 890: 
 891:           // 1. The glassPane fills entire viewable region (bounds - insets).
 892:           // 2. The layeredPane filles entire viewable region.
 893:           // 3. The titlePane is placed at the upper edge of the layeredPane.
 894:           // 4. The menuBar is positioned at the upper edge of layeredPane.
 895:           // 5. The contentPane fills viewable region minus menuBar minus
 896:           //    titlePane, if present.
 897: 
 898:           // +-------------------------------+
 899:           // |  JLayeredPane                 |
 900:           // |  +--------------------------+ |
 901:           // |  | titlePane                + |
 902:           // |  +--------------------------+ |
 903:           // |  +--------------------------+ |
 904:           // |  | menuBar                  | |
 905:           // |  +--------------------------+ |
 906:           // |  +--------------------------+ |
 907:           // |  |contentPane               | |
 908:           // |  |                          | |
 909:           // |  |                          | |
 910:           // |  |                          | |
 911:           // |  +--------------------------+ |
 912:           // +-------------------------------+
 913: 
 914:           // Setup titlePaneBounds.
 915:           if (titlePaneBounds == null)
 916:             titlePaneBounds = new Rectangle();
 917:           titlePaneBounds.width = containerWidth;
 918:           titlePaneBounds.height = titlePane.getPreferredSize().height;
 919: 
 920:           // Setup menuBarBounds.
 921:           if (menuBarBounds == null)
 922:             menuBarBounds = new Rectangle();
 923:           menuBarBounds.setBounds(0,
 924:                                   titlePaneBounds.y + titlePaneBounds.height,
 925:                                   containerWidth, 0);
 926:           if (menuBar != null)
 927:             {
 928:               Dimension menuBarSize = menuBar.getPreferredSize();
 929:               if (menuBarSize.height > containerHeight)
 930:                 menuBarBounds.height = containerHeight;
 931:               else
 932:                 menuBarBounds.height = menuBarSize.height;
 933:             }
 934: 
 935:           // Setup contentPaneBounds.
 936:           if (contentPaneBounds == null)
 937:             contentPaneBounds = new Rectangle();
 938:           contentPaneBounds.setBounds(0,
 939:                                       menuBarBounds.y + menuBarBounds.height,
 940:                                       containerWidth,
 941:                                       containerHeight - menuBarBounds.y
 942:                                       - menuBarBounds.height);
 943:           glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
 944:           layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
 945:         }
 946: 
 947:       // Layout components.
 948:       glassPane.setBounds(glassPaneBounds);
 949:       layeredPane.setBounds(layeredPaneBounds);
 950:       if (menuBar != null)
 951:         menuBar.setBounds(menuBarBounds);
 952:       contentPane.setBounds(contentPaneBounds);
 953:       titlePane.setBounds(titlePaneBounds);
 954:     }
 955: 
 956:   }
 957: 
 958:   /**
 959:    * The shared UI instance for MetalRootPaneUIs.
 960:    */
 961:   private static MetalRootPaneUI instance;
 962: 
 963:   /**
 964:    * Constructs a shared instance of <code>MetalRootPaneUI</code>.
 965:    */
 966:   public MetalRootPaneUI()
 967:   {
 968:     super();
 969:   }
 970: 
 971:   /**
 972:    * Returns a shared instance of <code>MetalRootPaneUI</code>.
 973:    *
 974:    * @param component the component for which we return an UI instance
 975:    *
 976:    * @return A shared instance of <code>MetalRootPaneUI</code>.
 977:    */
 978:   public static ComponentUI createUI(JComponent component)
 979:   {
 980:     if (instance == null)
 981:       instance = new MetalRootPaneUI();
 982:     return instance;
 983:   }
 984: 
 985:   /**
 986:    * Installs this UI to the root pane. If the
 987:    * <code>windowDecorationsStyle</code> property is set on the root pane,
 988:    * the Metal window decorations are installed on the root pane.
 989:    *
 990:    * @param c
 991:    */
 992:   public void installUI(JComponent c)
 993:   {
 994:     super.installUI(c);
 995:     JRootPane rp = (JRootPane) c;
 996:     if (rp.getWindowDecorationStyle() != JRootPane.NONE)
 997:       installWindowDecorations(rp);
 998:   }
 999: 
1000:   /**
1001:    * Uninstalls the UI from the root pane. This performs the superclass
1002:    * behaviour and uninstalls the window decorations that have possibly been
1003:    * installed by {@link #installUI}.
1004:    *
1005:    * @param c the root pane
1006:    */
1007:   public void uninstallUI(JComponent c)
1008:   {
1009:     JRootPane rp = (JRootPane) c;
1010:     if (rp.getWindowDecorationStyle() != JRootPane.NONE)
1011:       uninstallWindowDecorations(rp);
1012:     super.uninstallUI(c);
1013:   }
1014: 
1015:   /**
1016:    * Receives notification if any of the JRootPane's property changes. In
1017:    * particular this catches changes to the <code>windowDecorationStyle</code>
1018:    * property and installs the window decorations accordingly.
1019:    *
1020:    * @param ev the property change event
1021:    */
1022:   public void propertyChange(PropertyChangeEvent ev)
1023:   {
1024:     super.propertyChange(ev);
1025:     String propertyName = ev.getPropertyName();
1026:     if (propertyName.equals("windowDecorationStyle"))
1027:       {
1028:         JRootPane rp = (JRootPane) ev.getSource();
1029:         if (rp.getWindowDecorationStyle() != JRootPane.NONE)
1030:           installWindowDecorations(rp);
1031:         else
1032:           uninstallWindowDecorations(rp);
1033:       }
1034:   }
1035: 
1036:   /**
1037:    * Installs the window decorations to the root pane. This sets up a border,
1038:    * a title pane and a layout manager that can layout the root pane with that
1039:    * title pane.
1040:    *
1041:    * @param rp the root pane.
1042:    */
1043:   private void installWindowDecorations(JRootPane rp)
1044:   {
1045:     rp.setBorder(new MetalFrameBorder());
1046:     MetalTitlePane titlePane = new MetalTitlePane(rp);
1047:     rp.setLayout(new MetalRootLayout(titlePane));
1048:     // We should have a contentPane already.
1049:     assert rp.getLayeredPane().getComponentCount() > 0
1050:            : "We should have a contentPane already";
1051: 
1052:     rp.getLayeredPane().add(titlePane,
1053:                             JLayeredPane.FRAME_CONTENT_LAYER, 1);
1054:   }
1055: 
1056:   /**
1057:    * Uninstalls the window decorations from the root pane. This should rarely
1058:    * be necessary, but we do it anyway.
1059:    *
1060:    * @param rp the root pane
1061:    */
1062:   private void uninstallWindowDecorations(JRootPane rp)
1063:   {
1064:     rp.setBorder(null);
1065:     JLayeredPane lp = rp.getLayeredPane();
1066:     for (int i = lp.getComponentCount() - 1; i >= 0; --i)
1067:       {
1068:         if (lp.getComponent(i) instanceof MetalTitlePane)
1069:           {
1070:             lp.remove(i);
1071:             break;
1072:           }
1073:       }
1074:   }
1075: }