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 }