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