1. /*
  2. * @(#)SynthInternalFrameTitlePane.java 1.14 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 com.sun.java.swing.plaf.gtk;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import javax.swing.*;
  11. import javax.swing.plaf.*;
  12. import javax.swing.border.*;
  13. import javax.swing.event.InternalFrameEvent;
  14. import java.util.EventListener;
  15. import java.beans.PropertyChangeListener;
  16. import java.beans.PropertyChangeEvent;
  17. import java.beans.VetoableChangeListener;
  18. import java.beans.PropertyVetoException;
  19. /**
  20. * The class that manages a basic title bar
  21. * <p>
  22. * <strong>Warning:</strong>
  23. * Serialized objects of this class will not be compatible with
  24. * future Swing releases. The current serialization support is
  25. * appropriate for short term storage or RMI between applications running
  26. * the same version of Swing. As of 1.4, support for long term storage
  27. * of all JavaBeans<sup><font size="-2">TM</font></sup>
  28. * has been added to the <code>java.beans</code> package.
  29. * Please see {@link java.beans.XMLEncoder}.
  30. *
  31. * @version 1.14 01/23/03
  32. * (originally from version 1.52 of BasicInternalFrameTitlePane)
  33. * @author David Kloba
  34. * @author Joshua Outwater
  35. * @author Steve Wilson
  36. */
  37. class SynthInternalFrameTitlePane extends JComponent implements SynthUI {
  38. protected JButton minimizeButton;
  39. protected JButton maximizeButton;
  40. protected JButton closeButton;
  41. protected JPopupMenu systemPopupMenu;
  42. protected JButton menuButton;
  43. protected JInternalFrame frame;
  44. protected Icon maximizeIcon;
  45. protected Icon restoreIcon;
  46. protected Icon minimizeIcon;
  47. protected Icon closeIcon;
  48. protected PropertyChangeListener propertyChangeListener;
  49. protected Action closeAction;
  50. protected Action maximizeAction;
  51. protected Action iconifyAction;
  52. protected Action restoreAction;
  53. protected Action moveAction;
  54. protected Action sizeAction;
  55. // PENDING: This should be looked up as needed.
  56. protected static final String CLOSE_CMD =
  57. UIManager.getString("InternalFrameTitlePane.closeButtonText");
  58. protected static final String ICONIFY_CMD =
  59. UIManager.getString("InternalFrameTitlePane.minimizeButtonText");
  60. protected static final String RESTORE_CMD =
  61. UIManager.getString("InternalFrameTitlePane.restoreButtonText");
  62. protected static final String MAXIMIZE_CMD =
  63. UIManager.getString("InternalFrameTitlePane.maximizeButtonText");
  64. protected static final String MOVE_CMD =
  65. UIManager.getString("InternalFrameTitlePane.moveButtonText");
  66. protected static final String SIZE_CMD =
  67. UIManager.getString("InternalFrameTitlePane.sizeButtonText");
  68. private String closeButtonToolTip;
  69. private String minimizeButtonToolTip;
  70. private String restoreButtonToolTip;
  71. private String maximizeButtonToolTip;
  72. private SynthStyle style;
  73. public SynthInternalFrameTitlePane(JInternalFrame f) {
  74. fetchStyle(this);
  75. frame = f;
  76. installTitlePane();
  77. menuButton.setName("InternalFrameTitlePane.menuButton");
  78. minimizeButton.setName("InternalFrameTitlePane.iconifyButton");
  79. maximizeButton.setName("InternalFrameTitlePane.maximizeButton");
  80. closeButton.setName("InternalFrameTitlePane.closeButton");
  81. }
  82. public String getUIClassID() {
  83. return "InternalFrameTitlePaneUI";
  84. }
  85. protected void installTitlePane() {
  86. installDefaults();
  87. installListeners();
  88. createActions();
  89. enableActions();
  90. createActionMap();
  91. setLayout(createLayout());
  92. assembleSystemMenu();
  93. createButtons();
  94. addSubComponents();
  95. }
  96. public SynthContext getContext(JComponent c) {
  97. return getContext(c, getComponentState(c));
  98. }
  99. public SynthContext getContext(JComponent c, int state) {
  100. return SynthContext.getContext(SynthContext.class, c,
  101. SynthLookAndFeel.getRegion(c), style, state);
  102. }
  103. private Region getRegion(JComponent c) {
  104. return SynthLookAndFeel.getRegion(c);
  105. }
  106. private int getComponentState(JComponent c) {
  107. if (frame != null) {
  108. if (frame.isSelected()) {
  109. return SELECTED;
  110. }
  111. }
  112. return SynthLookAndFeel.getComponentState(c);
  113. }
  114. protected void addSubComponents() {
  115. add(menuButton);
  116. add(minimizeButton);
  117. add(maximizeButton);
  118. add(closeButton);
  119. }
  120. protected void createActions() {
  121. maximizeAction = new MaximizeAction();
  122. iconifyAction = new IconifyAction();
  123. closeAction = new CloseAction();
  124. restoreAction = new RestoreAction();
  125. moveAction = new MoveAction();
  126. sizeAction = new SizeAction();
  127. }
  128. ActionMap createActionMap() {
  129. ActionMap map = new ActionMapUIResource();
  130. map.put("showSystemMenu", new ShowSystemMenuAction(true));
  131. map.put("hideSystemMenu", new ShowSystemMenuAction(false));
  132. return map;
  133. }
  134. protected void installListeners() {
  135. if(propertyChangeListener == null) {
  136. propertyChangeListener = createPropertyChangeListener();
  137. }
  138. frame.addPropertyChangeListener(propertyChangeListener);
  139. }
  140. protected void uninstallListeners() {
  141. frame.removePropertyChangeListener(propertyChangeListener);
  142. }
  143. private void fetchStyle(JComponent c) {
  144. SynthContext context = getContext(this, ENABLED);
  145. SynthStyle oldStyle = style;
  146. style = SynthLookAndFeel.updateStyle(context, this);
  147. if (style != oldStyle) {
  148. maximizeIcon = style.getIcon(context,"InternalFrameTitlePane.maximizeIcon");
  149. restoreIcon = style.getIcon(context,"InternalFrameTitlePane.restoreIcon");
  150. minimizeIcon = style.getIcon(context,"InternalFrameTitlePane.iconifyIcon");
  151. closeIcon = style.getIcon(context,"InternalFrameTitlePane.closeIcon");
  152. }
  153. context.dispose();
  154. }
  155. protected void installDefaults() {
  156. closeButtonToolTip =
  157. UIManager.getString("InternalFrame.closeButtonToolTip");
  158. minimizeButtonToolTip =
  159. UIManager.getString("InternalFrame.iconButtonToolTip");
  160. restoreButtonToolTip =
  161. UIManager.getString("InternalFrame.restoreButtonToolTip");
  162. maximizeButtonToolTip =
  163. UIManager.getString("InternalFrame.maxButtonToolTip");
  164. }
  165. protected void uninstallDefaults() {
  166. SynthContext context = getContext(this, ENABLED);
  167. style.uninstallDefaults(context);
  168. context.dispose();
  169. style = null;
  170. }
  171. protected void createButtons() {
  172. minimizeButton = createNoFocusButton();
  173. minimizeButton.addActionListener(iconifyAction);
  174. if (minimizeButtonToolTip != null && minimizeButtonToolTip.length() != 0) {
  175. minimizeButton.setToolTipText(minimizeButtonToolTip);
  176. }
  177. maximizeButton = createNoFocusButton();
  178. maximizeButton.addActionListener(maximizeAction);
  179. closeButton = createNoFocusButton();
  180. closeButton.addActionListener(closeAction);
  181. if (closeButtonToolTip != null && closeButtonToolTip.length() != 0) {
  182. closeButton.setToolTipText(closeButtonToolTip);
  183. }
  184. setButtonIcons();
  185. }
  186. protected void setButtonIcons() {
  187. if(frame.isIcon()) {
  188. if (restoreIcon != null) {
  189. minimizeButton.setIcon(restoreIcon);
  190. }
  191. if (restoreButtonToolTip != null && restoreButtonToolTip.length() != 0) {
  192. minimizeButton.setToolTipText(restoreButtonToolTip);
  193. }
  194. if (maximizeIcon != null) {
  195. maximizeButton.setIcon(maximizeIcon);
  196. }
  197. if (maximizeButtonToolTip != null && maximizeButtonToolTip.length() != 0) {
  198. maximizeButton.setToolTipText(maximizeButtonToolTip);
  199. }
  200. } else if (frame.isMaximum()) {
  201. if (minimizeIcon != null) {
  202. minimizeButton.setIcon(minimizeIcon);
  203. }
  204. if (minimizeButtonToolTip != null && minimizeButtonToolTip.length() != 0) {
  205. minimizeButton.setToolTipText(minimizeButtonToolTip);
  206. }
  207. if (restoreIcon != null) {
  208. maximizeButton.setIcon(restoreIcon);
  209. }
  210. if (restoreButtonToolTip != null && restoreButtonToolTip.length() != 0) {
  211. maximizeButton.setToolTipText(restoreButtonToolTip);
  212. }
  213. } else {
  214. if (minimizeIcon != null) {
  215. minimizeButton.setIcon(minimizeIcon);
  216. }
  217. if (minimizeButtonToolTip != null && minimizeButtonToolTip.length() != 0) {
  218. minimizeButton.setToolTipText(minimizeButtonToolTip);
  219. }
  220. if (maximizeIcon != null) {
  221. maximizeButton.setIcon(maximizeIcon);
  222. }
  223. if (maximizeButtonToolTip != null && maximizeButtonToolTip.length() != 0) {
  224. maximizeButton.setToolTipText(maximizeButtonToolTip);
  225. }
  226. }
  227. if (closeIcon != null) {
  228. closeButton.setIcon(closeIcon);
  229. }
  230. }
  231. protected void assembleSystemMenu() {
  232. systemPopupMenu = new JPopupMenu();
  233. addSystemMenuItems(systemPopupMenu);
  234. enableActions();
  235. menuButton = createNoFocusButton();
  236. menuButton.setIcon(frame.getFrameIcon());
  237. menuButton.addMouseListener(new MouseAdapter() {
  238. public void mousePressed(MouseEvent e) {
  239. Dimension dim = new Dimension();
  240. Border border = frame.getBorder();
  241. if (border != null) {
  242. dim.width += border.getBorderInsets(frame).left +
  243. border.getBorderInsets(frame).right;
  244. dim.height += border.getBorderInsets(frame).bottom +
  245. border.getBorderInsets(frame).top;
  246. }
  247. if (!frame.isIcon()) {
  248. systemPopupMenu.show(e.getComponent(),
  249. getX() - dim.width,
  250. getY() + getHeight() - dim.height);
  251. } else {
  252. systemPopupMenu.show(e.getComponent(),
  253. getX() - dim.width,
  254. getY() - systemPopupMenu.getPreferredSize().height -
  255. dim.height);
  256. }
  257. }
  258. });
  259. }
  260. protected void addSystemMenuItems(JPopupMenu menu) {
  261. // PENDING: this should all be localizable!
  262. JMenuItem mi = (JMenuItem)menu.add(restoreAction);
  263. mi.setMnemonic('R');
  264. mi = (JMenuItem)menu.add(moveAction);
  265. mi.setMnemonic('M');
  266. mi = (JMenuItem)menu.add(sizeAction);
  267. mi.setMnemonic('S');
  268. mi = (JMenuItem)menu.add(iconifyAction);
  269. mi.setMnemonic('n');
  270. mi = (JMenuItem)menu.add(maximizeAction);
  271. mi.setMnemonic('x');
  272. menu.add(new JSeparator());
  273. mi = (JMenuItem)menu.add(closeAction);
  274. mi.setMnemonic('C');
  275. }
  276. // SynthInternalFrameTitlePane has no UI, we'll invoke paint on it.
  277. public void paintComponent(Graphics g) {
  278. SynthContext context = getContext(this);
  279. SynthLookAndFeel.update(context, g);
  280. context.dispose();
  281. }
  282. /**
  283. * Post a WINDOW_CLOSING-like event to the frame, so that it can
  284. * be treated like a regular Frame.
  285. */
  286. protected void postClosingEvent(JInternalFrame frame) {
  287. InternalFrameEvent e = new InternalFrameEvent(
  288. frame, InternalFrameEvent.INTERNAL_FRAME_CLOSING);
  289. // Try posting event, unless there's a SecurityManager.
  290. if (JInternalFrame.class.getClassLoader() == null) {
  291. try {
  292. Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(e);
  293. return;
  294. } catch (SecurityException se) {
  295. // Use dispatchEvent instead.
  296. }
  297. }
  298. frame.dispatchEvent(e);
  299. }
  300. protected void enableActions() {
  301. restoreAction.setEnabled(frame.isMaximum() || frame.isIcon());
  302. maximizeAction.setEnabled(frame.isMaximizable() && !frame.isMaximum());
  303. iconifyAction.setEnabled(frame.isIconifiable() && !frame.isIcon());
  304. closeAction.setEnabled(frame.isClosable());
  305. sizeAction.setEnabled(false);
  306. moveAction.setEnabled(false);
  307. }
  308. protected PropertyChangeListener createPropertyChangeListener() {
  309. return new PropertyChangeHandler();
  310. }
  311. protected LayoutManager createLayout() {
  312. SynthContext context = getContext(this);
  313. LayoutManager lm =
  314. (LayoutManager)style.get(context, "InternalFrameTitlePane.titlePaneLayout");
  315. context.dispose();
  316. return (lm != null) ? lm : new TitlePaneLayout();
  317. }
  318. /**
  319. * This inner class is marked "public" due to a compiler bug.
  320. * This class should be treated as a "protected" inner class.
  321. * Instantiate it only within subclasses of <Foo>.
  322. */
  323. class PropertyChangeHandler implements PropertyChangeListener {
  324. public void propertyChange(PropertyChangeEvent evt) {
  325. String prop = (String)evt.getPropertyName();
  326. if (SynthLookAndFeel.shouldUpdateStyle(evt)) {
  327. fetchStyle(SynthInternalFrameTitlePane.this);
  328. }
  329. if (JInternalFrame.IS_SELECTED_PROPERTY.equals(prop)) {
  330. repaint();
  331. return;
  332. }
  333. if (JInternalFrame.IS_ICON_PROPERTY.equals(prop) ||
  334. JInternalFrame.IS_MAXIMUM_PROPERTY.equals(prop)) {
  335. setButtonIcons();
  336. enableActions();
  337. return;
  338. }
  339. if (prop.equals("closable")) {
  340. if ((Boolean)evt.getNewValue() == Boolean.TRUE) {
  341. add(closeButton);
  342. } else {
  343. remove(closeButton);
  344. }
  345. } else if (prop.equals("maximizable")) {
  346. if ((Boolean)evt.getNewValue() == Boolean.TRUE) {
  347. add(maximizeButton);
  348. } else {
  349. remove(maximizeButton);
  350. }
  351. } else if (prop.equals("iconable")) {
  352. if ((Boolean)evt.getNewValue() == Boolean.TRUE) {
  353. add(minimizeButton);
  354. } else {
  355. remove(minimizeButton);
  356. }
  357. }
  358. enableActions();
  359. revalidate();
  360. repaint();
  361. }
  362. } // end PropertyHandler class
  363. /**
  364. * This inner class is marked "public" due to a compiler bug.
  365. * This class should be treated as a "protected" inner class.
  366. * Instantiate it only within subclasses of <Foo>.
  367. */
  368. class TitlePaneLayout implements LayoutManager {
  369. public void addLayoutComponent(String name, Component c) {}
  370. public void removeLayoutComponent(Component c) {}
  371. public Dimension preferredLayoutSize(Container c) {
  372. return minimumLayoutSize(c);
  373. }
  374. public Dimension minimumLayoutSize(Container c) {
  375. // Calculate width.
  376. int width = 22;
  377. if (frame.isClosable()) {
  378. width += 19;
  379. }
  380. if (frame.isMaximizable()) {
  381. width += 19;
  382. }
  383. if (frame.isIconifiable()) {
  384. width += 19;
  385. }
  386. FontMetrics fm = getFontMetrics(getFont());
  387. String frameTitle = frame.getTitle();
  388. int title_w = frameTitle != null ? fm.stringWidth(frameTitle) : 0;
  389. int title_length = frameTitle != null ? frameTitle.length() : 0;
  390. // Leave room for three characters in the title.
  391. if (title_length > 3) {
  392. int subtitle_w =
  393. fm.stringWidth(frameTitle.substring(0, 3) + "...");
  394. width += (title_w < subtitle_w) ? title_w : subtitle_w;
  395. } else {
  396. width += title_w;
  397. }
  398. // Calculate height.
  399. Icon icon = frame.getFrameIcon();
  400. int fontHeight = fm.getHeight();
  401. fontHeight += 2;
  402. int iconHeight = 0;
  403. if (icon != null) {
  404. // SystemMenuBar forces the icon to be 16x16 or less.
  405. iconHeight = Math.min(icon.getIconHeight(), 16);
  406. }
  407. iconHeight += 2;
  408. int height = Math.max( fontHeight, iconHeight );
  409. Dimension dim = new Dimension(width, height);
  410. // Take into account the border insets if any.
  411. if (getBorder() != null) {
  412. Insets insets = getBorder().getBorderInsets(c);
  413. dim.height += insets.top + insets.bottom;
  414. dim.width += insets.left + insets.right;
  415. }
  416. return dim;
  417. }
  418. public void layoutContainer(Container c) {
  419. boolean leftToRight = SynthLookAndFeel.isLeftToRight(frame);
  420. int w = getWidth();
  421. int h = getHeight();
  422. int x;
  423. Icon closeIcon = closeButton.getIcon();
  424. int buttonHeight = (closeIcon != null) ? closeIcon.getIconHeight(): 12;
  425. if (buttonHeight == 0) {
  426. buttonHeight = 12;
  427. }
  428. //int buttonWidth = closeButton.getIcon().getIconWidth();
  429. Icon icon = frame.getFrameIcon();
  430. int iconHeight = (icon != null) ? icon.getIconHeight() : buttonHeight;
  431. Insets insets = frame.getInsets();
  432. x = (leftToRight) ? insets.left : w - 16 - insets.right;
  433. menuButton.setBounds(x, (h - iconHeight) / 2, 16, 14);
  434. x = (leftToRight) ? w - 16 - insets.right : insets.left;
  435. if (frame.isClosable()) {
  436. closeButton.setBounds(x, (h - buttonHeight) / 2, 16, 14);
  437. x += (leftToRight) ? -(16 + 2) : 16 + 2;
  438. }
  439. if (frame.isMaximizable()) {
  440. maximizeButton.setBounds(x, (h - buttonHeight) / 2, 16, 14);
  441. x += (leftToRight) ? -(16 + 2) : 16 + 2;
  442. }
  443. if (frame.isIconifiable()) {
  444. minimizeButton.setBounds(x, (h - buttonHeight) / 2, 16, 14);
  445. }
  446. }
  447. } // end TitlePaneLayout
  448. /**
  449. * This inner class is marked "public" due to a compiler bug.
  450. * This class should be treated as a "protected" inner class.
  451. * Instantiate it only within subclasses of <Foo>.
  452. */
  453. class CloseAction extends AbstractAction {
  454. public CloseAction() {
  455. super(CLOSE_CMD);
  456. }
  457. public void actionPerformed(ActionEvent e) {
  458. if(frame.isClosable()) {
  459. frame.doDefaultCloseAction();
  460. }
  461. }
  462. } // end CloseAction
  463. /**
  464. * This inner class is marked "public" due to a compiler bug.
  465. * This class should be treated as a "protected" inner class.
  466. * Instantiate it only within subclasses of <Foo>.
  467. */
  468. class MaximizeAction extends AbstractAction {
  469. public MaximizeAction() {
  470. super(MAXIMIZE_CMD);
  471. }
  472. public void actionPerformed(ActionEvent e) {
  473. if (frame.isMaximizable()) {
  474. if (!frame.isMaximum()) {
  475. try {
  476. frame.setMaximum(true);
  477. } catch (PropertyVetoException e5) { }
  478. } else {
  479. try {
  480. frame.setMaximum(false);
  481. } catch (PropertyVetoException e6) { }
  482. }
  483. }
  484. }
  485. } // MaximizeAction
  486. /**
  487. * This inner class is marked "public" due to a compiler bug.
  488. * This class should be treated as a "protected" inner class.
  489. * Instantiate it only within subclasses of <Foo>.
  490. */
  491. class IconifyAction extends AbstractAction {
  492. public IconifyAction() {
  493. super(ICONIFY_CMD);
  494. }
  495. public void actionPerformed(ActionEvent e) {
  496. if (frame.isIconifiable()) {
  497. if (!frame.isIcon()) {
  498. try {
  499. frame.setIcon(true);
  500. } catch (PropertyVetoException e1) { }
  501. } else{
  502. try {
  503. frame.setIcon(false);
  504. } catch (PropertyVetoException e1) { }
  505. }
  506. }
  507. }
  508. } // end IconifyAction
  509. /**
  510. * This inner class is marked "public" due to a compiler bug.
  511. * This class should be treated as a "protected" inner class.
  512. * Instantiate it only within subclasses of <Foo>.
  513. */
  514. class RestoreAction extends AbstractAction {
  515. public RestoreAction() {
  516. super(RESTORE_CMD);
  517. }
  518. public void actionPerformed(ActionEvent e) {
  519. if (frame.isMaximizable() && frame.isMaximum()) {
  520. try {
  521. frame.setMaximum(false);
  522. } catch (PropertyVetoException e4) { }
  523. }
  524. else if (frame.isIconifiable() && frame.isIcon()) {
  525. try {
  526. frame.setIcon(false);
  527. } catch (PropertyVetoException e4) { }
  528. }
  529. }
  530. } // end RestoreAction
  531. /**
  532. * This inner class is marked "public" due to a compiler bug.
  533. * This class should be treated as a "protected" inner class.
  534. * Instantiate it only within subclasses of <Foo>.
  535. */
  536. class MoveAction extends AbstractAction {
  537. public MoveAction() {
  538. super(MOVE_CMD);
  539. }
  540. public void actionPerformed(ActionEvent e) {
  541. // This action is currently undefined
  542. }
  543. } // end MoveAction
  544. /*
  545. * Handles showing and hiding the system menu.
  546. */
  547. private class ShowSystemMenuAction extends AbstractAction {
  548. private boolean show; // whether to show the menu
  549. public ShowSystemMenuAction(boolean show) {
  550. this.show = show;
  551. }
  552. public void actionPerformed(ActionEvent e) {
  553. if (show) {
  554. // TODO: FIX THIS!!!
  555. } else {
  556. systemPopupMenu.setVisible(false);
  557. }
  558. }
  559. }
  560. /**
  561. * This inner class is marked "public" due to a compiler bug.
  562. * This class should be treated as a "protected" inner class.
  563. * Instantiate it only within subclasses of <Foo>.
  564. */
  565. class SizeAction extends AbstractAction {
  566. public SizeAction() {
  567. super(SIZE_CMD);
  568. }
  569. public void actionPerformed(ActionEvent e) {
  570. // This action is currently undefined
  571. }
  572. } // end SizeAction
  573. private JButton createNoFocusButton() {
  574. JButton button = new JButton();
  575. button.setFocusable(false);
  576. button.setMargin(new Insets(0,0,0,0));
  577. return button;
  578. }
  579. } // End Title Pane Class