001: /*
002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * o Redistributions of source code must retain the above copyright notice,
008: * this list of conditions and the following disclaimer.
009: *
010: * o Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
015: * its contributors may be used to endorse or promote products derived
016: * from this software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030: package org.jvnet.substance.utils;
031:
032: import java.awt.*;
033: import java.awt.event.ActionEvent;
034: import java.awt.event.ActionListener;
035: import java.beans.PropertyChangeEvent;
036: import java.beans.PropertyChangeListener;
037:
038: import javax.swing.*;
039: import javax.swing.JInternalFrame.JDesktopIcon;
040: import javax.swing.plaf.UIResource;
041: import javax.swing.plaf.basic.BasicInternalFrameTitlePane;
042:
043: import org.jvnet.lafwidget.layout.TransitionLayout;
044: import org.jvnet.substance.SubstanceButtonUI;
045: import org.jvnet.substance.SubstanceLookAndFeel;
046: import org.jvnet.substance.painter.decoration.DecorationAreaType;
047: import org.jvnet.substance.painter.decoration.SubstanceDecorationUtilities;
048: import org.jvnet.substance.painter.text.SubstanceTextPainter;
049: import org.jvnet.substance.theme.SubstanceShiftTheme;
050: import org.jvnet.substance.theme.SubstanceTheme;
051: import org.jvnet.substance.utils.icon.SubstanceIconFactory;
052: import org.jvnet.substance.utils.icon.TransitionAwareIcon;
053:
054: /**
055: * UI for internal frame title pane in <b>Substance </b> look and feel.
056: *
057: * @author Kirill Grouchnikov
058: */
059: public class SubstanceInternalFrameTitlePane extends
060: BasicInternalFrameTitlePane {
061: /**
062: * Listens on the changes to the internal frame title.
063: */
064: protected PropertyChangeListener substancePropertyListener;
065:
066: /**
067: * Listens to the changes to the
068: * {@link SubstanceLookAndFeel#WINDOW_MODIFIED} property on the internal
069: * frame and its root pane.
070: */
071: protected PropertyChangeListener substanceWinModifiedListener;
072:
073: /**
074: * Client property to mark an internal frame as being iconified.
075: */
076: protected static final String ICONIFYING = "substance.internal.internalTitleFramePane.iconifying";
077:
078: /**
079: * Client property to mark a title pane as uninstalled.
080: */
081: protected static final String UNINSTALLED = "substance.internal.internalTitleFramePane.uninstalled";
082:
083: /**
084: * Simple constructor.
085: *
086: * @param f
087: * Associated internal frame.
088: */
089: public SubstanceInternalFrameTitlePane(JInternalFrame f) {
090: super (f);
091: this .setToolTipText(f.getTitle());
092: SubstanceDecorationUtilities.setDecorationType(this ,
093: DecorationAreaType.SECONDARY_TITLE_PANE);
094: }
095:
096: /*
097: * (non-Javadoc)
098: *
099: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#installDefaults()
100: */
101: @Override
102: protected void installDefaults() {
103: super .installDefaults();
104: this .setForeground(SubstanceLookAndFeel.getTheme()
105: .getActiveTitlePaneTheme().getForegroundColor());
106: }
107:
108: /*
109: * (non-Javadoc)
110: *
111: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#installListeners()
112: */
113: @Override
114: protected void installListeners() {
115: super .installListeners();
116: this .substancePropertyListener = new PropertyChangeListener() {
117: public void propertyChange(PropertyChangeEvent evt) {
118: if (JInternalFrame.TITLE_PROPERTY.equals(evt
119: .getPropertyName())) {
120: SubstanceInternalFrameTitlePane.this
121: .setToolTipText((String) evt.getNewValue());
122: }
123: }
124: };
125: this .frame
126: .addPropertyChangeListener(this .substancePropertyListener);
127:
128: // Property change listener for pulsating close button
129: // when window has been marked as changed.
130: this .substanceWinModifiedListener = new PropertyChangeListener() {
131: public void propertyChange(PropertyChangeEvent evt) {
132: if (SubstanceLookAndFeel.WINDOW_MODIFIED.equals(evt
133: .getPropertyName())) {
134: syncCloseButtonTooltip();
135: // if (Boolean.TRUE.equals(evt.getNewValue())) {
136: // SubstanceInternalFrameTitlePane.this.closeButton
137: // .setToolTipText(SubstanceLookAndFeel
138: // .getLabelBundle().getString(
139: // "SystemMenu.close")
140: // + " ["
141: // + SubstanceLookAndFeel
142: // .getLabelBundle()
143: // .getString(
144: // "Tooltip.contentsNotSaved")
145: // + "]");
146: // } else {
147: // SubstanceInternalFrameTitlePane.this.closeButton
148: // .setToolTipText(SubstanceLookAndFeel
149: // .getLabelBundle().getString(
150: // "SystemMenu.close"));
151: // }
152: }
153: }
154: };
155: // Wire it on the frame itself and its root pane.
156: this .frame
157: .addPropertyChangeListener(this .substanceWinModifiedListener);
158: this .frame.getRootPane().addPropertyChangeListener(
159: this .substanceWinModifiedListener);
160: }
161:
162: /*
163: * (non-Javadoc)
164: *
165: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#uninstallListeners()
166: */
167: @Override
168: public void uninstallListeners() {
169: this .frame
170: .removePropertyChangeListener(this .substancePropertyListener);
171: this .substancePropertyListener = null;
172:
173: this .frame
174: .removePropertyChangeListener(this .substanceWinModifiedListener);
175: this .frame.getRootPane().removePropertyChangeListener(
176: this .substanceWinModifiedListener);
177: this .substanceWinModifiedListener = null;
178:
179: // if ((this.menuBar != null) && (this.menuBar.getMenuCount() > 0)) {
180: // if (this.getParent() instanceof JInternalFrame)
181: // this.menuBar.getUI().uninstallUI(this.menuBar);
182: // SubstanceTitlePane.uninstallMenu(this.menuBar.getMenu(0));
183: // }
184:
185: super .uninstallListeners();
186: }
187:
188: /**
189: * Uninstalls <code>this</code> title pane.
190: */
191: public void uninstall() {
192: if ((this .menuBar != null) && (this .menuBar.getMenuCount() > 0)) {
193: this .menuBar.getUI().uninstallUI(this .menuBar);
194: SubstanceCoreUtilities.uninstallMenu(this .menuBar
195: .getMenu(0));
196: this .remove(menuBar);
197: }
198: this .uninstallListeners();
199: this .putClientProperty(UNINSTALLED, Boolean.TRUE);
200: }
201:
202: /*
203: * (non-Javadoc)
204: *
205: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#enableActions()
206: */
207: @Override
208: protected void enableActions() {
209: super .enableActions();
210:
211: if (!this .frame.isIcon()) {
212: if (this .maxButton != null)
213: this .maxButton.setEnabled(this .maximizeAction
214: .isEnabled()
215: || this .restoreAction.isEnabled());
216: if (this .iconButton != null)
217: this .iconButton.setEnabled(this .iconifyAction
218: .isEnabled());
219: }
220: }
221:
222: // protected static Map<String, BufferedImage> imageCache = new
223: // LinkedHashMap<String, BufferedImage>() {
224: // @Override
225: // protected boolean removeEldestEntry(Entry<String, BufferedImage> eldest)
226: // {
227: // return this.size() > 50;
228: // }
229: // };
230: //
231: /*
232: * (non-Javadoc)
233: *
234: * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
235: */
236: @Override
237: public void paintComponent(Graphics g) {
238: // if (this.isPalette) {
239: // this.paintPalette(g);
240: // return;
241: // }
242: Graphics2D graphics = (Graphics2D) g.create();
243: // Desktop icon is translucent.
244: final float coef = (this .getParent() instanceof JDesktopIcon) ? 0.6f
245: : 1.0f;
246: graphics.setComposite(TransitionLayout.getAlphaComposite(
247: this .frame, coef, g));
248:
249: boolean isSelected = this .frame.isSelected();
250: boolean leftToRight = this .frame.getComponentOrientation()
251: .isLeftToRight();
252:
253: final int width = this .getWidth();
254: final int height = this .getHeight() + 2;
255:
256: SubstanceTheme theme = isSelected ? SubstanceLookAndFeel
257: .getTheme().getActiveTitlePaneTheme()
258: : SubstanceLookAndFeel.getTheme()
259: .getDefaultTitlePaneTheme();
260: JInternalFrame hostFrame = (JInternalFrame) SwingUtilities
261: .getAncestorOfClass(JInternalFrame.class, this );
262: JComponent hostForColorization = hostFrame;
263: if (hostFrame == null) {
264: // try desktop icon
265: JDesktopIcon desktopIcon = (JDesktopIcon) SwingUtilities
266: .getAncestorOfClass(JDesktopIcon.class, this );
267: if (desktopIcon != null)
268: hostFrame = desktopIcon.getInternalFrame();
269: hostForColorization = desktopIcon;
270: }
271: if ((hostFrame != null)
272: && SubstanceCoreUtilities.hasColorization(this )) {
273: Color backgr = hostFrame.getBackground();
274: if (!(backgr instanceof UIResource)) {
275: double colorization = SubstanceCoreUtilities
276: .getColorizationFactor(hostForColorization);
277: theme = SubstanceShiftTheme.getShiftedTheme(theme,
278: backgr, colorization, null, 0.0);
279: }
280: }
281: String theTitle = this .frame.getTitle();
282:
283: // offset of border
284: int xOffset = 0;
285: int leftEnd;
286: int rightEnd;
287:
288: if (leftToRight) {
289: xOffset = 5;
290: Icon icon = this .frame.getFrameIcon();
291: if (icon != null) {
292: xOffset += icon.getIconWidth() + 5;
293: }
294:
295: leftEnd = (this .menuBar == null) ? 0 : (this .menuBar
296: .getWidth() + 5);
297: xOffset += leftEnd;
298: if (icon != null)
299: leftEnd += (icon.getIconWidth() + 5);
300:
301: rightEnd = width - 5;
302:
303: // find the leftmost button for the right end
304: AbstractButton leftmostButton = null;
305: if (this .frame.isIconifiable()) {
306: leftmostButton = this .iconButton;
307: } else {
308: if (this .frame.isMaximizable()) {
309: leftmostButton = this .maxButton;
310: } else {
311: if (this .frame.isClosable()) {
312: leftmostButton = this .closeButton;
313: }
314: }
315: }
316:
317: if (leftmostButton != null) {
318: Rectangle rect = leftmostButton.getBounds();
319: rightEnd = rect.getBounds().x - 5;
320: }
321: if (theTitle != null) {
322: FontMetrics fm = this .frame.getFontMetrics(graphics
323: .getFont());
324: int titleWidth = rightEnd - leftEnd;
325: String clippedTitle = SubstanceCoreUtilities
326: .clipString(fm, titleWidth, theTitle);
327: // show tooltip with full title only if necessary
328: if (theTitle.equals(clippedTitle))
329: this .setToolTipText(null);
330: else
331: this .setToolTipText(theTitle);
332: theTitle = clippedTitle;
333: }
334: } else {
335: xOffset = width - 5;
336:
337: Icon icon = this .frame.getFrameIcon();
338: if (icon != null) {
339: xOffset -= (icon.getIconWidth() + 5);
340: }
341:
342: rightEnd = (this .menuBar == null) ? xOffset : xOffset
343: - this .menuBar.getWidth() - 5;
344:
345: // find the rightmost button for the left end
346: AbstractButton rightmostButton = null;
347: if (this .frame.isIconifiable()) {
348: rightmostButton = this .iconButton;
349: } else {
350: if (this .frame.isMaximizable()) {
351: rightmostButton = this .maxButton;
352: } else {
353: if (this .frame.isClosable()) {
354: rightmostButton = this .closeButton;
355: }
356: }
357: }
358:
359: leftEnd = 5;
360: if (rightmostButton != null) {
361: Rectangle rect = rightmostButton.getBounds();
362: leftEnd = rect.getBounds().x + 5;
363: }
364: if (theTitle != null) {
365: FontMetrics fm = this .frame.getFontMetrics(graphics
366: .getFont());
367: int titleWidth = rightEnd - leftEnd;
368: String clippedTitle = SubstanceCoreUtilities
369: .clipString(fm, titleWidth, theTitle);
370: // show tooltip with full title only if necessary
371: if (theTitle.equals(clippedTitle)) {
372: this .setToolTipText(null);
373: } else {
374: this .setToolTipText(theTitle);
375: }
376: theTitle = clippedTitle;
377: xOffset = rightEnd - fm.stringWidth(theTitle);
378: }
379: }
380:
381: SubstanceTextPainter textPainter = SubstanceLookAndFeel
382: .getCurrentTextPainter();
383: textPainter.init(this , null, true);
384:
385: if (textPainter.needsBackgroundImage()) {
386: textPainter
387: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
388: public void paintBackground(Graphics g) {
389: SubstanceDecorationUtilities
390: .paintDecorationBackground(
391: g,
392: SubstanceInternalFrameTitlePane.this ,
393: false);
394: }
395: });
396: } else {
397: SubstanceDecorationUtilities.paintDecorationBackground(
398: graphics, SubstanceInternalFrameTitlePane.this ,
399: false);
400: }
401:
402: // draw the title (if needed)
403: if (theTitle != null) {
404: JRootPane rootPane = this .getRootPane();
405: FontMetrics fm = rootPane
406: .getFontMetrics(graphics.getFont());
407: int yOffset = ((height - fm.getHeight()) / 2)
408: + fm.getAscent();
409:
410: SubstanceCoreUtilities.paintTextWithDropShadow(this ,
411: graphics, theme.getForegroundColor(), theTitle,
412: width, height, xOffset, yOffset);
413: }
414: textPainter.renderSurface(graphics);
415:
416: Icon icon = this .frame.getFrameIcon();
417: if (icon != null) {
418: if (leftToRight) {
419: int iconY = ((height / 2) - (icon.getIconHeight() / 2));
420: icon.paintIcon(this .frame, graphics, 5, iconY);
421: } else {
422: int iconY = ((height / 2) - (icon.getIconHeight() / 2));
423: icon.paintIcon(this .frame, graphics, width - 5
424: - icon.getIconWidth(), iconY);
425: }
426: }
427:
428: graphics.dispose();
429: }
430:
431: /*
432: * (non-Javadoc)
433: *
434: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#setButtonIcons()
435: */
436: @Override
437: protected void setButtonIcons() {
438: super .setButtonIcons();
439: // SubstanceTheme iconTheme = SubstanceLookAndFeel.getTheme()
440: // .getActiveTitlePaneTheme();
441:
442: Icon restoreIcon = new TransitionAwareIcon(this .maxButton,
443: new TransitionAwareIcon.Delegate() {
444: public Icon getThemeIcon(SubstanceTheme theme) {
445: return SubstanceIconFactory.getTitlePaneIcon(
446: SubstanceIconFactory.IconKind.RESTORE,
447: theme);
448: }
449: });
450: Icon maximizeIcon = new TransitionAwareIcon(this .maxButton,
451: new TransitionAwareIcon.Delegate() {
452: public Icon getThemeIcon(SubstanceTheme theme) {
453: return SubstanceIconFactory.getTitlePaneIcon(
454: SubstanceIconFactory.IconKind.MAXIMIZE,
455: theme);
456: }
457: });
458: Icon minimizeIcon = new TransitionAwareIcon(this .iconButton,
459: new TransitionAwareIcon.Delegate() {
460: public Icon getThemeIcon(SubstanceTheme theme) {
461: return SubstanceIconFactory.getTitlePaneIcon(
462: SubstanceIconFactory.IconKind.MINIMIZE,
463: theme);
464: }
465: });
466: Icon closeIcon = new TransitionAwareIcon(this .closeButton,
467: new TransitionAwareIcon.Delegate() {
468: public Icon getThemeIcon(SubstanceTheme theme) {
469: return SubstanceIconFactory.getTitlePaneIcon(
470: SubstanceIconFactory.IconKind.CLOSE,
471: theme);
472: }
473: });
474: if (this .frame.isIcon()) {
475: this .iconButton.setIcon(restoreIcon);
476: this .iconButton.setToolTipText(SubstanceCoreUtilities
477: .getResourceBundle(frame).getString(
478: "SystemMenu.restore"));
479: this .maxButton.setIcon(maximizeIcon);
480: this .maxButton.setToolTipText(SubstanceCoreUtilities
481: .getResourceBundle(frame).getString(
482: "SystemMenu.maximize"));
483: } else {
484: this .iconButton.setIcon(minimizeIcon);
485: this .iconButton.setToolTipText(SubstanceCoreUtilities
486: .getResourceBundle(frame).getString(
487: "SystemMenu.iconify"));
488: if (this .frame.isMaximum()) {
489: this .maxButton.setIcon(restoreIcon);
490: this .maxButton.setToolTipText(SubstanceCoreUtilities
491: .getResourceBundle(frame).getString(
492: "SystemMenu.restore"));
493: } else {
494: this .maxButton.setIcon(maximizeIcon);
495: this .maxButton.setToolTipText(SubstanceCoreUtilities
496: .getResourceBundle(frame).getString(
497: "SystemMenu.maximize"));
498: }
499: }
500: if (closeIcon != null) {
501: this .closeButton.setIcon(closeIcon);
502: // this.closeButton.setToolTipText(SubstanceLookAndFeel
503: // .getLabelBundle().getString("SystemMenu.close"));
504: syncCloseButtonTooltip();
505: }
506: }
507:
508: /**
509: * Click correction listener that resets models of minimize and restore
510: * buttons on click (so that the rollover behaviour will be preserved
511: * correctly).
512: *
513: * @author Kirill Grouchnikov.
514: */
515: public static class ClickListener implements ActionListener {
516: /*
517: * (non-Javadoc)
518: *
519: * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
520: */
521: public void actionPerformed(ActionEvent e) {
522: AbstractButton src = (AbstractButton) e.getSource();
523: ButtonModel model = src.getModel();
524: model.setArmed(false);
525: model.setPressed(false);
526: model.setRollover(false);
527: model.setSelected(false);
528: }
529: }
530:
531: /*
532: * (non-Javadoc)
533: *
534: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#createActions()
535: */
536: @Override
537: protected void createActions() {
538: super .createActions();
539: this .iconifyAction = new SubstanceIconifyAction();
540: }
541:
542: /*
543: * (non-Javadoc)
544: *
545: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#createButtons()
546: */
547: @Override
548: protected void createButtons() {
549: iconButton = new SubstanceTitleButton(
550: "InternalFrameTitlePane.iconifyButtonAccessibleName");
551: iconButton.addActionListener(iconifyAction);
552:
553: maxButton = new SubstanceTitleButton(
554: "InternalFrameTitlePane.maximizeButtonAccessibleName");
555: maxButton.addActionListener(maximizeAction);
556:
557: closeButton = new SubstanceTitleButton(
558: "InternalFrameTitlePane.closeButtonAccessibleName");
559: closeButton.addActionListener(closeAction);
560:
561: setButtonIcons();
562:
563: for (ActionListener listener : this .iconButton
564: .getActionListeners())
565: if (listener instanceof ClickListener)
566: return;
567: this .iconButton.addActionListener(new ClickListener());
568: for (ActionListener listener : this .maxButton
569: .getActionListeners())
570: if (listener instanceof ClickListener)
571: return;
572: this .maxButton.addActionListener(new ClickListener());
573: this .iconButton.putClientProperty(
574: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
575:
576: this .maxButton.putClientProperty(
577: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
578:
579: this .closeButton.putClientProperty(
580: SubstanceButtonUI.IS_TITLE_CLOSE_BUTTON, Boolean.TRUE);
581: this .closeButton.putClientProperty(
582: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
583:
584: this .enableActions();
585: }
586:
587: /*
588: * (non-Javadoc)
589: *
590: * @see javax.swing.plaf.basic.BasicInternalFrameTitlePane#createLayout()
591: */
592: @Override
593: protected LayoutManager createLayout() {
594: return new SubstanceTitlePaneLayout();
595: }
596:
597: /**
598: * Synchronizes the tooltip of the close button.
599: */
600: protected void syncCloseButtonTooltip() {
601: if (SubstanceCoreUtilities.isInternalFrameModified(this .frame)) {
602: this .closeButton.setToolTipText(SubstanceCoreUtilities
603: .getResourceBundle(frame).getString(
604: "SystemMenu.close")
605: + " ["
606: + SubstanceCoreUtilities.getResourceBundle(frame)
607: .getString("Tooltip.contentsNotSaved")
608: + "]");
609: } else {
610: this .closeButton.setToolTipText(SubstanceCoreUtilities
611: .getResourceBundle(frame).getString(
612: "SystemMenu.close"));
613: }
614: this .closeButton.repaint();
615: }
616:
617: /*
618: * (non-Javadoc)
619: *
620: * @see javax.swing.JComponent#removeNotify()
621: */
622: @Override
623: public void removeNotify() {
624: super .removeNotify();
625:
626: // fix for defect 211 - internal frames that are iconified
627: // programmatically should not uninstall the title panes.
628: boolean isAlive = ((this .frame.isIcon() && !this .frame
629: .isClosed()) || Boolean.TRUE.equals(frame
630: .getClientProperty(ICONIFYING)));
631: if (!isAlive) {
632: this .uninstall();
633: }
634: }
635:
636: /*
637: * (non-Javadoc)
638: *
639: * @see javax.swing.JComponent#addNotify()
640: */
641: @Override
642: public void addNotify() {
643: super .addNotify();
644: if (Boolean.TRUE.equals(this .getClientProperty(UNINSTALLED))) {
645: this .installTitlePane();
646: // this.installListeners();
647: this .putClientProperty(UNINSTALLED, null);
648: }
649: }
650:
651: /**
652: * Layout manager for this title pane.
653: *
654: * @author Kirill Grouchnikov
655: */
656: protected class SubstanceTitlePaneLayout extends TitlePaneLayout {
657: @Override
658: public void addLayoutComponent(String name, Component c) {
659: }
660:
661: @Override
662: public void removeLayoutComponent(Component c) {
663: }
664:
665: @Override
666: public Dimension preferredLayoutSize(Container c) {
667: return minimumLayoutSize(c);
668: }
669:
670: @Override
671: public Dimension minimumLayoutSize(Container c) {
672: // Compute width.
673: int width = 30;
674: if (frame.isClosable()) {
675: width += 21;
676: }
677: if (frame.isMaximizable()) {
678: width += 16 + (frame.isClosable() ? 10 : 4);
679: }
680: if (frame.isIconifiable()) {
681: width += 16 + (frame.isMaximizable() ? 2 : (frame
682: .isClosable() ? 10 : 4));
683: }
684: FontMetrics fm = frame.getFontMetrics(getFont());
685: String frameTitle = frame.getTitle();
686: int title_w = frameTitle != null ? fm
687: .stringWidth(frameTitle) : 0;
688: int title_length = frameTitle != null ? frameTitle.length()
689: : 0;
690:
691: if (title_length > 2) {
692: int subtitle_w = fm.stringWidth(frame.getTitle()
693: .substring(0, 2)
694: + "...");
695: width += (title_w < subtitle_w) ? title_w : subtitle_w;
696: } else {
697: width += title_w;
698: }
699:
700: // Compute height.
701: int height = 0;
702: // if (isPalette) {
703: // height = paletteTitleHeight;
704: // } else {
705: int fontHeight = fm.getHeight();
706: fontHeight += 7;
707: Icon icon = frame.getFrameIcon();
708: int iconHeight = 0;
709: if (icon != null) {
710: // SystemMenuBar forces the icon to be 16x16 or less.
711: iconHeight = Math.min(icon.getIconHeight(), 16);
712: }
713: iconHeight += 5;
714: height = Math.max(fontHeight, iconHeight);
715: // }
716:
717: return new Dimension(width, height);
718: }
719:
720: @Override
721: public void layoutContainer(Container c) {
722: boolean leftToRight = frame.getComponentOrientation()
723: .isLeftToRight();
724:
725: int w = getWidth();
726: int x = leftToRight ? w : 0;
727: int y = 2;
728: int spacing;
729:
730: // assumes all buttons have the same dimensions
731: // these dimensions include the borders
732: int buttonHeight = closeButton.getIcon().getIconHeight();
733: int buttonWidth = closeButton.getIcon().getIconWidth();
734:
735: y = (getHeight() - buttonHeight) / 2;
736:
737: if (frame.isClosable()) {
738: // if (isPalette) {
739: // spacing = 3;
740: // x += leftToRight ? -spacing - (buttonWidth + 2) : spacing;
741: // closeButton.setBounds(x, y, buttonWidth + 2,
742: // getHeight() - 4);
743: // if (!leftToRight)
744: // x += (buttonWidth + 2);
745: // } else {
746: spacing = 4;
747: x += leftToRight ? -spacing - buttonWidth : spacing;
748: closeButton.setBounds(x, y, buttonWidth, buttonHeight);
749: if (!leftToRight)
750: x += buttonWidth;
751: // }
752: }
753:
754: if (frame.isMaximizable()) {// && !isPalette) {
755: spacing = frame.isClosable() ? 10 : 4;
756: x += leftToRight ? -spacing - buttonWidth : spacing;
757: maxButton.setBounds(x, y, buttonWidth, buttonHeight);
758: if (!leftToRight)
759: x += buttonWidth;
760: }
761:
762: if (frame.isIconifiable()) {// && !isPalette) {
763: spacing = frame.isMaximizable() ? 2 : (frame
764: .isClosable() ? 10 : 4);
765: x += leftToRight ? -spacing - buttonWidth : spacing;
766: iconButton.setBounds(x, y, buttonWidth, buttonHeight);
767: if (!leftToRight)
768: x += buttonWidth;
769: }
770: //
771: // buttonsWidth = leftToRight ? w - x : x;
772: }
773: }
774:
775: /**
776: * Custom iconifying action.
777: *
778: * @author Kirill Grouchnikov
779: */
780: public class SubstanceIconifyAction extends IconifyAction {
781: /**
782: * Creates an iconifying action.
783: */
784: public SubstanceIconifyAction() {
785: super ();
786: }
787:
788: @Override
789: public void actionPerformed(ActionEvent e) {
790: frame.putClientProperty(ICONIFYING, Boolean.TRUE);
791: super.actionPerformed(e);
792: frame.putClientProperty(ICONIFYING, null);
793: }
794: }
795: }
|