1 /*
   2  * Copyright (c) 2000, 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 package javax.swing.plaf.metal;
  27 
  28 import sun.swing.SwingUtilities2;
  29 import sun.awt.SunToolkit;
  30 import java.awt.*;
  31 import java.awt.event.*;
  32 import java.beans.*;
  33 import javax.swing.*;
  34 import javax.swing.border.*;
  35 import javax.swing.event.InternalFrameEvent;
  36 import javax.swing.plaf.*;
  37 import javax.swing.plaf.basic.*;
  38 import java.util.Locale;
  39 import javax.accessibility.*;
  40 
  41 
  42 /**
  43  * Class that manages a JLF awt.Window-descendant class's title bar.
  44  * <p>
  45  * This class assumes it will be created with a particular window
  46  * decoration style, and that if the style changes, a new one will
  47  * be created.
  48  *
  49  * @author Terry Kellerman
  50  * @since 1.4
  51  */
  52 @SuppressWarnings("serial") // Superclass is not serializable across versions
  53 class MetalTitlePane extends JComponent {
  54     private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0);
  55     private static final int IMAGE_HEIGHT = 16;
  56     private static final int IMAGE_WIDTH = 16;
  57 
  58     /**
  59      * PropertyChangeListener added to the JRootPane.
  60      */
  61     private PropertyChangeListener propertyChangeListener;
  62 
  63     /**
  64      * JMenuBar, typically renders the system menu items.
  65      */
  66     private JMenuBar menuBar;
  67     /**
  68      * Action used to close the Window.
  69      */
  70     private Action closeAction;
  71 
  72     /**
  73      * Action used to iconify the Frame.
  74      */
  75     private Action iconifyAction;
  76 
  77     /**
  78      * Action to restore the Frame size.
  79      */
  80     private Action restoreAction;
  81 
  82     /**
  83      * Action to restore the Frame size.
  84      */
  85     private Action maximizeAction;
  86 
  87     /**
  88      * Button used to maximize or restore the Frame.
  89      */
  90     private JButton toggleButton;
  91 
  92     /**
  93      * Button used to maximize or restore the Frame.
  94      */
  95     private JButton iconifyButton;
  96 
  97     /**
  98      * Button used to maximize or restore the Frame.
  99      */
 100     private JButton closeButton;
 101 
 102     /**
 103      * Icon used for toggleButton when window is normal size.
 104      */
 105     private Icon maximizeIcon;
 106 
 107     /**
 108      * Icon used for toggleButton when window is maximized.
 109      */
 110     private Icon minimizeIcon;
 111 
 112     /**
 113      * Image used for the system menu icon
 114      */
 115     private Image systemIcon;
 116 
 117     /**
 118      * Listens for changes in the state of the Window listener to update
 119      * the state of the widgets.
 120      */
 121     private WindowListener windowListener;
 122 
 123     /**
 124      * Window we're currently in.
 125      */
 126     private Window window;
 127 
 128     /**
 129      * JRootPane rendering for.
 130      */
 131     private JRootPane rootPane;
 132 
 133     /**
 134      * Room remaining in title for bumps.
 135      */
 136     private int buttonsWidth;
 137 
 138     /**
 139      * Buffered Frame.state property. As state isn't bound, this is kept
 140      * to determine when to avoid updating widgets.
 141      */
 142     private int state;
 143 
 144     /**
 145      * MetalRootPaneUI that created us.
 146      */
 147     private MetalRootPaneUI rootPaneUI;
 148 
 149 
 150     // Colors
 151     private Color inactiveBackground = UIManager.getColor("inactiveCaption");
 152     private Color inactiveForeground = UIManager.getColor("inactiveCaptionText");
 153     private Color inactiveShadow = UIManager.getColor("inactiveCaptionBorder");
 154     private Color activeBumpsHighlight = MetalLookAndFeel.getPrimaryControlHighlight();
 155     private Color activeBumpsShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
 156     private Color activeBackground = null;
 157     private Color activeForeground = null;
 158     private Color activeShadow = null;
 159 
 160     // Bumps
 161     private MetalBumps activeBumps
 162         = new MetalBumps( 0, 0,
 163                           activeBumpsHighlight,
 164                           activeBumpsShadow,
 165                           MetalLookAndFeel.getPrimaryControl() );
 166     private MetalBumps inactiveBumps
 167         = new MetalBumps( 0, 0,
 168                           MetalLookAndFeel.getControlHighlight(),
 169                           MetalLookAndFeel.getControlDarkShadow(),
 170                           MetalLookAndFeel.getControl() );
 171 
 172 
 173     public MetalTitlePane(JRootPane root, MetalRootPaneUI ui) {
 174         this.rootPane = root;
 175         rootPaneUI = ui;
 176 
 177         state = -1;
 178 
 179         installSubcomponents();
 180         determineColors();
 181         installDefaults();
 182 
 183         setLayout(createLayout());
 184     }
 185 
 186     /**
 187      * Uninstalls the necessary state.
 188      */
 189     private void uninstall() {
 190         uninstallListeners();
 191         window = null;
 192         removeAll();
 193     }
 194 
 195     /**
 196      * Installs the necessary listeners.
 197      */
 198     private void installListeners() {
 199         if (window != null) {
 200             windowListener = createWindowListener();
 201             window.addWindowListener(windowListener);
 202             propertyChangeListener = createWindowPropertyChangeListener();
 203             window.addPropertyChangeListener(propertyChangeListener);
 204         }
 205     }
 206 
 207     /**
 208      * Uninstalls the necessary listeners.
 209      */
 210     private void uninstallListeners() {
 211         if (window != null) {
 212             window.removeWindowListener(windowListener);
 213             window.removePropertyChangeListener(propertyChangeListener);
 214         }
 215     }
 216 
 217     /**
 218      * Returns the <code>WindowListener</code> to add to the
 219      * <code>Window</code>.
 220      */
 221     private WindowListener createWindowListener() {
 222         return new WindowHandler();
 223     }
 224 
 225     /**
 226      * Returns the <code>PropertyChangeListener</code> to install on
 227      * the <code>Window</code>.
 228      */
 229     private PropertyChangeListener createWindowPropertyChangeListener() {
 230         return new PropertyChangeHandler();
 231     }
 232 
 233     /**
 234      * Returns the <code>JRootPane</code> this was created for.
 235      */
 236     public JRootPane getRootPane() {
 237         return rootPane;
 238     }
 239 
 240     /**
 241      * Returns the decoration style of the <code>JRootPane</code>.
 242      */
 243     private int getWindowDecorationStyle() {
 244         return getRootPane().getWindowDecorationStyle();
 245     }
 246 
 247     public void addNotify() {
 248         super.addNotify();
 249 
 250         uninstallListeners();
 251 
 252         window = SwingUtilities.getWindowAncestor(this);
 253         if (window != null) {
 254             if (window instanceof Frame) {
 255                 setState(((Frame)window).getExtendedState());
 256             }
 257             else {
 258                 setState(0);
 259             }
 260             setActive(window.isActive());
 261             installListeners();
 262             updateSystemIcon();
 263         }
 264     }
 265 
 266     public void removeNotify() {
 267         super.removeNotify();
 268 
 269         uninstallListeners();
 270         window = null;
 271     }
 272 
 273     /**
 274      * Adds any sub-Components contained in the <code>MetalTitlePane</code>.
 275      */
 276     private void installSubcomponents() {
 277         int decorationStyle = getWindowDecorationStyle();
 278         if (decorationStyle == JRootPane.FRAME) {
 279             createActions();
 280             menuBar = createMenuBar();
 281             add(menuBar);
 282             createButtons();
 283             add(iconifyButton);
 284             add(toggleButton);
 285             add(closeButton);
 286         } else if (decorationStyle == JRootPane.PLAIN_DIALOG ||
 287                 decorationStyle == JRootPane.INFORMATION_DIALOG ||
 288                 decorationStyle == JRootPane.ERROR_DIALOG ||
 289                 decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG ||
 290                 decorationStyle == JRootPane.FILE_CHOOSER_DIALOG ||
 291                 decorationStyle == JRootPane.QUESTION_DIALOG ||
 292                 decorationStyle == JRootPane.WARNING_DIALOG) {
 293             createActions();
 294             createButtons();
 295             add(closeButton);
 296         }
 297     }
 298 
 299     /**
 300      * Determines the Colors to draw with.
 301      */
 302     private void determineColors() {
 303         switch (getWindowDecorationStyle()) {
 304         case JRootPane.FRAME:
 305             activeBackground = UIManager.getColor("activeCaption");
 306             activeForeground = UIManager.getColor("activeCaptionText");
 307             activeShadow = UIManager.getColor("activeCaptionBorder");
 308             break;
 309         case JRootPane.ERROR_DIALOG:
 310             activeBackground = UIManager.getColor(
 311                 "OptionPane.errorDialog.titlePane.background");
 312             activeForeground = UIManager.getColor(
 313                 "OptionPane.errorDialog.titlePane.foreground");
 314             activeShadow = UIManager.getColor(
 315                 "OptionPane.errorDialog.titlePane.shadow");
 316             break;
 317         case JRootPane.QUESTION_DIALOG:
 318         case JRootPane.COLOR_CHOOSER_DIALOG:
 319         case JRootPane.FILE_CHOOSER_DIALOG:
 320             activeBackground = UIManager.getColor(
 321                 "OptionPane.questionDialog.titlePane.background");
 322             activeForeground = UIManager.getColor(
 323                 "OptionPane.questionDialog.titlePane.foreground");
 324             activeShadow = UIManager.getColor(
 325                 "OptionPane.questionDialog.titlePane.shadow");
 326             break;
 327         case JRootPane.WARNING_DIALOG:
 328             activeBackground = UIManager.getColor(
 329                 "OptionPane.warningDialog.titlePane.background");
 330             activeForeground = UIManager.getColor(
 331                 "OptionPane.warningDialog.titlePane.foreground");
 332             activeShadow = UIManager.getColor(
 333                 "OptionPane.warningDialog.titlePane.shadow");
 334             break;
 335         case JRootPane.PLAIN_DIALOG:
 336         case JRootPane.INFORMATION_DIALOG:
 337         default:
 338             activeBackground = UIManager.getColor("activeCaption");
 339             activeForeground = UIManager.getColor("activeCaptionText");
 340             activeShadow = UIManager.getColor("activeCaptionBorder");
 341             break;
 342         }
 343         activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow,
 344                                   activeBackground);
 345     }
 346 
 347     /**
 348      * Installs the fonts and necessary properties on the MetalTitlePane.
 349      */
 350     private void installDefaults() {
 351         setFont(UIManager.getFont("InternalFrame.titleFont", getLocale()));
 352     }
 353 
 354     /**
 355      * Uninstalls any previously installed UI values.
 356      */
 357     private void uninstallDefaults() {
 358     }
 359 
 360     /**
 361      * Returns the <code>JMenuBar</code> displaying the appropriate
 362      * system menu items.
 363      */
 364     protected JMenuBar createMenuBar() {
 365         menuBar = new SystemMenuBar();
 366         menuBar.setFocusable(false);
 367         menuBar.setBorderPainted(true);
 368         menuBar.add(createMenu());
 369         return menuBar;
 370     }
 371 
 372     /**
 373      * Closes the Window.
 374      */
 375     private void close() {
 376         Window window = getWindow();
 377 
 378         if (window != null) {
 379             window.dispatchEvent(new WindowEvent(
 380                                  window, WindowEvent.WINDOW_CLOSING));
 381         }
 382     }
 383 
 384     /**
 385      * Iconifies the Frame.
 386      */
 387     private void iconify() {
 388         Frame frame = getFrame();
 389         if (frame != null) {
 390             frame.setExtendedState(state | Frame.ICONIFIED);
 391         }
 392     }
 393 
 394     /**
 395      * Maximizes the Frame.
 396      */
 397     private void maximize() {
 398         Frame frame = getFrame();
 399         if (frame != null) {
 400             frame.setExtendedState(state | Frame.MAXIMIZED_BOTH);
 401         }
 402     }
 403 
 404     /**
 405      * Restores the Frame size.
 406      */
 407     private void restore() {
 408         Frame frame = getFrame();
 409 
 410         if (frame == null) {
 411             return;
 412         }
 413 
 414         if ((state & Frame.ICONIFIED) != 0) {
 415             frame.setExtendedState(state & ~Frame.ICONIFIED);
 416         } else {
 417             frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
 418         }
 419     }
 420 
 421     /**
 422      * Create the <code>Action</code>s that get associated with the
 423      * buttons and menu items.
 424      */
 425     private void createActions() {
 426         closeAction = new CloseAction();
 427         if (getWindowDecorationStyle() == JRootPane.FRAME) {
 428             iconifyAction = new IconifyAction();
 429             restoreAction = new RestoreAction();
 430             maximizeAction = new MaximizeAction();
 431         }
 432     }
 433 
 434     /**
 435      * Returns the <code>JMenu</code> displaying the appropriate menu items
 436      * for manipulating the Frame.
 437      */
 438     private JMenu createMenu() {
 439         JMenu menu = new JMenu("");
 440         if (getWindowDecorationStyle() == JRootPane.FRAME) {
 441             addMenuItems(menu);
 442         }
 443         return menu;
 444     }
 445 
 446     /**
 447      * Adds the necessary <code>JMenuItem</code>s to the passed in menu.
 448      */
 449     private void addMenuItems(JMenu menu) {
 450         Locale locale = getRootPane().getLocale();
 451         JMenuItem mi = menu.add(restoreAction);
 452         int mnemonic = MetalUtils.getInt("MetalTitlePane.restoreMnemonic", -1);
 453 
 454         if (mnemonic != -1) {
 455             mi.setMnemonic(mnemonic);
 456         }
 457 
 458         mi = menu.add(iconifyAction);
 459         mnemonic = MetalUtils.getInt("MetalTitlePane.iconifyMnemonic", -1);
 460         if (mnemonic != -1) {
 461             mi.setMnemonic(mnemonic);
 462         }
 463 
 464         if (Toolkit.getDefaultToolkit().isFrameStateSupported(
 465                 Frame.MAXIMIZED_BOTH)) {
 466             mi = menu.add(maximizeAction);
 467             mnemonic =
 468                 MetalUtils.getInt("MetalTitlePane.maximizeMnemonic", -1);
 469             if (mnemonic != -1) {
 470                 mi.setMnemonic(mnemonic);
 471             }
 472         }
 473 
 474         menu.add(new JSeparator());
 475 
 476         mi = menu.add(closeAction);
 477         mnemonic = MetalUtils.getInt("MetalTitlePane.closeMnemonic", -1);
 478         if (mnemonic != -1) {
 479             mi.setMnemonic(mnemonic);
 480         }
 481     }
 482 
 483     /**
 484      * Returns a <code>JButton</code> appropriate for placement on the
 485      * TitlePane.
 486      */
 487     private JButton createTitleButton() {
 488         JButton button = new JButton();
 489 
 490         button.setFocusPainted(false);
 491         button.setFocusable(false);
 492         button.setOpaque(true);
 493         return button;
 494     }
 495 
 496     /**
 497      * Creates the Buttons that will be placed on the TitlePane.
 498      */
 499     private void createButtons() {
 500         closeButton = createTitleButton();
 501         closeButton.setAction(closeAction);
 502         closeButton.setText(null);
 503         closeButton.putClientProperty("paintActive", Boolean.TRUE);
 504         closeButton.setBorder(handyEmptyBorder);
 505         closeButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
 506                                       "Close");
 507         closeButton.setIcon(UIManager.getIcon("InternalFrame.closeIcon"));
 508 
 509         if (getWindowDecorationStyle() == JRootPane.FRAME) {
 510             maximizeIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
 511             minimizeIcon = UIManager.getIcon("InternalFrame.minimizeIcon");
 512 
 513             iconifyButton = createTitleButton();
 514             iconifyButton.setAction(iconifyAction);
 515             iconifyButton.setText(null);
 516             iconifyButton.putClientProperty("paintActive", Boolean.TRUE);
 517             iconifyButton.setBorder(handyEmptyBorder);
 518             iconifyButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
 519                                             "Iconify");
 520             iconifyButton.setIcon(UIManager.getIcon("InternalFrame.iconifyIcon"));
 521 
 522             toggleButton = createTitleButton();
 523             toggleButton.setAction(restoreAction);
 524             toggleButton.putClientProperty("paintActive", Boolean.TRUE);
 525             toggleButton.setBorder(handyEmptyBorder);
 526             toggleButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
 527                                            "Maximize");
 528             toggleButton.setIcon(maximizeIcon);
 529         }
 530     }
 531 
 532     /**
 533      * Returns the <code>LayoutManager</code> that should be installed on
 534      * the <code>MetalTitlePane</code>.
 535      */
 536     private LayoutManager createLayout() {
 537         return new TitlePaneLayout();
 538     }
 539 
 540     /**
 541      * Updates state dependant upon the Window's active state.
 542      */
 543     private void setActive(boolean isActive) {
 544         Boolean activeB = isActive ? Boolean.TRUE : Boolean.FALSE;
 545 
 546         closeButton.putClientProperty("paintActive", activeB);
 547         if (getWindowDecorationStyle() == JRootPane.FRAME) {
 548             iconifyButton.putClientProperty("paintActive", activeB);
 549             toggleButton.putClientProperty("paintActive", activeB);
 550         }
 551         // Repaint the whole thing as the Borders that are used have
 552         // different colors for active vs inactive
 553         getRootPane().repaint();
 554     }
 555 
 556     /**
 557      * Sets the state of the Window.
 558      */
 559     private void setState(int state) {
 560         setState(state, false);
 561     }
 562 
 563     /**
 564      * Sets the state of the window. If <code>updateRegardless</code> is
 565      * true and the state has not changed, this will update anyway.
 566      */
 567     private void setState(int state, boolean updateRegardless) {
 568         Window w = getWindow();
 569 
 570         if (w != null && getWindowDecorationStyle() == JRootPane.FRAME) {
 571             if (this.state == state && !updateRegardless) {
 572                 return;
 573             }
 574             Frame frame = getFrame();
 575 
 576             if (frame != null) {
 577                 JRootPane rootPane = getRootPane();
 578 
 579                 if (((state & Frame.MAXIMIZED_BOTH) != 0) &&
 580                         (rootPane.getBorder() == null ||
 581                         (rootPane.getBorder() instanceof UIResource)) &&
 582                             frame.isShowing()) {
 583                     rootPane.setBorder(null);
 584                 }
 585                 else if ((state & Frame.MAXIMIZED_BOTH) == 0) {
 586                     // This is a croak, if state becomes bound, this can
 587                     // be nuked.
 588                     rootPaneUI.installBorder(rootPane);
 589                 }
 590                 if (frame.isResizable()) {
 591                     if ((state & Frame.MAXIMIZED_BOTH) != 0) {
 592                         updateToggleButton(restoreAction, minimizeIcon);
 593                         maximizeAction.setEnabled(false);
 594                         restoreAction.setEnabled(true);
 595                     }
 596                     else {
 597                         updateToggleButton(maximizeAction, maximizeIcon);
 598                         maximizeAction.setEnabled(true);
 599                         restoreAction.setEnabled(false);
 600                     }
 601                     if (toggleButton.getParent() == null ||
 602                         iconifyButton.getParent() == null) {
 603                         add(toggleButton);
 604                         add(iconifyButton);
 605                         revalidate();
 606                         repaint();
 607                     }
 608                     toggleButton.setText(null);
 609                 }
 610                 else {
 611                     maximizeAction.setEnabled(false);
 612                     restoreAction.setEnabled(false);
 613                     if (toggleButton.getParent() != null) {
 614                         remove(toggleButton);
 615                         revalidate();
 616                         repaint();
 617                     }
 618                 }
 619             }
 620             else {
 621                 // Not contained in a Frame
 622                 maximizeAction.setEnabled(false);
 623                 restoreAction.setEnabled(false);
 624                 iconifyAction.setEnabled(false);
 625                 remove(toggleButton);
 626                 remove(iconifyButton);
 627                 revalidate();
 628                 repaint();
 629             }
 630             closeAction.setEnabled(true);
 631             this.state = state;
 632         }
 633     }
 634 
 635     /**
 636      * Updates the toggle button to contain the Icon <code>icon</code>, and
 637      * Action <code>action</code>.
 638      */
 639     private void updateToggleButton(Action action, Icon icon) {
 640         toggleButton.setAction(action);
 641         toggleButton.setIcon(icon);
 642         toggleButton.setText(null);
 643     }
 644 
 645     /**
 646      * Returns the Frame rendering in. This will return null if the
 647      * <code>JRootPane</code> is not contained in a <code>Frame</code>.
 648      */
 649     private Frame getFrame() {
 650         Window window = getWindow();
 651 
 652         if (window instanceof Frame) {
 653             return (Frame)window;
 654         }
 655         return null;
 656     }
 657 
 658     /**
 659      * Returns the <code>Window</code> the <code>JRootPane</code> is
 660      * contained in. This will return null if there is no parent ancestor
 661      * of the <code>JRootPane</code>.
 662      */
 663     private Window getWindow() {
 664         return window;
 665     }
 666 
 667     /**
 668      * Returns the String to display as the title.
 669      */
 670     private String getTitle() {
 671         Window w = getWindow();
 672 
 673         if (w instanceof Frame) {
 674             return ((Frame)w).getTitle();
 675         }
 676         else if (w instanceof Dialog) {
 677             return ((Dialog)w).getTitle();
 678         }
 679         return null;
 680     }
 681 
 682     /**
 683      * Renders the TitlePane.
 684      */
 685     public void paintComponent(Graphics g)  {
 686         // As state isn't bound, we need a convenience place to check
 687         // if it has changed. Changing the state typically changes the
 688         if (getFrame() != null) {
 689             setState(getFrame().getExtendedState());
 690         }
 691         JRootPane rootPane = getRootPane();
 692         Window window = getWindow();
 693         boolean leftToRight = (window == null) ?
 694                                rootPane.getComponentOrientation().isLeftToRight() :
 695                                window.getComponentOrientation().isLeftToRight();
 696         boolean isSelected = (window == null) ? true : window.isActive();
 697         int width = getWidth();
 698         int height = getHeight();
 699 
 700         Color background;
 701         Color foreground;
 702         Color darkShadow;
 703 
 704         MetalBumps bumps;
 705 
 706         if (isSelected) {
 707             background = activeBackground;
 708             foreground = activeForeground;
 709             darkShadow = activeShadow;
 710             bumps = activeBumps;
 711         } else {
 712             background = inactiveBackground;
 713             foreground = inactiveForeground;
 714             darkShadow = inactiveShadow;
 715             bumps = inactiveBumps;
 716         }
 717 
 718         g.setColor(background);
 719         g.fillRect(0, 0, width, height);
 720 
 721         g.setColor( darkShadow );
 722         g.drawLine ( 0, height - 1, width, height -1);
 723         g.drawLine ( 0, 0, 0 ,0);
 724         g.drawLine ( width - 1, 0 , width -1, 0);
 725 
 726         int xOffset = leftToRight ? 5 : width - 5;
 727 
 728         if (getWindowDecorationStyle() == JRootPane.FRAME) {
 729             xOffset += leftToRight ? IMAGE_WIDTH + 5 : - IMAGE_WIDTH - 5;
 730         }
 731 
 732         String theTitle = getTitle();
 733         if (theTitle != null) {
 734             FontMetrics fm = SwingUtilities2.getFontMetrics(rootPane, g);
 735 
 736             g.setColor(foreground);
 737 
 738             int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
 739 
 740             Rectangle rect = new Rectangle(0, 0, 0, 0);
 741             if (iconifyButton != null && iconifyButton.getParent() != null) {
 742                 rect = iconifyButton.getBounds();
 743             }
 744             int titleW;
 745 
 746             if( leftToRight ) {
 747                 if (rect.x == 0) {
 748                     rect.x = window.getWidth() - window.getInsets().right-2;
 749                 }
 750                 titleW = rect.x - xOffset - 4;
 751                 theTitle = SwingUtilities2.clipStringIfNecessary(
 752                                 rootPane, fm, theTitle, titleW);
 753             } else {
 754                 titleW = xOffset - rect.x - rect.width - 4;
 755                 theTitle = SwingUtilities2.clipStringIfNecessary(
 756                                 rootPane, fm, theTitle, titleW);
 757                 xOffset -= SwingUtilities2.stringWidth(rootPane, fm,
 758                                                        theTitle);
 759             }
 760             int titleLength = SwingUtilities2.stringWidth(rootPane, fm,
 761                                                           theTitle);
 762             SwingUtilities2.drawString(rootPane, g, theTitle, xOffset,
 763                                        yOffset );
 764             xOffset += leftToRight ? titleLength + 5  : -5;
 765         }
 766 
 767         int bumpXOffset;
 768         int bumpLength;
 769         if( leftToRight ) {
 770             bumpLength = width - buttonsWidth - xOffset - 5;
 771             bumpXOffset = xOffset;
 772         } else {
 773             bumpLength = xOffset - buttonsWidth - 5;
 774             bumpXOffset = buttonsWidth + 5;
 775         }
 776         int bumpYOffset = 3;
 777         int bumpHeight = getHeight() - (2 * bumpYOffset);
 778         bumps.setBumpArea( bumpLength, bumpHeight );
 779         bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
 780     }
 781 
 782     /**
 783      * Actions used to <code>close</code> the <code>Window</code>.
 784      */
 785     @SuppressWarnings("serial") // Superclass is not serializable across versions
 786     private class CloseAction extends AbstractAction {
 787         public CloseAction() {
 788             super(UIManager.getString("MetalTitlePane.closeTitle",
 789                                       getLocale()));
 790         }
 791 
 792         public void actionPerformed(ActionEvent e) {
 793             close();
 794         }
 795     }
 796 
 797 
 798     /**
 799      * Actions used to <code>iconfiy</code> the <code>Frame</code>.
 800      */
 801     @SuppressWarnings("serial") // Superclass is not serializable across versions
 802     private class IconifyAction extends AbstractAction {
 803         public IconifyAction() {
 804             super(UIManager.getString("MetalTitlePane.iconifyTitle",
 805                                       getLocale()));
 806         }
 807 
 808         public void actionPerformed(ActionEvent e) {
 809             iconify();
 810         }
 811     }
 812 
 813 
 814     /**
 815      * Actions used to <code>restore</code> the <code>Frame</code>.
 816      */
 817     @SuppressWarnings("serial") // Superclass is not serializable across versions
 818     private class RestoreAction extends AbstractAction {
 819         public RestoreAction() {
 820             super(UIManager.getString
 821                   ("MetalTitlePane.restoreTitle", getLocale()));
 822         }
 823 
 824         public void actionPerformed(ActionEvent e) {
 825             restore();
 826         }
 827     }
 828 
 829 
 830     /**
 831      * Actions used to <code>restore</code> the <code>Frame</code>.
 832      */
 833     @SuppressWarnings("serial") // Superclass is not serializable across versions
 834     private class MaximizeAction extends AbstractAction {
 835         public MaximizeAction() {
 836             super(UIManager.getString("MetalTitlePane.maximizeTitle",
 837                                       getLocale()));
 838         }
 839 
 840         public void actionPerformed(ActionEvent e) {
 841             maximize();
 842         }
 843     }
 844 
 845 
 846     /**
 847      * Class responsible for drawing the system menu. Looks up the
 848      * image to draw from the Frame associated with the
 849      * <code>JRootPane</code>.
 850      */
 851     @SuppressWarnings("serial") // Superclass is not serializable across versions
 852     private class SystemMenuBar extends JMenuBar {
 853         public void paint(Graphics g) {
 854             if (isOpaque()) {
 855                 g.setColor(getBackground());
 856                 g.fillRect(0, 0, getWidth(), getHeight());
 857             }
 858 
 859             if (systemIcon != null) {
 860                 g.drawImage(systemIcon, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
 861             } else {
 862                 Icon icon = UIManager.getIcon("InternalFrame.icon");
 863 
 864                 if (icon != null) {
 865                     icon.paintIcon(this, g, 0, 0);
 866                 }
 867             }
 868         }
 869         public Dimension getMinimumSize() {
 870             return getPreferredSize();
 871         }
 872         public Dimension getPreferredSize() {
 873             Dimension size = super.getPreferredSize();
 874 
 875             return new Dimension(Math.max(IMAGE_WIDTH, size.width),
 876                                  Math.max(size.height, IMAGE_HEIGHT));
 877         }
 878     }
 879 
 880     private class TitlePaneLayout implements LayoutManager {
 881         public void addLayoutComponent(String name, Component c) {}
 882         public void removeLayoutComponent(Component c) {}
 883         public Dimension preferredLayoutSize(Container c)  {
 884             int height = computeHeight();
 885             return new Dimension(height, height);
 886         }
 887 
 888         public Dimension minimumLayoutSize(Container c) {
 889             return preferredLayoutSize(c);
 890         }
 891 
 892         private int computeHeight() {
 893             FontMetrics fm = rootPane.getFontMetrics(getFont());
 894             int fontHeight = fm.getHeight();
 895             fontHeight += 7;
 896             int iconHeight = 0;
 897             if (getWindowDecorationStyle() == JRootPane.FRAME) {
 898                 iconHeight = IMAGE_HEIGHT;
 899             }
 900 
 901             int finalHeight = Math.max( fontHeight, iconHeight );
 902             return finalHeight;
 903         }
 904 
 905         public void layoutContainer(Container c) {
 906             boolean leftToRight = (window == null) ?
 907                 getRootPane().getComponentOrientation().isLeftToRight() :
 908                 window.getComponentOrientation().isLeftToRight();
 909 
 910             int w = getWidth();
 911             int x;
 912             int y = 3;
 913             int spacing;
 914             int buttonHeight;
 915             int buttonWidth;
 916 
 917             if (closeButton != null && closeButton.getIcon() != null) {
 918                 buttonHeight = closeButton.getIcon().getIconHeight();
 919                 buttonWidth = closeButton.getIcon().getIconWidth();
 920             }
 921             else {
 922                 buttonHeight = IMAGE_HEIGHT;
 923                 buttonWidth = IMAGE_WIDTH;
 924             }
 925 
 926             // assumes all buttons have the same dimensions
 927             // these dimensions include the borders
 928 
 929             x = leftToRight ? w : 0;
 930 
 931             spacing = 5;
 932             x = leftToRight ? spacing : w - buttonWidth - spacing;
 933             if (menuBar != null) {
 934                 menuBar.setBounds(x, y, buttonWidth, buttonHeight);
 935             }
 936 
 937             x = leftToRight ? w : 0;
 938             spacing = 4;
 939             x += leftToRight ? -spacing -buttonWidth : spacing;
 940             if (closeButton != null) {
 941                 closeButton.setBounds(x, y, buttonWidth, buttonHeight);
 942             }
 943 
 944             if( !leftToRight ) x += buttonWidth;
 945 
 946             if (getWindowDecorationStyle() == JRootPane.FRAME) {
 947                 if (Toolkit.getDefaultToolkit().isFrameStateSupported(
 948                         Frame.MAXIMIZED_BOTH)) {
 949                     if (toggleButton.getParent() != null) {
 950                         spacing = 10;
 951                         x += leftToRight ? -spacing -buttonWidth : spacing;
 952                         toggleButton.setBounds(x, y, buttonWidth, buttonHeight);
 953                         if (!leftToRight) {
 954                             x += buttonWidth;
 955                         }
 956                     }
 957                 }
 958 
 959                 if (iconifyButton != null && iconifyButton.getParent() != null) {
 960                     spacing = 2;
 961                     x += leftToRight ? -spacing -buttonWidth : spacing;
 962                     iconifyButton.setBounds(x, y, buttonWidth, buttonHeight);
 963                     if (!leftToRight) {
 964                         x += buttonWidth;
 965                     }
 966                 }
 967             }
 968             buttonsWidth = leftToRight ? w - x : x;
 969         }
 970     }
 971 
 972 
 973 
 974     /**
 975      * PropertyChangeListener installed on the Window. Updates the necessary
 976      * state as the state of the Window changes.
 977      */
 978     private class PropertyChangeHandler implements PropertyChangeListener {
 979         public void propertyChange(PropertyChangeEvent pce) {
 980             String name = pce.getPropertyName();
 981 
 982             // Frame.state isn't currently bound.
 983             if ("resizable".equals(name) || "state".equals(name)) {
 984                 Frame frame = getFrame();
 985 
 986                 if (frame != null) {
 987                     setState(frame.getExtendedState(), true);
 988                 }
 989                 if ("resizable".equals(name)) {
 990                     getRootPane().repaint();
 991                 }
 992             }
 993             else if ("title".equals(name)) {
 994                 repaint();
 995             }
 996             else if ("componentOrientation" == name) {
 997                 revalidate();
 998                 repaint();
 999             }
1000             else if ("iconImage" == name) {
1001                 updateSystemIcon();
1002                 revalidate();
1003                 repaint();
1004             }
1005         }
1006     }
1007 
1008     /**
1009      * Update the image used for the system icon
1010      */
1011     private void updateSystemIcon() {
1012         Window window = getWindow();
1013         if (window == null) {
1014             systemIcon = null;
1015             return;
1016         }
1017         java.util.List<Image> icons = window.getIconImages();
1018         assert icons != null;
1019 
1020         if (icons.size() == 0) {
1021             systemIcon = null;
1022         }
1023         else if (icons.size() == 1) {
1024             systemIcon = icons.get(0);
1025         }
1026         else {
1027             systemIcon = SunToolkit.getScaledIconImage(icons,
1028                                                        IMAGE_WIDTH,
1029                                                        IMAGE_HEIGHT);
1030         }
1031     }
1032 
1033 
1034     /**
1035      * WindowListener installed on the Window, updates the state as necessary.
1036      */
1037     private class WindowHandler extends WindowAdapter {
1038         public void windowActivated(WindowEvent ev) {
1039             setActive(true);
1040         }
1041 
1042         public void windowDeactivated(WindowEvent ev) {
1043             setActive(false);
1044         }
1045     }
1046 }