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