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