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