1. /*
  2. * @(#)WindowsInternalFrameTitlePane.java 1.17 04/04/15
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.windows;
  8. import com.sun.java.swing.SwingUtilities2;
  9. import javax.swing.*;
  10. import javax.swing.border.*;
  11. import javax.swing.UIManager;
  12. import javax.swing.plaf.*;
  13. import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
  14. import java.awt.*;
  15. import java.awt.event.*;
  16. import java.beans.PropertyChangeEvent;
  17. import java.beans.PropertyChangeListener;
  18. public class WindowsInternalFrameTitlePane extends BasicInternalFrameTitlePane {
  19. private Color selectedTitleGradientColor;
  20. private Color notSelectedTitleGradientColor;
  21. private JPopupMenu systemPopupMenu;
  22. private JLabel systemLabel;
  23. private Font titleFont;
  24. private int titlePaneHeight;
  25. private int buttonWidth, buttonHeight;
  26. public WindowsInternalFrameTitlePane(JInternalFrame f) {
  27. super(f);
  28. }
  29. protected void addSubComponents() {
  30. add(systemLabel);
  31. add(iconButton);
  32. add(maxButton);
  33. add(closeButton);
  34. }
  35. protected void installDefaults() {
  36. super.installDefaults();
  37. titlePaneHeight = UIManager.getInt("InternalFrame.titlePaneHeight");
  38. buttonWidth = UIManager.getInt("InternalFrame.titleButtonWidth") - 4;
  39. buttonHeight = UIManager.getInt("InternalFrame.titleButtonHeight") - 4;
  40. if (XPStyle.getXP() != null) {
  41. // Fix for XP bug where sometimes these sizes aren't updated properly
  42. // Assume for now that XP buttons are always square
  43. buttonWidth = buttonHeight;
  44. } else {
  45. buttonWidth += 2;
  46. selectedTitleGradientColor =
  47. UIManager.getColor("InternalFrame.activeTitleGradient");
  48. notSelectedTitleGradientColor =
  49. UIManager.getColor("InternalFrame.inactiveTitleGradient");
  50. Color activeBorderColor =
  51. UIManager.getColor("InternalFrame.activeBorderColor");
  52. setBorder(BorderFactory.createLineBorder(activeBorderColor, 1));
  53. }
  54. }
  55. protected void uninstallListeners() {
  56. // Get around protected method in superclass
  57. super.uninstallListeners();
  58. }
  59. protected void createButtons() {
  60. super.createButtons();
  61. if (XPStyle.getXP() != null) {
  62. iconButton.setContentAreaFilled(false);
  63. maxButton.setContentAreaFilled(false);
  64. closeButton.setContentAreaFilled(false);
  65. }
  66. }
  67. public void paintComponent(Graphics g) {
  68. XPStyle xp = XPStyle.getXP();
  69. paintTitleBackground(g);
  70. String title = frame.getTitle();
  71. if (title != null) {
  72. boolean isSelected = frame.isSelected();
  73. Font oldFont = g.getFont();
  74. Font newFont = (titleFont != null) ? titleFont : getFont();
  75. g.setFont(newFont);
  76. // Center text vertically.
  77. FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g, newFont);
  78. int baseline = (getHeight() + fm.getAscent() - fm.getLeading() -
  79. fm.getDescent()) / 2;
  80. int titleX;
  81. Rectangle r = new Rectangle(0, 0, 0, 0);
  82. if (frame.isIconifiable()) r = iconButton.getBounds();
  83. else if (frame.isMaximizable()) r = maxButton.getBounds();
  84. else if (frame.isClosable()) r = closeButton.getBounds();
  85. int titleW;
  86. if(WindowsGraphicsUtils.isLeftToRight(frame) ) {
  87. if (r.x == 0) r.x = frame.getWidth()-frame.getInsets().right;
  88. titleX = systemLabel.getX() + systemLabel.getWidth() + 2;
  89. if (xp != null) {
  90. titleX += 2;
  91. }
  92. titleW = r.x - titleX - 3;
  93. title = getTitle(frame.getTitle(), fm, titleW);
  94. } else {
  95. titleX = systemLabel.getX() - 2
  96. - SwingUtilities2.stringWidth(frame,fm,title);
  97. }
  98. if (xp != null) {
  99. String shadowType = null;
  100. if (isSelected) {
  101. shadowType = xp.getString("window.caption", "active", "textshadowtype");
  102. }
  103. if ("single".equalsIgnoreCase(shadowType)) {
  104. Point shadowOffset = xp.getPoint("window.textshadowoffset");
  105. Color shadowColor = xp.getColor("window.textshadowcolor", null);
  106. if (shadowOffset != null && shadowColor != null) {
  107. g.setColor(shadowColor);
  108. SwingUtilities2.drawString(frame, g, title,
  109. titleX + shadowOffset.x,
  110. baseline + shadowOffset.y);
  111. }
  112. }
  113. }
  114. g.setColor(isSelected ? selectedTextColor : notSelectedTextColor);
  115. SwingUtilities2.drawString(frame, g, title, titleX, baseline);
  116. g.setFont(oldFont);
  117. }
  118. }
  119. public Dimension getPreferredSize() {
  120. return getMinimumSize();
  121. }
  122. public Dimension getMinimumSize() {
  123. Dimension d = new Dimension(super.getMinimumSize());
  124. d.height = titlePaneHeight + 2;
  125. XPStyle xp = XPStyle.getXP();
  126. if (xp != null) {
  127. // Note: Don't know how to calculate height on XP,
  128. // the captionbarheight is 25 but native caption is 30 (maximized 26)
  129. if (frame.isMaximum()) {
  130. d.height -= 1;
  131. } else {
  132. d.height += 3;
  133. }
  134. }
  135. return d;
  136. }
  137. protected void paintTitleBackground(Graphics g) {
  138. XPStyle xp = XPStyle.getXP();
  139. if (xp != null) {
  140. XPStyle.Skin skin = xp.getSkin(frame.isIcon() ? "window.mincaption"
  141. : (frame.isMaximum() ? "window.maxcaption"
  142. : "window.caption"));
  143. skin.paintSkin(g, 0, 0, getSize().width, getSize().height, frame.isSelected() ? 0 : 1);
  144. } else {
  145. Boolean gradientsOn = (Boolean)LookAndFeel.getDesktopPropertyValue(
  146. "win.frame.captionGradientsOn", Boolean.valueOf(false));
  147. if (gradientsOn.booleanValue() && g instanceof Graphics2D) {
  148. Graphics2D g2 = (Graphics2D)g;
  149. Paint savePaint = g2.getPaint();
  150. boolean isSelected = frame.isSelected();
  151. int w = getWidth();
  152. if (isSelected) {
  153. GradientPaint titleGradient = new GradientPaint(0,0,
  154. selectedTitleColor,
  155. (int)(w*.75),0,
  156. selectedTitleGradientColor);
  157. g2.setPaint(titleGradient);
  158. } else {
  159. GradientPaint titleGradient = new GradientPaint(0,0,
  160. notSelectedTitleColor,
  161. (int)(w*.75),0,
  162. notSelectedTitleGradientColor);
  163. g2.setPaint(titleGradient);
  164. }
  165. g2.fillRect(0, 0, getWidth(), getHeight());
  166. g2.setPaint(savePaint);
  167. } else {
  168. super.paintTitleBackground(g);
  169. }
  170. }
  171. }
  172. protected void assembleSystemMenu() {
  173. systemPopupMenu = new JPopupMenu();
  174. addSystemMenuItems(systemPopupMenu);
  175. enableActions();
  176. systemLabel = new JLabel(frame.getFrameIcon()) {
  177. protected void paintComponent(Graphics g) {
  178. int x = 0;
  179. int y = 0;
  180. int w = getWidth();
  181. int h = getHeight();
  182. g = g.create(); // Create scratch graphics
  183. if (isOpaque()) {
  184. g.setColor(getBackground());
  185. g.fillRect(0, 0, w, h);
  186. }
  187. Icon icon = getIcon();
  188. int iconWidth = 0;
  189. int iconHeight = 0;
  190. if (icon != null &&
  191. (iconWidth = icon.getIconWidth()) > 0 &&
  192. (iconHeight = icon.getIconHeight()) > 0) {
  193. // Set drawing scale to make icon scale to our desired size
  194. double drawScale;
  195. if (iconWidth > iconHeight) {
  196. // Center icon vertically
  197. y = (h - w*iconHeighticonWidth) / 2;
  198. drawScale = w / (double)iconWidth;
  199. } else {
  200. // Center icon horizontally
  201. x = (w - h*iconWidthiconHeight) / 2;
  202. drawScale = h / (double)iconHeight;
  203. }
  204. ((Graphics2D)g).translate(x, y);
  205. ((Graphics2D)g).scale(drawScale, drawScale);
  206. icon.paintIcon(this, g, 0, 0);
  207. }
  208. g.dispose();
  209. }
  210. };
  211. systemLabel.addMouseListener(new MouseAdapter() {
  212. public void mousePressed(MouseEvent e) {
  213. showSystemPopupMenu(e.getComponent());
  214. }
  215. });
  216. }
  217. protected void addSystemMenuItems(JPopupMenu menu) {
  218. JMenuItem mi = (JMenuItem)menu.add(restoreAction);
  219. mi.setMnemonic('R');
  220. mi = (JMenuItem)menu.add(moveAction);
  221. mi.setMnemonic('M');
  222. mi = (JMenuItem)menu.add(sizeAction);
  223. mi.setMnemonic('S');
  224. mi = (JMenuItem)menu.add(iconifyAction);
  225. mi.setMnemonic('n');
  226. mi = (JMenuItem)menu.add(maximizeAction);
  227. mi.setMnemonic('x');
  228. systemPopupMenu.add(new JSeparator());
  229. mi = (JMenuItem)menu.add(closeAction);
  230. mi.setMnemonic('C');
  231. }
  232. protected void showSystemMenu(){
  233. showSystemPopupMenu(systemLabel);
  234. }
  235. private void showSystemPopupMenu(Component invoker){
  236. Dimension dim = new Dimension();
  237. Border border = frame.getBorder();
  238. if (border != null) {
  239. dim.width += border.getBorderInsets(frame).left +
  240. border.getBorderInsets(frame).right;
  241. dim.height += border.getBorderInsets(frame).bottom +
  242. border.getBorderInsets(frame).top;
  243. }
  244. if (!frame.isIcon()) {
  245. systemPopupMenu.show(invoker,
  246. getX() - dim.width,
  247. getY() + getHeight() - dim.height);
  248. } else {
  249. systemPopupMenu.show(invoker,
  250. getX() - dim.width,
  251. getY() - systemPopupMenu.getPreferredSize().height -
  252. dim.height);
  253. }
  254. }
  255. protected PropertyChangeListener createPropertyChangeListener() {
  256. return new WindowsPropertyChangeHandler();
  257. }
  258. protected LayoutManager createLayout() {
  259. return new WindowsTitlePaneLayout();
  260. }
  261. public class WindowsTitlePaneLayout extends BasicInternalFrameTitlePane.TitlePaneLayout {
  262. private Insets captionMargin = null;
  263. private Insets contentMargin = null;
  264. private XPStyle xp = XPStyle.getXP();
  265. WindowsTitlePaneLayout() {
  266. if (xp != null) {
  267. captionMargin = xp.getMargin("window.caption.captionmargins");
  268. contentMargin = xp.getMargin("window.caption.contentmargins");
  269. }
  270. if (captionMargin == null) {
  271. captionMargin = new Insets(0, 2, 0, 2);
  272. }
  273. if (contentMargin == null) {
  274. contentMargin = new Insets(0, 0, 0, 0);
  275. }
  276. }
  277. private int layoutButton(JComponent button, String category,
  278. int x, int y, int w, int h, int gap,
  279. boolean leftToRight) {
  280. if (!leftToRight) {
  281. x -= w;
  282. }
  283. button.setBounds(x, y, w, h);
  284. if (leftToRight) {
  285. x += w + 2;
  286. } else {
  287. x -= 2;
  288. }
  289. return x;
  290. }
  291. public void layoutContainer(Container c) {
  292. boolean leftToRight = WindowsGraphicsUtils.isLeftToRight(frame);
  293. int x, y;
  294. int w = getWidth();
  295. int h = getHeight();
  296. // System button
  297. // Note: this icon is square, but the buttons aren't always.
  298. int iconSize = (xp != null) ? (h-2)*6/10 : h-4;
  299. if (xp != null) {
  300. x = (leftToRight) ? captionMargin.left + 2 : w - captionMargin.right - 2;
  301. } else {
  302. x = (leftToRight) ? captionMargin.left : w - captionMargin.right;
  303. }
  304. y = (h - iconSize) / 2;
  305. layoutButton(systemLabel, "window.sysbutton",
  306. x, y, iconSize, iconSize, 0,
  307. leftToRight);
  308. // Right hand buttons
  309. if (xp != null) {
  310. x = (leftToRight) ? w - captionMargin.right - 2 : captionMargin.left + 2;
  311. y = 1; // XP seems to ignore margins and offset here
  312. if (frame.isMaximum()) {
  313. y += 1;
  314. } else {
  315. y += 5;
  316. }
  317. } else {
  318. x = (leftToRight) ? w - captionMargin.right : captionMargin.left;
  319. y = (h - buttonHeight) / 2;
  320. }
  321. if(frame.isClosable()) {
  322. x = layoutButton(closeButton, "window.closebutton",
  323. x, y, buttonWidth, buttonHeight, 2,
  324. !leftToRight);
  325. }
  326. if(frame.isMaximizable()) {
  327. x = layoutButton(maxButton, "window.maxbutton",
  328. x, y, buttonWidth, buttonHeight, (xp != null) ? 2 : 0,
  329. !leftToRight);
  330. }
  331. if(frame.isIconifiable()) {
  332. layoutButton(iconButton, "window.minbutton",
  333. x, y, buttonWidth, buttonHeight, 0,
  334. !leftToRight);
  335. }
  336. }
  337. } // end WindowsTitlePaneLayout
  338. public class WindowsPropertyChangeHandler extends PropertyChangeHandler {
  339. public void propertyChange(PropertyChangeEvent evt) {
  340. String prop = (String)evt.getPropertyName();
  341. // Update the internal frame icon for the system menu.
  342. if (JInternalFrame.FRAME_ICON_PROPERTY.equals(prop) &&
  343. systemLabel != null) {
  344. systemLabel.setIcon(frame.getFrameIcon());
  345. }
  346. super.propertyChange(evt);
  347. }
  348. }
  349. /**
  350. * A versatile Icon implementation which can take an array of Icon
  351. * instances (typically <code>ImageIcon</code>s) and choose one that gives the best
  352. * quality for a given Graphics2D scale factor when painting.
  353. * <p>
  354. * The class is public so it can be instantiated by UIDefaults.ProxyLazyValue.
  355. * <p>
  356. * Note: We assume here that icons are square.
  357. */
  358. public static class ScalableIconUIResource implements Icon, UIResource {
  359. // We can use an arbitrary size here because we scale to it in paintIcon()
  360. private static final int SIZE = 16;
  361. private Icon[] icons;
  362. /**
  363. * @params objects an array of Icon or UIDefaults.LazyValue
  364. * <p>
  365. * The constructor is public so it can be called by UIDefaults.ProxyLazyValue.
  366. */
  367. public ScalableIconUIResource(Object[] objects) {
  368. this.icons = new Icon[objects.length];
  369. for (int i = 0; i < objects.length; i++) {
  370. if (objects[i] instanceof UIDefaults.LazyValue) {
  371. icons[i] = (Icon)((UIDefaults.LazyValue)objects[i]).createValue(null);
  372. } else {
  373. icons[i] = (Icon)objects[i];
  374. }
  375. }
  376. }
  377. /**
  378. * @return the <code>Icon</code> closest to the requested size
  379. */
  380. protected Icon getBestIcon(int size) {
  381. if (icons != null && icons.length > 0) {
  382. int bestIndex = 0;
  383. int minDiff = Integer.MAX_VALUE;
  384. for (int i=0; i < icons.length; i++) {
  385. Icon icon = icons[i];
  386. int iconSize;
  387. if (icon != null && (iconSize = icon.getIconWidth()) > 0) {
  388. int diff = Math.abs(iconSize - size);
  389. if (diff < minDiff) {
  390. minDiff = diff;
  391. bestIndex = i;
  392. }
  393. }
  394. }
  395. return icons[bestIndex];
  396. } else {
  397. return null;
  398. }
  399. }
  400. public void paintIcon(Component c, Graphics g, int x, int y) {
  401. Graphics2D g2d = (Graphics2D)g.create();
  402. // Calculate how big our drawing area is in pixels
  403. // Assume we are square
  404. int size = getIconWidth();
  405. double scale = g2d.getTransform().getScaleX();
  406. Icon icon = getBestIcon((int)(size * scale));
  407. int iconSize;
  408. if (icon != null && (iconSize = icon.getIconWidth()) > 0) {
  409. // Set drawing scale to make icon act true to our reported size
  410. double drawScale = size / (double)iconSize;
  411. g2d.translate(x, y);
  412. g2d.scale(drawScale, drawScale);
  413. icon.paintIcon(c, g2d, 0, 0);
  414. }
  415. g2d.dispose();
  416. }
  417. public int getIconWidth() {
  418. return SIZE;
  419. }
  420. public int getIconHeight() {
  421. return SIZE;
  422. }
  423. }
  424. }