001: /*
002: * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.java.swing.plaf.windows;
027:
028: import sun.swing.SwingUtilities2;
029:
030: import javax.swing.*;
031: import javax.swing.border.*;
032: import javax.swing.UIManager;
033: import javax.swing.plaf.*;
034: import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
035: import java.awt.*;
036: import java.awt.event.*;
037: import java.beans.PropertyChangeEvent;
038: import java.beans.PropertyChangeListener;
039: import java.beans.PropertyVetoException;
040:
041: import static com.sun.java.swing.plaf.windows.TMSchema.*;
042: import static com.sun.java.swing.plaf.windows.XPStyle.Skin;
043:
044: public class WindowsInternalFrameTitlePane extends
045: BasicInternalFrameTitlePane {
046: private Color selectedTitleGradientColor;
047: private Color notSelectedTitleGradientColor;
048: private JPopupMenu systemPopupMenu;
049: private JLabel systemLabel;
050:
051: private Font titleFont;
052: private int titlePaneHeight;
053: private int buttonWidth, buttonHeight;
054: private boolean hotTrackingOn;
055:
056: public WindowsInternalFrameTitlePane(JInternalFrame f) {
057: super (f);
058: }
059:
060: protected void addSubComponents() {
061: add(systemLabel);
062: add(iconButton);
063: add(maxButton);
064: add(closeButton);
065: }
066:
067: protected void installDefaults() {
068: super .installDefaults();
069:
070: titlePaneHeight = UIManager
071: .getInt("InternalFrame.titlePaneHeight");
072: buttonWidth = UIManager
073: .getInt("InternalFrame.titleButtonWidth") - 4;
074: buttonHeight = UIManager
075: .getInt("InternalFrame.titleButtonHeight") - 4;
076:
077: Object obj = UIManager
078: .get("InternalFrame.titleButtonToolTipsOn");
079: hotTrackingOn = (obj instanceof Boolean) ? (Boolean) obj : true;
080:
081: if (XPStyle.getXP() != null) {
082: // Fix for XP bug where sometimes these sizes aren't updated properly
083: // Assume for now that height is correct and derive width using the
084: // ratio from the uxtheme part
085: buttonWidth = buttonHeight;
086: Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON,
087: State.NORMAL);
088: if (d != null && d.width != 0 && d.height != 0) {
089: buttonWidth = (int) ((float) buttonWidth * d.width / d.height);
090: }
091: } else {
092: buttonWidth += 2;
093: selectedTitleGradientColor = UIManager
094: .getColor("InternalFrame.activeTitleGradient");
095: notSelectedTitleGradientColor = UIManager
096: .getColor("InternalFrame.inactiveTitleGradient");
097: Color activeBorderColor = UIManager
098: .getColor("InternalFrame.activeBorderColor");
099: setBorder(BorderFactory.createLineBorder(activeBorderColor,
100: 1));
101: }
102: }
103:
104: protected void uninstallListeners() {
105: // Get around protected method in superclass
106: super .uninstallListeners();
107: }
108:
109: protected void createButtons() {
110: super .createButtons();
111: if (XPStyle.getXP() != null) {
112: iconButton.setContentAreaFilled(false);
113: maxButton.setContentAreaFilled(false);
114: closeButton.setContentAreaFilled(false);
115: }
116: }
117:
118: protected void setButtonIcons() {
119: super .setButtonIcons();
120:
121: if (!hotTrackingOn) {
122: iconButton.setToolTipText(null);
123: maxButton.setToolTipText(null);
124: closeButton.setToolTipText(null);
125: }
126: }
127:
128: public void paintComponent(Graphics g) {
129: XPStyle xp = XPStyle.getXP();
130:
131: paintTitleBackground(g);
132:
133: String title = frame.getTitle();
134: if (title != null) {
135: boolean isSelected = frame.isSelected();
136: Font oldFont = g.getFont();
137: Font newFont = (titleFont != null) ? titleFont : getFont();
138: g.setFont(newFont);
139:
140: // Center text vertically.
141: FontMetrics fm = SwingUtilities2.getFontMetrics(frame, g,
142: newFont);
143: int baseline = (getHeight() + fm.getAscent()
144: - fm.getLeading() - fm.getDescent()) / 2;
145:
146: int titleX;
147: Rectangle r = new Rectangle(0, 0, 0, 0);
148: if (frame.isIconifiable())
149: r = iconButton.getBounds();
150: else if (frame.isMaximizable())
151: r = maxButton.getBounds();
152: else if (frame.isClosable())
153: r = closeButton.getBounds();
154: int titleW;
155:
156: if (WindowsGraphicsUtils.isLeftToRight(frame)) {
157: if (r.x == 0)
158: r.x = frame.getWidth() - frame.getInsets().right;
159: titleX = systemLabel.getX() + systemLabel.getWidth()
160: + 2;
161: if (xp != null) {
162: titleX += 2;
163: }
164: titleW = r.x - titleX - 3;
165: title = getTitle(frame.getTitle(), fm, titleW);
166: } else {
167: titleX = systemLabel.getX() - 2
168: - SwingUtilities2.stringWidth(frame, fm, title);
169: }
170: if (xp != null) {
171: String shadowType = null;
172: if (isSelected) {
173: shadowType = xp.getString(this , Part.WP_CAPTION,
174: State.ACTIVE, Prop.TEXTSHADOWTYPE);
175: }
176: if ("single".equalsIgnoreCase(shadowType)) {
177: Point shadowOffset = xp.getPoint(this ,
178: Part.WP_WINDOW, State.ACTIVE,
179: Prop.TEXTSHADOWOFFSET);
180: Color shadowColor = xp.getColor(this ,
181: Part.WP_WINDOW, State.ACTIVE,
182: Prop.TEXTSHADOWCOLOR, null);
183: if (shadowOffset != null && shadowColor != null) {
184: g.setColor(shadowColor);
185: SwingUtilities2.drawString(frame, g, title,
186: titleX + shadowOffset.x, baseline
187: + shadowOffset.y);
188: }
189: }
190: }
191: g.setColor(isSelected ? selectedTextColor
192: : notSelectedTextColor);
193: SwingUtilities2.drawString(frame, g, title, titleX,
194: baseline);
195: g.setFont(oldFont);
196: }
197: }
198:
199: public Dimension getPreferredSize() {
200: return getMinimumSize();
201: }
202:
203: public Dimension getMinimumSize() {
204: Dimension d = new Dimension(super .getMinimumSize());
205: d.height = titlePaneHeight + 2;
206:
207: XPStyle xp = XPStyle.getXP();
208: if (xp != null) {
209: // Note: Don't know how to calculate height on XP,
210: // the captionbarheight is 25 but native caption is 30 (maximized 26)
211: if (frame.isMaximum()) {
212: d.height -= 1;
213: } else {
214: d.height += 3;
215: }
216: }
217: return d;
218: }
219:
220: protected void paintTitleBackground(Graphics g) {
221: XPStyle xp = XPStyle.getXP();
222: if (xp != null) {
223: Part part = frame.isIcon() ? Part.WP_MINCAPTION
224: : (frame.isMaximum() ? Part.WP_MAXCAPTION
225: : Part.WP_CAPTION);
226: State state = frame.isSelected() ? State.ACTIVE
227: : State.INACTIVE;
228: Skin skin = xp.getSkin(this , part);
229: skin.paintSkin(g, 0, 0, getWidth(), getHeight(), state);
230: } else {
231: Boolean gradientsOn = (Boolean) LookAndFeel
232: .getDesktopPropertyValue(
233: "win.frame.captionGradientsOn", Boolean
234: .valueOf(false));
235: if (gradientsOn.booleanValue() && g instanceof Graphics2D) {
236: Graphics2D g2 = (Graphics2D) g;
237: Paint savePaint = g2.getPaint();
238:
239: boolean isSelected = frame.isSelected();
240: int w = getWidth();
241:
242: if (isSelected) {
243: GradientPaint titleGradient = new GradientPaint(0,
244: 0, selectedTitleColor, (int) (w * .75), 0,
245: selectedTitleGradientColor);
246: g2.setPaint(titleGradient);
247: } else {
248: GradientPaint titleGradient = new GradientPaint(0,
249: 0, notSelectedTitleColor, (int) (w * .75),
250: 0, notSelectedTitleGradientColor);
251: g2.setPaint(titleGradient);
252: }
253: g2.fillRect(0, 0, getWidth(), getHeight());
254: g2.setPaint(savePaint);
255: } else {
256: super .paintTitleBackground(g);
257: }
258: }
259: }
260:
261: protected void assembleSystemMenu() {
262: systemPopupMenu = new JPopupMenu();
263: addSystemMenuItems(systemPopupMenu);
264: enableActions();
265: systemLabel = new JLabel(frame.getFrameIcon()) {
266: protected void paintComponent(Graphics g) {
267: int x = 0;
268: int y = 0;
269: int w = getWidth();
270: int h = getHeight();
271: g = g.create(); // Create scratch graphics
272: if (isOpaque()) {
273: g.setColor(getBackground());
274: g.fillRect(0, 0, w, h);
275: }
276: Icon icon = getIcon();
277: int iconWidth = 0;
278: int iconHeight = 0;
279: if (icon != null
280: && (iconWidth = icon.getIconWidth()) > 0
281: && (iconHeight = icon.getIconHeight()) > 0) {
282:
283: // Set drawing scale to make icon scale to our desired size
284: double drawScale;
285: if (iconWidth > iconHeight) {
286: // Center icon vertically
287: y = (h - w * iconHeight / iconWidth) / 2;
288: drawScale = w / (double) iconWidth;
289: } else {
290: // Center icon horizontally
291: x = (w - h * iconWidth / iconHeight) / 2;
292: drawScale = h / (double) iconHeight;
293: }
294: ((Graphics2D) g).translate(x, y);
295: ((Graphics2D) g).scale(drawScale, drawScale);
296: icon.paintIcon(this , g, 0, 0);
297: }
298: g.dispose();
299: }
300: };
301: systemLabel.addMouseListener(new MouseAdapter() {
302: public void mouseClicked(MouseEvent e) {
303: if (e.getClickCount() == 2 && frame.isClosable()
304: && !frame.isIcon()) {
305: systemPopupMenu.setVisible(false);
306: frame.doDefaultCloseAction();
307: } else {
308: super .mouseClicked(e);
309: }
310: }
311:
312: public void mousePressed(MouseEvent e) {
313: try {
314: frame.setSelected(true);
315: } catch (PropertyVetoException pve) {
316: }
317: showSystemPopupMenu(e.getComponent());
318: }
319: });
320: }
321:
322: protected void addSystemMenuItems(JPopupMenu menu) {
323: JMenuItem mi = (JMenuItem) menu.add(restoreAction);
324: mi.setMnemonic('R');
325: mi = (JMenuItem) menu.add(moveAction);
326: mi.setMnemonic('M');
327: mi = (JMenuItem) menu.add(sizeAction);
328: mi.setMnemonic('S');
329: mi = (JMenuItem) menu.add(iconifyAction);
330: mi.setMnemonic('n');
331: mi = (JMenuItem) menu.add(maximizeAction);
332: mi.setMnemonic('x');
333: systemPopupMenu.add(new JSeparator());
334: mi = (JMenuItem) menu.add(closeAction);
335: mi.setMnemonic('C');
336: }
337:
338: protected void showSystemMenu() {
339: showSystemPopupMenu(systemLabel);
340: }
341:
342: private void showSystemPopupMenu(Component invoker) {
343: Dimension dim = new Dimension();
344: Border border = frame.getBorder();
345: if (border != null) {
346: dim.width += border.getBorderInsets(frame).left
347: + border.getBorderInsets(frame).right;
348: dim.height += border.getBorderInsets(frame).bottom
349: + border.getBorderInsets(frame).top;
350: }
351: if (!frame.isIcon()) {
352: systemPopupMenu.show(invoker, getX() - dim.width, getY()
353: + getHeight() - dim.height);
354: } else {
355: systemPopupMenu.show(invoker, getX() - dim.width, getY()
356: - systemPopupMenu.getPreferredSize().height
357: - dim.height);
358: }
359: }
360:
361: protected PropertyChangeListener createPropertyChangeListener() {
362: return new WindowsPropertyChangeHandler();
363: }
364:
365: protected LayoutManager createLayout() {
366: return new WindowsTitlePaneLayout();
367: }
368:
369: public class WindowsTitlePaneLayout extends
370: BasicInternalFrameTitlePane.TitlePaneLayout {
371: private Insets captionMargin = null;
372: private Insets contentMargin = null;
373: private XPStyle xp = XPStyle.getXP();
374:
375: WindowsTitlePaneLayout() {
376: if (xp != null) {
377: Component c = WindowsInternalFrameTitlePane.this ;
378: captionMargin = xp.getMargin(c, Part.WP_CAPTION, null,
379: Prop.CAPTIONMARGINS);
380: contentMargin = xp.getMargin(c, Part.WP_CAPTION, null,
381: Prop.CONTENTMARGINS);
382: }
383: if (captionMargin == null) {
384: captionMargin = new Insets(0, 2, 0, 2);
385: }
386: if (contentMargin == null) {
387: contentMargin = new Insets(0, 0, 0, 0);
388: }
389: }
390:
391: private int layoutButton(JComponent button, Part part, int x,
392: int y, int w, int h, int gap, boolean leftToRight) {
393: if (!leftToRight) {
394: x -= w;
395: }
396: button.setBounds(x, y, w, h);
397: if (leftToRight) {
398: x += w + 2;
399: } else {
400: x -= 2;
401: }
402: return x;
403: }
404:
405: public void layoutContainer(Container c) {
406: boolean leftToRight = WindowsGraphicsUtils
407: .isLeftToRight(frame);
408: int x, y;
409: int w = getWidth();
410: int h = getHeight();
411:
412: // System button
413: // Note: this icon is square, but the buttons aren't always.
414: int iconSize = (xp != null) ? (h - 2) * 6 / 10 : h - 4;
415: if (xp != null) {
416: x = (leftToRight) ? captionMargin.left + 2 : w
417: - captionMargin.right - 2;
418: } else {
419: x = (leftToRight) ? captionMargin.left : w
420: - captionMargin.right;
421: }
422: y = (h - iconSize) / 2;
423: layoutButton(systemLabel, Part.WP_SYSBUTTON, x, y,
424: iconSize, iconSize, 0, leftToRight);
425:
426: // Right hand buttons
427: if (xp != null) {
428: x = (leftToRight) ? w - captionMargin.right - 2
429: : captionMargin.left + 2;
430: y = 1; // XP seems to ignore margins and offset here
431: if (frame.isMaximum()) {
432: y += 1;
433: } else {
434: y += 5;
435: }
436: } else {
437: x = (leftToRight) ? w - captionMargin.right
438: : captionMargin.left;
439: y = (h - buttonHeight) / 2;
440: }
441:
442: if (frame.isClosable()) {
443: x = layoutButton(closeButton, Part.WP_CLOSEBUTTON, x,
444: y, buttonWidth, buttonHeight, 2, !leftToRight);
445: }
446:
447: if (frame.isMaximizable()) {
448: x = layoutButton(maxButton, Part.WP_MAXBUTTON, x, y,
449: buttonWidth, buttonHeight,
450: (xp != null) ? 2 : 0, !leftToRight);
451: }
452:
453: if (frame.isIconifiable()) {
454: layoutButton(iconButton, Part.WP_MINBUTTON, x, y,
455: buttonWidth, buttonHeight, 0, !leftToRight);
456: }
457: }
458: } // end WindowsTitlePaneLayout
459:
460: public class WindowsPropertyChangeHandler extends
461: PropertyChangeHandler {
462: public void propertyChange(PropertyChangeEvent evt) {
463: String prop = (String) evt.getPropertyName();
464:
465: // Update the internal frame icon for the system menu.
466: if (JInternalFrame.FRAME_ICON_PROPERTY.equals(prop)
467: && systemLabel != null) {
468: systemLabel.setIcon(frame.getFrameIcon());
469: }
470:
471: super .propertyChange(evt);
472: }
473: }
474:
475: /**
476: * A versatile Icon implementation which can take an array of Icon
477: * instances (typically <code>ImageIcon</code>s) and choose one that gives the best
478: * quality for a given Graphics2D scale factor when painting.
479: * <p>
480: * The class is public so it can be instantiated by UIDefaults.ProxyLazyValue.
481: * <p>
482: * Note: We assume here that icons are square.
483: */
484: public static class ScalableIconUIResource implements Icon,
485: UIResource {
486: // We can use an arbitrary size here because we scale to it in paintIcon()
487: private static final int SIZE = 16;
488:
489: private Icon[] icons;
490:
491: /**
492: * @params objects an array of Icon or UIDefaults.LazyValue
493: * <p>
494: * The constructor is public so it can be called by UIDefaults.ProxyLazyValue.
495: */
496: public ScalableIconUIResource(Object[] objects) {
497: this .icons = new Icon[objects.length];
498:
499: for (int i = 0; i < objects.length; i++) {
500: if (objects[i] instanceof UIDefaults.LazyValue) {
501: icons[i] = (Icon) ((UIDefaults.LazyValue) objects[i])
502: .createValue(null);
503: } else {
504: icons[i] = (Icon) objects[i];
505: }
506: }
507: }
508:
509: /**
510: * @return the <code>Icon</code> closest to the requested size
511: */
512: protected Icon getBestIcon(int size) {
513: if (icons != null && icons.length > 0) {
514: int bestIndex = 0;
515: int minDiff = Integer.MAX_VALUE;
516: for (int i = 0; i < icons.length; i++) {
517: Icon icon = icons[i];
518: int iconSize;
519: if (icon != null
520: && (iconSize = icon.getIconWidth()) > 0) {
521: int diff = Math.abs(iconSize - size);
522: if (diff < minDiff) {
523: minDiff = diff;
524: bestIndex = i;
525: }
526: }
527: }
528: return icons[bestIndex];
529: } else {
530: return null;
531: }
532: }
533:
534: public void paintIcon(Component c, Graphics g, int x, int y) {
535: Graphics2D g2d = (Graphics2D) g.create();
536: // Calculate how big our drawing area is in pixels
537: // Assume we are square
538: int size = getIconWidth();
539: double scale = g2d.getTransform().getScaleX();
540: Icon icon = getBestIcon((int) (size * scale));
541: int iconSize;
542: if (icon != null && (iconSize = icon.getIconWidth()) > 0) {
543: // Set drawing scale to make icon act true to our reported size
544: double drawScale = size / (double) iconSize;
545: g2d.translate(x, y);
546: g2d.scale(drawScale, drawScale);
547: icon.paintIcon(c, g2d, 0, 0);
548: }
549: g2d.dispose();
550: }
551:
552: public int getIconWidth() {
553: return SIZE;
554: }
555:
556: public int getIconHeight() {
557: return SIZE;
558: }
559: }
560: }
|