1. /*
  2. * @(#)MetalInternalFrameTitlePane.java 1.51 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 javax.swing.*;
  11. import javax.swing.border.*;
  12. import javax.swing.event.InternalFrameEvent;
  13. import java.util.EventListener;
  14. import java.beans.PropertyChangeListener;
  15. import java.beans.PropertyChangeEvent;
  16. import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
  17. /**
  18. * Class that manages a JLF title bar
  19. * @version 1.51 01/23/03
  20. * @author Steve Wilson
  21. * @author Brian Beck
  22. * @since 1.3
  23. */
  24. public class MetalInternalFrameTitlePane extends BasicInternalFrameTitlePane {
  25. protected boolean isPalette = false;
  26. protected Icon paletteCloseIcon;
  27. protected int paletteTitleHeight;
  28. private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0);
  29. /**
  30. * Key used to lookup Color from UIManager. If this is null,
  31. * <code>getWindowTitleBackground</code> is used.
  32. */
  33. private String selectedBackgroundKey;
  34. /**
  35. * Key used to lookup Color from UIManager. If this is null,
  36. * <code>getWindowTitleForeground</code> is used.
  37. */
  38. private String selectedForegroundKey;
  39. /**
  40. * Key used to lookup shadow color from UIManager. If this is null,
  41. * <code>getPrimaryControlDarkShadow</code> is used.
  42. */
  43. private String selectedShadowKey;
  44. /**
  45. * Boolean indicating the state of the <code>JInternalFrame</code>s
  46. * closable property at <code>updateUI</code> time.
  47. */
  48. private boolean wasClosable;
  49. int buttonsWidth = 0;
  50. MetalBumps activeBumps
  51. = new MetalBumps( 0, 0,
  52. MetalLookAndFeel.getPrimaryControlHighlight(),
  53. MetalLookAndFeel.getPrimaryControlDarkShadow(),
  54. MetalLookAndFeel.getPrimaryControl() );
  55. MetalBumps inactiveBumps
  56. = new MetalBumps( 0, 0,
  57. MetalLookAndFeel.getControlHighlight(),
  58. MetalLookAndFeel.getControlDarkShadow(),
  59. MetalLookAndFeel.getControl() );
  60. MetalBumps paletteBumps;
  61. private Color activeBumpsHighlight = MetalLookAndFeel.
  62. getPrimaryControlHighlight();
  63. private Color activeBumpsShadow = MetalLookAndFeel.
  64. getPrimaryControlDarkShadow();
  65. public MetalInternalFrameTitlePane(JInternalFrame f) {
  66. super( f );
  67. }
  68. public void addNotify() {
  69. super.addNotify();
  70. // This is done here instead of in installDefaults as I was worried
  71. // that the BasicInternalFrameUI might not be fully initialized, and
  72. // that if this resets the closable state the BasicInternalFrameUI
  73. // Listeners that get notified might be in an odd/uninitialized state.
  74. updateOptionPaneState();
  75. }
  76. protected void installDefaults() {
  77. super.installDefaults();
  78. setFont( UIManager.getFont("InternalFrame.titleFont") );
  79. paletteTitleHeight
  80. = UIManager.getInt("InternalFrame.paletteTitleHeight");
  81. paletteCloseIcon = UIManager.getIcon("InternalFrame.paletteCloseIcon");
  82. wasClosable = frame.isClosable();
  83. selectedForegroundKey = selectedBackgroundKey = null;
  84. }
  85. protected void uninstallDefaults() {
  86. super.uninstallDefaults();
  87. if (wasClosable != frame.isClosable()) {
  88. frame.setClosable(wasClosable);
  89. }
  90. }
  91. protected void createButtons() {
  92. super.createButtons();
  93. Boolean paintActive = frame.isSelected() ? Boolean.TRUE:Boolean.FALSE;
  94. iconButton.putClientProperty("paintActive", paintActive);
  95. iconButton.setBorder(handyEmptyBorder);
  96. iconButton.getAccessibleContext().setAccessibleName(
  97. UIManager.getString(
  98. "InternalFrameTitlePane.iconifyButtonAccessibleName"));
  99. maxButton.putClientProperty("paintActive", paintActive);
  100. maxButton.setBorder(handyEmptyBorder);
  101. maxButton.getAccessibleContext().setAccessibleName(
  102. UIManager.getString("InternalFrameTitlePane.maximizeButtonAccessibleName"));
  103. closeButton.putClientProperty("paintActive", paintActive);
  104. closeButton.setBorder(handyEmptyBorder);
  105. closeButton.getAccessibleContext().setAccessibleName(
  106. UIManager.getString("InternalFrameTitlePane.closeButtonAccessibleName"));
  107. // The palette close icon isn't opaque while the regular close icon is.
  108. // This makes sure palette close buttons have the right background.
  109. closeButton.setBackground(MetalLookAndFeel.getPrimaryControlShadow());
  110. }
  111. /**
  112. * Override the parent's method to do nothing. Metal frames do not
  113. * have system menus.
  114. */
  115. protected void assembleSystemMenu() {}
  116. /**
  117. * Override the parent's method to do nothing. Metal frames do not
  118. * have system menus.
  119. */
  120. protected void addSystemMenuItems(JMenu systemMenu) {}
  121. /**
  122. * Override the parent's method to do nothing. Metal frames do not
  123. * have system menus.
  124. */
  125. protected void showSystemMenu() {}
  126. /**
  127. * Override the parent's method avoid creating a menu bar. Metal frames
  128. * do not have system menus.
  129. */
  130. protected void addSubComponents() {
  131. add(iconButton);
  132. add(maxButton);
  133. add(closeButton);
  134. }
  135. protected PropertyChangeListener createPropertyChangeListener() {
  136. return new MetalPropertyChangeHandler();
  137. }
  138. protected LayoutManager createLayout() {
  139. return new MetalTitlePaneLayout();
  140. }
  141. class MetalPropertyChangeHandler
  142. extends BasicInternalFrameTitlePane.PropertyChangeHandler
  143. {
  144. public void propertyChange(PropertyChangeEvent evt) {
  145. String prop = (String)evt.getPropertyName();
  146. if( prop.equals(JInternalFrame.IS_SELECTED_PROPERTY) ) {
  147. Boolean b = (Boolean)evt.getNewValue();
  148. iconButton.putClientProperty("paintActive", b);
  149. closeButton.putClientProperty("paintActive", b);
  150. maxButton.putClientProperty("paintActive", b);
  151. }
  152. else if ("JInternalFrame.messageType".equals(prop)) {
  153. updateOptionPaneState();
  154. frame.repaint();
  155. }
  156. super.propertyChange(evt);
  157. }
  158. }
  159. class MetalTitlePaneLayout extends TitlePaneLayout {
  160. public void addLayoutComponent(String name, Component c) {}
  161. public void removeLayoutComponent(Component c) {}
  162. public Dimension preferredLayoutSize(Container c) {
  163. return minimumLayoutSize(c);
  164. }
  165. public Dimension minimumLayoutSize(Container c) {
  166. // Compute width.
  167. int width = 30;
  168. if (frame.isClosable()) {
  169. width += 21;
  170. }
  171. if (frame.isMaximizable()) {
  172. width += 16 + (frame.isClosable() ? 10 : 4);
  173. }
  174. if (frame.isIconifiable()) {
  175. width += 16 + (frame.isMaximizable() ? 2 :
  176. (frame.isClosable() ? 10 : 4));
  177. }
  178. FontMetrics fm = getFontMetrics(getFont());
  179. String frameTitle = frame.getTitle();
  180. int title_w = frameTitle != null ? fm.stringWidth(frameTitle) : 0;
  181. int title_length = frameTitle != null ? frameTitle.length() : 0;
  182. if (title_length > 2) {
  183. int subtitle_w =
  184. fm.stringWidth(frame.getTitle().substring(0, 2) + "...");
  185. width += (title_w < subtitle_w) ? title_w : subtitle_w;
  186. }
  187. else {
  188. width += title_w;
  189. }
  190. // Compute height.
  191. int height = 0;
  192. if (isPalette) {
  193. height = paletteTitleHeight;
  194. } else {
  195. int fontHeight = fm.getHeight();
  196. fontHeight += 7;
  197. Icon icon = frame.getFrameIcon();
  198. int iconHeight = 0;
  199. if (icon != null) {
  200. // SystemMenuBar forces the icon to be 16x16 or less.
  201. iconHeight = Math.min(icon.getIconHeight(), 16);
  202. }
  203. iconHeight += 5;
  204. height = Math.max(fontHeight, iconHeight);
  205. }
  206. return new Dimension(width, height);
  207. }
  208. public void layoutContainer(Container c) {
  209. boolean leftToRight = MetalUtils.isLeftToRight(frame);
  210. int w = getWidth();
  211. int x = leftToRight ? w : 0;
  212. int y = 2;
  213. int spacing;
  214. // assumes all buttons have the same dimensions
  215. // these dimensions include the borders
  216. int buttonHeight = closeButton.getIcon().getIconHeight();
  217. int buttonWidth = closeButton.getIcon().getIconWidth();
  218. if(frame.isClosable()) {
  219. if (isPalette) {
  220. spacing = 3;
  221. x += leftToRight ? -spacing -(buttonWidth+2) : spacing;
  222. closeButton.setBounds(x, y, buttonWidth+2, getHeight()-4);
  223. if( !leftToRight ) x += (buttonWidth+2);
  224. } else {
  225. spacing = 4;
  226. x += leftToRight ? -spacing -buttonWidth : spacing;
  227. closeButton.setBounds(x, y, buttonWidth, buttonHeight);
  228. if( !leftToRight ) x += buttonWidth;
  229. }
  230. }
  231. if(frame.isMaximizable() && !isPalette ) {
  232. spacing = frame.isClosable() ? 10 : 4;
  233. x += leftToRight ? -spacing -buttonWidth : spacing;
  234. maxButton.setBounds(x, y, buttonWidth, buttonHeight);
  235. if( !leftToRight ) x += buttonWidth;
  236. }
  237. if(frame.isIconifiable() && !isPalette ) {
  238. spacing = frame.isMaximizable() ? 2
  239. : (frame.isClosable() ? 10 : 4);
  240. x += leftToRight ? -spacing -buttonWidth : spacing;
  241. iconButton.setBounds(x, y, buttonWidth, buttonHeight);
  242. if( !leftToRight ) x += buttonWidth;
  243. }
  244. buttonsWidth = leftToRight ? w - x : x;
  245. }
  246. }
  247. public void paintPalette(Graphics g) {
  248. boolean leftToRight = MetalUtils.isLeftToRight(frame);
  249. int width = getWidth();
  250. int height = getHeight();
  251. if (paletteBumps == null) {
  252. paletteBumps
  253. = new MetalBumps(0, 0,
  254. MetalLookAndFeel.getPrimaryControlHighlight(),
  255. MetalLookAndFeel.getPrimaryControlInfo(),
  256. MetalLookAndFeel.getPrimaryControlShadow() );
  257. }
  258. Color background = MetalLookAndFeel.getPrimaryControlShadow();
  259. Color darkShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
  260. g.setColor(background);
  261. g.fillRect(0, 0, width, height);
  262. g.setColor( darkShadow );
  263. g.drawLine ( 0, height - 1, width, height -1);
  264. int xOffset = leftToRight ? 4 : buttonsWidth + 4;
  265. int bumpLength = width - buttonsWidth -2*4;
  266. int bumpHeight = getHeight() - 4;
  267. paletteBumps.setBumpArea( bumpLength, bumpHeight );
  268. paletteBumps.paintIcon( this, g, xOffset, 2);
  269. }
  270. public void paintComponent(Graphics g) {
  271. if(isPalette) {
  272. paintPalette(g);
  273. return;
  274. }
  275. boolean leftToRight = MetalUtils.isLeftToRight(frame);
  276. boolean isSelected = frame.isSelected();
  277. int width = getWidth();
  278. int height = getHeight();
  279. Color background = null;
  280. Color foreground = null;
  281. Color shadow = null;
  282. MetalBumps bumps;
  283. if (isSelected) {
  284. if (selectedBackgroundKey != null) {
  285. background = UIManager.getColor(selectedBackgroundKey);
  286. }
  287. if (background == null) {
  288. background = MetalLookAndFeel.getWindowTitleBackground();
  289. }
  290. if (selectedForegroundKey != null) {
  291. foreground = UIManager.getColor(selectedForegroundKey);
  292. }
  293. if (selectedShadowKey != null) {
  294. shadow = UIManager.getColor(selectedShadowKey);
  295. }
  296. if (shadow == null) {
  297. shadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
  298. }
  299. if (foreground == null) {
  300. foreground = MetalLookAndFeel.getWindowTitleForeground();
  301. }
  302. activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow,
  303. background);
  304. bumps = activeBumps;
  305. } else {
  306. background = MetalLookAndFeel.getWindowTitleInactiveBackground();
  307. foreground = MetalLookAndFeel.getWindowTitleInactiveForeground();
  308. shadow = MetalLookAndFeel.getControlDarkShadow();
  309. bumps = inactiveBumps;
  310. }
  311. g.setColor(background);
  312. g.fillRect(0, 0, width, height);
  313. g.setColor( shadow );
  314. g.drawLine ( 0, height - 1, width, height -1);
  315. g.drawLine ( 0, 0, 0 ,0);
  316. g.drawLine ( width - 1, 0 , width -1, 0);
  317. int titleLength = 0;
  318. int xOffset = leftToRight ? 5 : width - 5;
  319. String frameTitle = frame.getTitle();
  320. Icon icon = frame.getFrameIcon();
  321. if ( icon != null ) {
  322. if( !leftToRight )
  323. xOffset -= icon.getIconWidth();
  324. int iconY = ((height / 2) - (icon.getIconHeight() /2));
  325. icon.paintIcon(frame, g, xOffset, iconY);
  326. xOffset += leftToRight ? icon.getIconWidth() + 5 : -5;
  327. }
  328. if(frameTitle != null) {
  329. Font f = getFont();
  330. g.setFont(f);
  331. FontMetrics fm = g.getFontMetrics();
  332. int fHeight = fm.getHeight();
  333. g.setColor(foreground);
  334. int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
  335. Rectangle rect = new Rectangle(0, 0, 0, 0);
  336. if (frame.isIconifiable()) { rect = iconButton.getBounds(); }
  337. else if (frame.isMaximizable()) { rect = maxButton.getBounds(); }
  338. else if (frame.isClosable()) { rect = closeButton.getBounds(); }
  339. int titleW;
  340. if( leftToRight ) {
  341. if (rect.x == 0) {
  342. rect.x = frame.getWidth()-frame.getInsets().right-2;
  343. }
  344. titleW = rect.x - xOffset - 4;
  345. frameTitle = getTitle(frameTitle, fm, titleW);
  346. } else {
  347. titleW = xOffset - rect.x - rect.width - 4;
  348. frameTitle = getTitle(frameTitle, fm, titleW);
  349. xOffset -= SwingUtilities.computeStringWidth(fm, frameTitle);
  350. }
  351. titleLength = SwingUtilities.computeStringWidth(fm, frameTitle);
  352. g.drawString( frameTitle, xOffset, yOffset );
  353. xOffset += leftToRight ? titleLength + 5 : -5;
  354. }
  355. int bumpXOffset;
  356. int bumpLength;
  357. if( leftToRight ) {
  358. bumpLength = width - buttonsWidth - xOffset - 5;
  359. bumpXOffset = xOffset;
  360. } else {
  361. bumpLength = xOffset - buttonsWidth - 5;
  362. bumpXOffset = buttonsWidth + 5;
  363. }
  364. int bumpYOffset = 3;
  365. int bumpHeight = getHeight() - (2 * bumpYOffset);
  366. bumps.setBumpArea( bumpLength, bumpHeight );
  367. bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
  368. }
  369. public void setPalette(boolean b) {
  370. isPalette = b;
  371. if (isPalette) {
  372. closeButton.setIcon(paletteCloseIcon);
  373. if( frame.isMaximizable() )
  374. remove(maxButton);
  375. if( frame.isIconifiable() )
  376. remove(iconButton);
  377. } else {
  378. closeButton.setIcon(closeIcon);
  379. if( frame.isMaximizable() )
  380. add(maxButton);
  381. if( frame.isIconifiable() )
  382. add(iconButton);
  383. }
  384. revalidate();
  385. repaint();
  386. }
  387. /**
  388. * Updates any state dependant upon the JInternalFrame being shown in
  389. * a <code>JOptionPane</code>.
  390. */
  391. private void updateOptionPaneState() {
  392. int type = -2;
  393. boolean closable = wasClosable;
  394. Object obj = frame.getClientProperty("JInternalFrame.messageType");
  395. if (obj == null) {
  396. // Don't change the closable state unless in an JOptionPane.
  397. return;
  398. }
  399. if (obj instanceof Integer) {
  400. type = ((Integer) obj).intValue();
  401. }
  402. switch (type) {
  403. case JOptionPane.ERROR_MESSAGE:
  404. selectedBackgroundKey =
  405. "OptionPane.errorDialog.titlePane.background";
  406. selectedForegroundKey =
  407. "OptionPane.errorDialog.titlePane.foreground";
  408. selectedShadowKey = "OptionPane.errorDialog.titlePane.shadow";
  409. closable = false;
  410. break;
  411. case JOptionPane.QUESTION_MESSAGE:
  412. selectedBackgroundKey =
  413. "OptionPane.questionDialog.titlePane.background";
  414. selectedForegroundKey =
  415. "OptionPane.questionDialog.titlePane.foreground";
  416. selectedShadowKey =
  417. "OptionPane.questionDialog.titlePane.shadow";
  418. closable = false;
  419. break;
  420. case JOptionPane.WARNING_MESSAGE:
  421. selectedBackgroundKey =
  422. "OptionPane.warningDialog.titlePane.background";
  423. selectedForegroundKey =
  424. "OptionPane.warningDialog.titlePane.foreground";
  425. selectedShadowKey = "OptionPane.warningDialog.titlePane.shadow";
  426. closable = false;
  427. break;
  428. case JOptionPane.INFORMATION_MESSAGE:
  429. case JOptionPane.PLAIN_MESSAGE:
  430. selectedBackgroundKey = selectedForegroundKey = selectedShadowKey =
  431. null;
  432. closable = false;
  433. break;
  434. default:
  435. selectedBackgroundKey = selectedForegroundKey = selectedShadowKey =
  436. null;
  437. break;
  438. }
  439. if (closable != frame.isClosable()) {
  440. frame.setClosable(closable);
  441. }
  442. }
  443. }