0001: /*
0002: * Copyright (c) 2005-2008 Substance Kirill Grouchnikov. All Rights Reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * o Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: *
0010: * o Redistributions in binary form must reproduce the above copyright notice,
0011: * this list of conditions and the following disclaimer in the documentation
0012: * and/or other materials provided with the distribution.
0013: *
0014: * o Neither the name of Substance Kirill Grouchnikov nor the names of
0015: * its contributors may be used to endorse or promote products derived
0016: * from this software without specific prior written permission.
0017: *
0018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
0020: * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
0021: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
0022: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
0023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
0024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
0025: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
0026: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
0027: * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0028: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0029: */
0030: package org.jvnet.substance.utils;
0031:
0032: import java.awt.*;
0033: import java.awt.event.*;
0034: import java.awt.image.BufferedImage;
0035: import java.beans.PropertyChangeEvent;
0036: import java.beans.PropertyChangeListener;
0037: import java.io.*;
0038: import java.lang.ref.WeakReference;
0039: import java.text.SimpleDateFormat;
0040: import java.util.*;
0041:
0042: import javax.swing.*;
0043: import javax.swing.plaf.UIResource;
0044:
0045: import org.jvnet.lafwidget.LafWidget;
0046: import org.jvnet.lafwidget.LafWidgetUtilities;
0047: import org.jvnet.lafwidget.animation.effects.GhostPaintingUtils;
0048: import org.jvnet.lafwidget.utils.TrackableThread;
0049: import org.jvnet.lafwidget.utils.LafConstants.AnimationKind;
0050: import org.jvnet.substance.*;
0051: import org.jvnet.substance.border.BorderPainterChangeListener;
0052: import org.jvnet.substance.border.BorderPainterInfo;
0053: import org.jvnet.substance.button.ButtonShaperChangeListener;
0054: import org.jvnet.substance.button.ButtonShaperInfo;
0055: import org.jvnet.substance.color.ColorScheme;
0056: import org.jvnet.substance.painter.GradientPainterChangeListener;
0057: import org.jvnet.substance.painter.GradientPainterInfo;
0058: import org.jvnet.substance.painter.decoration.DecorationAreaType;
0059: import org.jvnet.substance.painter.decoration.SubstanceDecorationUtilities;
0060: import org.jvnet.substance.painter.text.SubstanceTextPainter;
0061: import org.jvnet.substance.skin.SkinInfo;
0062: import org.jvnet.substance.theme.*;
0063: import org.jvnet.substance.theme.SubstanceTheme.ThemeKind;
0064: import org.jvnet.substance.utils.SubstanceConstants.FocusKind;
0065: import org.jvnet.substance.utils.icon.SubstanceIconFactory;
0066: import org.jvnet.substance.utils.icon.TransitionAwareIcon;
0067: import org.jvnet.substance.utils.menu.TraitMenuHandler;
0068: import org.jvnet.substance.watermark.*;
0069:
0070: /**
0071: * Title pane for <b>Substance</b> look and feel.
0072: *
0073: * @author Kirill Grouchnikov
0074: */
0075: public class SubstanceTitlePane extends JComponent {
0076: /**
0077: * Name for the client property that is set on a root pane to specify that
0078: * the heap status panel should be displayed permanently.
0079: */
0080: public static final String HEAP_STATUS_PANEL_PERMANENT = "substancelaf.internal.heapStatusPanelPermanent";
0081:
0082: // /**
0083: // * Max icon height for the title icon.
0084: // */
0085: // public static final int IMAGE_HEIGHT = 16;
0086: //
0087: // /**
0088: // * Max icon width for the title icon.
0089: // */
0090: // public static final int IMAGE_WIDTH = 16;
0091: //
0092: /**
0093: * PropertyChangeListener added to the JRootPane.
0094: */
0095: private PropertyChangeListener propertyChangeListener;
0096:
0097: /**
0098: * JMenuBar, typically renders the system menu items.
0099: */
0100: private JMenuBar menuBar;
0101:
0102: /**
0103: * Action used to close the Window.
0104: */
0105: private Action closeAction;
0106:
0107: /**
0108: * Action used to iconify the Frame.
0109: */
0110: private Action iconifyAction;
0111:
0112: /**
0113: * Action to restore the Frame size.
0114: */
0115: private Action restoreAction;
0116:
0117: /**
0118: * Action to restore the Frame size.
0119: */
0120: private Action maximizeAction;
0121:
0122: /**
0123: * Button used to maximize or restore the frame.
0124: */
0125: private JButton toggleButton;
0126:
0127: /**
0128: * Button used to minimize the frame
0129: */
0130: private JButton minimizeButton;
0131:
0132: /**
0133: * Button used to close the frame.
0134: */
0135: private JButton closeButton;
0136:
0137: /**
0138: * Listens for changes in the state of the Window listener to update the
0139: * state of the widgets.
0140: */
0141: private WindowListener windowListener;
0142:
0143: /**
0144: * Window we're currently in.
0145: */
0146: private Window window;
0147:
0148: /**
0149: * JRootPane rendering for.
0150: */
0151: private JRootPane rootPane;
0152:
0153: /**
0154: * Buffered Frame.state property. As state isn't bound, this is kept to
0155: * determine when to avoid updating widgets.
0156: */
0157: private int state;
0158:
0159: /**
0160: * SubstanceRootPaneUI that created us.
0161: */
0162: private SubstanceRootPaneUI rootPaneUI;
0163:
0164: /**
0165: * Indication whether any title pane can have heap status panel.
0166: */
0167: protected static boolean canHaveHeapStatusPanel;
0168:
0169: /**
0170: * The logfile name for the heap status panel. Can be <code>null</code> -
0171: * in this case the {@link HeapStatusThread} will not write heap
0172: * information.
0173: */
0174: protected static String heapStatusLogfileName;
0175:
0176: /**
0177: * The heap status panel of <code>this</code> title pane.
0178: */
0179: protected HeapStatusPanel heapStatusPanel;
0180:
0181: /**
0182: * The heap status toggle menu item of <code>this</code> title pane.
0183: */
0184: protected JCheckBoxMenuItem heapStatusMenuItem;
0185:
0186: /**
0187: * Listens on changes to <code>componentOrientation</code> and
0188: * {@link SubstanceLookAndFeel#WINDOW_MODIFIED} properties.
0189: */
0190: protected PropertyChangeListener propertyListener;
0191:
0192: protected TraitMenuHandler themeMenuHandler;
0193:
0194: protected ThemeChangeListener themeChangeListener;
0195:
0196: protected TraitMenuHandler watermarkMenuHandler;
0197:
0198: protected WatermarkChangeListener watermarkChangeListener;
0199:
0200: protected TraitMenuHandler buttonShaperMenuHandler;
0201:
0202: protected ButtonShaperChangeListener buttonShaperChangeListener;
0203:
0204: protected TraitMenuHandler gradientPainterMenuHandler;
0205:
0206: protected GradientPainterChangeListener gradientPainterChangeListener;
0207:
0208: protected TraitMenuHandler titlePainterMenuHandler;
0209:
0210: protected TraitMenuHandler borderPainterMenuHandler;
0211:
0212: protected BorderPainterChangeListener borderPainterChangeListener;
0213:
0214: protected MouseListener substanceDebugUiListener;
0215:
0216: public static final String HAS_BEEN_UNINSTALLED = "substancelaf.internal.titlePane.hasBeenUninstalled";
0217:
0218: /**
0219: * Panel that shows heap status and allows running the garbage collector.
0220: *
0221: * @author Kirill Grouchnikov
0222: */
0223: public static class HeapStatusPanel extends JPanel {
0224: /**
0225: * The current heap size in kilobytes.
0226: */
0227: private int currHeapSizeKB;
0228:
0229: /**
0230: * The current used portion of heap in kilobytes.
0231: */
0232: private int currTakenHeapSizeKB;
0233:
0234: /**
0235: * History of used heap portion (in percents). Each value is in 0.0-1.0
0236: * range.
0237: */
0238: private LinkedList<Double> graphValues;
0239:
0240: private SubstanceTitlePane titlePane;
0241:
0242: /**
0243: * Creates new heap status panel.
0244: */
0245: public HeapStatusPanel(SubstanceTitlePane titlePane) {
0246: this .graphValues = new LinkedList<Double>();
0247: this .titlePane = titlePane;
0248: HeapStatusThread.getInstance();
0249: }
0250:
0251: /**
0252: * Updates the values for <code>this</code> heap status panel.
0253: *
0254: * @param currHeapSizeKB
0255: * The current heap size in kilobytes.
0256: * @param currTakenHeapSizeKB
0257: * The current used portion of heap in kilobytes.
0258: */
0259: public synchronized void updateStatus(int currHeapSizeKB,
0260: int currTakenHeapSizeKB) {
0261: this .currHeapSizeKB = currHeapSizeKB;
0262: this .currTakenHeapSizeKB = currTakenHeapSizeKB;
0263: double newGraphValue = (double) currTakenHeapSizeKB
0264: / (double) currHeapSizeKB;
0265: this .graphValues.addLast(newGraphValue);
0266: this .repaint();
0267: }
0268:
0269: /*
0270: * (non-Javadoc)
0271: *
0272: * @see javax.swing.JComponent#paint(java.awt.Graphics)
0273: */
0274: @Override
0275: public synchronized void paint(Graphics g) {
0276: Graphics2D graphics = (Graphics2D) g.create();
0277:
0278: Window window = this .titlePane.getWindow();
0279: boolean isSelected = (window == null) ? true : window
0280: .isActive();
0281: final SubstanceTheme theme = isSelected ? SubstanceLookAndFeel
0282: .getTheme().getActiveTitlePaneTheme()
0283: : SubstanceLookAndFeel.getTheme()
0284: .getDefaultTitlePaneTheme();
0285: ColorScheme scheme = theme.getColorScheme();
0286:
0287: graphics.setColor(scheme.getDarkColor());
0288: int w = this .getWidth();
0289: int h = this .getHeight();
0290:
0291: graphics.drawRect(0, 0, w - 1, h - 1);
0292:
0293: graphics.setColor(scheme.getExtraLightColor());
0294: graphics.fillRect(1, 1, w - 2, h - 2);
0295:
0296: while (this .graphValues.size() > (w - 2))
0297: this .graphValues.removeFirst();
0298:
0299: int xOff = w - this .graphValues.size() - 1;
0300: graphics.setColor(scheme.getMidColor());
0301: int count = 0;
0302: for (double value : this .graphValues) {
0303: int valueH = (int) (value * (h - 2));
0304: graphics.drawLine(xOff + count, h - 1 - valueH, xOff
0305: + count, h - 2);
0306: count++;
0307: }
0308:
0309: graphics.setFont(UIManager.getFont("Panel.font"));
0310: FontMetrics fm = graphics.getFontMetrics();
0311:
0312: StringBuffer longFormat = new StringBuffer();
0313: Formatter longFormatter = new Formatter(longFormat);
0314: longFormatter.format("%.1fMB / %.1fMB",
0315: this .currTakenHeapSizeKB / 1024.f,
0316: this .currHeapSizeKB / 1024.f);
0317: int strW = fm.stringWidth(longFormat.toString());
0318: int strH = fm.getAscent() + fm.getDescent();
0319:
0320: graphics.setColor(scheme.getForegroundColor());
0321: if (strW < (w - 5)) {
0322: graphics.drawString(longFormat.toString(),
0323: (w - strW) / 2, (h + strH) / 2 - 2);
0324: } else {
0325: String shortFormat = (this .currTakenHeapSizeKB / 1024)
0326: + "MB / " + (this .currHeapSizeKB / 1024) + "MB";
0327: strW = fm.stringWidth(shortFormat);
0328: graphics.drawString(shortFormat, (w - strW) / 2,
0329: (h + strH) / 2 - 2);
0330: }
0331:
0332: graphics.dispose();
0333: }
0334: }
0335:
0336: /**
0337: * Thread for heap status panel.
0338: */
0339: public static class HeapStatusThread extends TrackableThread {
0340: /**
0341: * Current heap size in kilobytes.
0342: */
0343: private int heapSizeKB;
0344:
0345: /**
0346: * Current used portion of heap in kilobytes.
0347: */
0348: private int takenHeapSizeKB;
0349:
0350: /**
0351: * All heap status panels.
0352: */
0353: private static Set<WeakReference<HeapStatusPanel>> panels = new HashSet<WeakReference<HeapStatusPanel>>();
0354:
0355: /**
0356: * Single instance of <code>this</code> thread.
0357: */
0358: private static HeapStatusThread instance;
0359:
0360: /**
0361: * Formatter object (for logfile).
0362: */
0363: private SimpleDateFormat format;
0364:
0365: /**
0366: * Signifies whether a stop request has been issued on <code>this</code>
0367: * thread using the {@link #requestStop()} call.
0368: */
0369: private boolean isStopRequested;
0370:
0371: /**
0372: * Simple constructor. Defined private for singleton.
0373: *
0374: * @see #getInstance()
0375: */
0376: private HeapStatusThread() {
0377: this .format = new SimpleDateFormat(
0378: "dd/MM/yyyy HH:mm:ss.SSS");
0379: this .isStopRequested = false;
0380: this .setName("Substance heap status");
0381: }
0382:
0383: /**
0384: * Gets singleton instance of <code>this</code> thread.
0385: *
0386: * @return Singleton instance of <code>this</code> thread.
0387: */
0388: public synchronized static HeapStatusThread getInstance() {
0389: if (HeapStatusThread.instance == null) {
0390: HeapStatusThread.instance = new HeapStatusThread();
0391: HeapStatusThread.instance.start();
0392: }
0393: return HeapStatusThread.instance;
0394: }
0395:
0396: /**
0397: * Registers new heap status panel with <code>this</code> thread.
0398: *
0399: * @param panel
0400: * Heap statuc panel.
0401: */
0402: public static synchronized void registerPanel(
0403: HeapStatusPanel panel) {
0404: panels.add(new WeakReference<HeapStatusPanel>(panel));
0405: }
0406:
0407: /**
0408: * Unregisters new heap status panel from <code>this</code> thread.
0409: *
0410: * @param panel
0411: * Heap statuc panel.
0412: */
0413: public static synchronized void unregisterPanel(
0414: HeapStatusPanel panel) {
0415: for (Iterator<WeakReference<HeapStatusPanel>> it = panels
0416: .iterator(); it.hasNext();) {
0417: WeakReference<HeapStatusPanel> ref = it.next();
0418: HeapStatusPanel currPanel = ref.get();
0419: if (panel == currPanel) {
0420: it.remove();
0421: return;
0422: }
0423: }
0424: }
0425:
0426: /**
0427: * Updates the values of heap status.
0428: */
0429: private synchronized void updateHeapCounts() {
0430: long heapSize = Runtime.getRuntime().totalMemory();
0431: long heapFreeSize = Runtime.getRuntime().freeMemory();
0432:
0433: this .heapSizeKB = (int) (heapSize / 1024);
0434: this .takenHeapSizeKB = (int) ((heapSize - heapFreeSize) / 1024);
0435: }
0436:
0437: /*
0438: * (non-Javadoc)
0439: *
0440: * @see java.lang.Thread#run()
0441: */
0442: @Override
0443: public void run() {
0444: while (!this .isStopRequested) {
0445: try {
0446: // update every 0.5 seconds
0447: Thread.sleep(500);
0448: } catch (InterruptedException ie) {
0449: }
0450: this .updateHeapCounts();
0451: for (Iterator<WeakReference<HeapStatusPanel>> it = panels
0452: .iterator(); it.hasNext();) {
0453: WeakReference<HeapStatusPanel> refPanel = it.next();
0454: HeapStatusPanel panel = refPanel.get();
0455: if (panel == null) {
0456: // prune
0457: panels.remove(it);
0458: continue;
0459: }
0460:
0461: panel.updateStatus(this .heapSizeKB,
0462: this .takenHeapSizeKB);
0463: }
0464: // see if need to put info in log file
0465: if (SubstanceTitlePane.heapStatusLogfileName != null) {
0466: PrintWriter pw = null;
0467: try {
0468: pw = new PrintWriter(
0469: new FileWriter(
0470: SubstanceTitlePane.heapStatusLogfileName,
0471: true));
0472: pw.println(this .format.format(new Date()) + " "
0473: + this .takenHeapSizeKB + "KB / "
0474: + this .heapSizeKB + "KB");
0475: } catch (IOException ioe) {
0476:
0477: } finally {
0478: if (pw != null) {
0479: pw.close();
0480: }
0481: }
0482: }
0483: }
0484: }
0485:
0486: @Override
0487: protected void requestStop() {
0488: this .isStopRequested = true;
0489: HeapStatusThread.instance = null;
0490: }
0491: }
0492:
0493: /**
0494: * Creates a new title pane.
0495: *
0496: * @param root
0497: * Root pane.
0498: * @param ui
0499: * Root pane UI.
0500: */
0501: public SubstanceTitlePane(JRootPane root, SubstanceRootPaneUI ui) {
0502: this .rootPane = root;
0503: this .rootPaneUI = ui;
0504:
0505: this .state = -1;
0506:
0507: this .installSubcomponents();
0508: this .installDefaults();
0509:
0510: this .setLayout(this .createLayout());
0511:
0512: this .setToolTipText(this .getTitle());
0513:
0514: SubstanceDecorationUtilities.setDecorationType(this ,
0515: DecorationAreaType.PRIMARY_TITLE_PANE);
0516: }
0517:
0518: /**
0519: * Uninstalls the necessary state.
0520: */
0521: public void uninstall() {
0522: this .uninstallListeners();
0523: this .window = null;
0524: // Swing bug (?) - the updateComponentTree never gets to the
0525: // system menu (and in our case we have radio menu items with
0526: // rollover listeners). Fix for defect 109 - memory leak on theme
0527: // switch
0528: if ((this .menuBar != null) && (this .menuBar.getMenuCount() > 0)) {
0529: this .menuBar.getUI().uninstallUI(this .menuBar);
0530: SubstanceCoreUtilities.uninstallMenu(this .menuBar
0531: .getMenu(0));
0532: }
0533:
0534: if (SubstanceTitlePane.canHaveHeapStatusPanel) {
0535: if (this .heapStatusPanel != null) {
0536: for (MouseListener listener : this .heapStatusPanel
0537: .getMouseListeners())
0538: this .heapStatusPanel.removeMouseListener(listener);
0539: HeapStatusThread.unregisterPanel(this .heapStatusPanel);
0540: this .remove(this .heapStatusPanel);
0541: }
0542: }
0543:
0544: if (this .menuBar != null)
0545: this .menuBar.removeAll();
0546: this .removeAll();
0547:
0548: SubstanceLookAndFeel
0549: .unregisterThemeChangeListener(this .themeChangeListener);
0550: this .themeChangeListener = null;
0551: SubstanceLookAndFeel
0552: .unregisterWatermarkChangeListener(this .watermarkChangeListener);
0553: this .watermarkChangeListener = null;
0554: SubstanceLookAndFeel
0555: .unregisterButtonShaperChangeListener(this .buttonShaperChangeListener);
0556: this .buttonShaperChangeListener = null;
0557: SubstanceLookAndFeel
0558: .unregisterGradientPainterChangeListener(this .gradientPainterChangeListener);
0559: this .gradientPainterChangeListener = null;
0560: SubstanceLookAndFeel
0561: .unregisterBorderPainterChangeListener(this .borderPainterChangeListener);
0562: this .borderPainterChangeListener = null;
0563: }
0564:
0565: /**
0566: * Installs the necessary listeners.
0567: */
0568: private void installListeners() {
0569: if (this .window != null) {
0570: this .windowListener = new WindowHandler();
0571: this .window.addWindowListener(this .windowListener);
0572: this .propertyChangeListener = new PropertyChangeHandler();
0573: this .window
0574: .addPropertyChangeListener(this .propertyChangeListener);
0575: }
0576:
0577: // Property change listener for pulsating close button
0578: // when window has been marked as changed.
0579: // Fix for defect 109 - memory leak on theme change.
0580: this .propertyListener = new PropertyChangeListener() {
0581: public void propertyChange(final PropertyChangeEvent evt) {
0582: if (SubstanceLookAndFeel.WINDOW_MODIFIED.equals(evt
0583: .getPropertyName())) {
0584: syncCloseButtonTooltip();
0585: // if (Boolean.TRUE.equals(evt.getNewValue())) {
0586: // SubstanceTitlePane.this.closeButton
0587: // .setToolTipText(SubstanceLookAndFeel
0588: // .getLabelBundle().getString(
0589: // "SystemMenu.close")
0590: // + " ["
0591: // + SubstanceLookAndFeel
0592: // .getLabelBundle()
0593: // .getString(
0594: // "Tooltip.contentsNotSaved")
0595: // + "]");
0596: // } else {
0597: // SubstanceTitlePane.this.closeButton
0598: // .setToolTipText(SubstanceLookAndFeel
0599: // .getLabelBundle().getString(
0600: // "SystemMenu.close"));
0601: // }
0602: // SubstanceTitlePane.this.closeButton.repaint();
0603: }
0604:
0605: if ("componentOrientation"
0606: .equals(evt.getPropertyName())) {
0607: SwingUtilities.invokeLater(new Runnable() {
0608: public void run() {
0609: if (SubstanceTitlePane.this .menuBar != null) {
0610: SubstanceTitlePane.this .menuBar
0611: .applyComponentOrientation((ComponentOrientation) evt
0612: .getNewValue());
0613: }
0614: }
0615: });
0616: }
0617: }
0618: };
0619: // Wire it on the frame itself and its root pane.
0620: this .rootPane.addPropertyChangeListener(this .propertyListener);
0621: if (this .getFrame() != null)
0622: this .getFrame().addPropertyChangeListener(
0623: this .propertyListener);
0624:
0625: if (SubstanceLookAndFeel.isDebugUiMode()) {
0626: this .substanceDebugUiListener = new MouseAdapter() {
0627: @Override
0628: public void mousePressed(MouseEvent e) {
0629: process(e);
0630: }
0631:
0632: @Override
0633: public void mouseReleased(MouseEvent e) {
0634: process(e);
0635: }
0636:
0637: protected void process(MouseEvent e) {
0638: if (e.isPopupTrigger()) {
0639: JPopupMenu popup = new JPopupMenu();
0640: JMenu cbMenu = new JMenu("Color blindness");
0641: JMenuItem protanopiaCurrent = new JMenuItem(
0642: "Protanopia current");
0643: protanopiaCurrent
0644: .addActionListener(new ThemeChanger(
0645: SubstanceLookAndFeel.getTheme()
0646: .protanopia()));
0647: cbMenu.add(protanopiaCurrent);
0648: JMenuItem deuteranopiaCurrent = new JMenuItem(
0649: "Deuteranopia current");
0650: deuteranopiaCurrent
0651: .addActionListener(new ThemeChanger(
0652: SubstanceLookAndFeel.getTheme()
0653: .deuteranopia()));
0654: cbMenu.add(deuteranopiaCurrent);
0655: JMenuItem tritanopiaCurrent = new JMenuItem(
0656: "Tritanopia current");
0657: tritanopiaCurrent
0658: .addActionListener(new ThemeChanger(
0659: SubstanceLookAndFeel.getTheme()
0660: .tritanopia()));
0661: cbMenu.add(tritanopiaCurrent);
0662:
0663: cbMenu.addSeparator();
0664:
0665: JMenuItem restoreOriginal = new JMenuItem(
0666: "Restore original");
0667: if (SubstanceLookAndFeel.getTheme() instanceof SubstanceColorBlindTheme) {
0668: restoreOriginal
0669: .addActionListener(new ThemeChanger(
0670: ((SubstanceColorBlindTheme) SubstanceLookAndFeel
0671: .getTheme())
0672: .getOriginalTheme()));
0673: } else {
0674: restoreOriginal.setEnabled(false);
0675: }
0676: cbMenu.add(restoreOriginal);
0677:
0678: popup.add(cbMenu);
0679:
0680: JMenu animMenu = new JMenu("Animation rate");
0681: JMenuItem debugNone = new JMenuItem("None");
0682: debugNone
0683: .addActionListener(new AnimationChanger(
0684: AnimationKind.NONE));
0685: animMenu.add(debugNone);
0686: JMenuItem debugAnim = new JMenuItem(
0687: "Debug rate (extra slow)");
0688: debugAnim
0689: .addActionListener(new AnimationChanger(
0690: AnimationKind.DEBUG));
0691: animMenu.add(debugAnim);
0692: JMenuItem debugAnimFast = new JMenuItem(
0693: "Debug rate (faster)");
0694: debugAnimFast
0695: .addActionListener(new AnimationChanger(
0696: AnimationKind.DEBUG_FAST));
0697: animMenu.add(debugAnimFast);
0698: JMenuItem debugSlow = new JMenuItem("Slow rate");
0699: debugSlow
0700: .addActionListener(new AnimationChanger(
0701: AnimationKind.SLOW));
0702: animMenu.add(debugSlow);
0703: JMenuItem debugRegular = new JMenuItem(
0704: "Regular rate");
0705: debugRegular
0706: .addActionListener(new AnimationChanger(
0707: AnimationKind.REGULAR));
0708: animMenu.add(debugRegular);
0709: JMenuItem debugFast = new JMenuItem("Fast rate");
0710: debugFast
0711: .addActionListener(new AnimationChanger(
0712: AnimationKind.FAST));
0713: animMenu.add(debugFast);
0714:
0715: popup.add(animMenu);
0716:
0717: JMenu focusMenu = new JMenu("Focus kind");
0718: for (FocusKind fKind : FocusKind.values()) {
0719: JMenuItem focusMenuItem = new JMenuItem(
0720: fKind.name().toLowerCase());
0721: focusMenuItem
0722: .addActionListener(new FocusKindChanger(
0723: fKind));
0724: focusMenu.add(focusMenuItem);
0725: }
0726: popup.add(focusMenu);
0727:
0728: JMenuItem dumpHierarchy = new JMenuItem(
0729: "Dump hierarchy");
0730: dumpHierarchy
0731: .addActionListener(new ActionListener() {
0732: public void actionPerformed(
0733: ActionEvent e) {
0734: dump(rootPane, 0);
0735: }
0736: });
0737: popup.add(dumpHierarchy);
0738:
0739: final JCheckBoxMenuItem ltrChange = new JCheckBoxMenuItem(
0740: "Is left-to-right");
0741: ltrChange.setSelected(rootPane
0742: .getComponentOrientation()
0743: .isLeftToRight());
0744: ltrChange
0745: .addActionListener(new ActionListener() {
0746: public void actionPerformed(
0747: ActionEvent e) {
0748: SwingUtilities
0749: .invokeLater(new Runnable() {
0750: public void run() {
0751: rootPane
0752: .applyComponentOrientation(ltrChange
0753: .isSelected() ? ComponentOrientation.LEFT_TO_RIGHT
0754: : ComponentOrientation.RIGHT_TO_LEFT);
0755: }
0756: });
0757: }
0758: });
0759: popup.add(ltrChange);
0760:
0761: final JCheckBoxMenuItem useThemedIcons = new JCheckBoxMenuItem(
0762: "Use themed icons");
0763: useThemedIcons
0764: .setSelected(SubstanceCoreUtilities
0765: .useThemedDefaultIcon(null));
0766: useThemedIcons
0767: .addActionListener(new ActionListener() {
0768: public void actionPerformed(
0769: ActionEvent e) {
0770: SwingUtilities
0771: .invokeLater(new Runnable() {
0772: public void run() {
0773: UIManager
0774: .put(
0775: SubstanceLookAndFeel.USE_THEMED_DEFAULT_ICONS,
0776: useThemedIcons
0777: .isSelected() ? Boolean.TRUE
0778: : null);
0779: rootPane
0780: .repaint();
0781: }
0782: });
0783: }
0784: });
0785: popup.add(useThemedIcons);
0786:
0787: final JCheckBoxMenuItem ghostDebugMode = new JCheckBoxMenuItem(
0788: "Ghost debug mode");
0789: ghostDebugMode
0790: .addActionListener(new ActionListener() {
0791: public void actionPerformed(
0792: ActionEvent e) {
0793: SwingUtilities
0794: .invokeLater(new Runnable() {
0795: public void run() {
0796: ghostDebugMode
0797: .setEnabled(false);
0798: GhostPaintingUtils.MAX_ICON_GHOSTING_ALPHA = 0.8f;
0799: GhostPaintingUtils.MIN_ICON_GHOSTING_ALPHA = 0.6f;
0800: GhostPaintingUtils.MAX_PRESS_GHOSTING_ALPHA = 0.8f;
0801: GhostPaintingUtils.MIN_PRESS_GHOSTING_ALPHA = 0.6f;
0802: GhostPaintingUtils.DECAY_FACTOR = 0.7f;
0803: }
0804: });
0805: }
0806: });
0807: popup.add(ghostDebugMode);
0808:
0809: popup.show(SubstanceTitlePane.this , e.getX(), e
0810: .getY());
0811: }
0812: }
0813: };
0814: this .addMouseListener(this .substanceDebugUiListener);
0815: }
0816: }
0817:
0818: /**
0819: * Uninstalls the necessary listeners.
0820: */
0821: private void uninstallListeners() {
0822: if (this .window != null) {
0823: this .window.removeWindowListener(this .windowListener);
0824: this .windowListener = null;
0825: this .window
0826: .removePropertyChangeListener(this .propertyChangeListener);
0827: this .propertyChangeListener = null;
0828: }
0829:
0830: // Fix for defect 109 - memory leak on theme change.
0831: this .rootPane
0832: .removePropertyChangeListener(this .propertyListener);
0833: if (this .getFrame() != null)
0834: this .getFrame().removePropertyChangeListener(
0835: this .propertyListener);
0836: this .propertyListener = null;
0837:
0838: if (this .substanceDebugUiListener != null) {
0839: this .removeMouseListener(this .substanceDebugUiListener);
0840: this .substanceDebugUiListener = null;
0841: }
0842: }
0843:
0844: /**
0845: * Returns the <code>JRootPane</code> this was created for.
0846: */
0847: @Override
0848: public JRootPane getRootPane() {
0849: return this .rootPane;
0850: }
0851:
0852: /**
0853: * Returns the decoration style of the <code>JRootPane</code>.
0854: *
0855: * @return Decoration style of the <code>JRootPane</code>.
0856: */
0857: private int getWindowDecorationStyle() {
0858: return this .getRootPane().getWindowDecorationStyle();
0859: }
0860:
0861: /*
0862: * (non-Javadoc)
0863: *
0864: * @see java.awt.Component#addNotify()
0865: */
0866: @Override
0867: public void addNotify() {
0868: super .addNotify();
0869:
0870: this .uninstallListeners();
0871:
0872: this .window = SwingUtilities.getWindowAncestor(this );
0873: if (this .window != null) {
0874: this .setActive(this .window.isActive());
0875: if (Boolean.TRUE.equals(SwingUtilities.getRootPane(this )
0876: .getClientProperty(HAS_BEEN_UNINSTALLED))) {
0877: // System.out.println("Reinstalling");
0878: this .installSubcomponents();
0879: this .installDefaults();
0880: this .setLayout(this .createLayout());
0881: this .setToolTipText(this .getTitle());
0882: }
0883: if (this .window instanceof Frame) {
0884: this .setState(((Frame) this .window).getExtendedState());
0885: } else {
0886: this .setState(0);
0887: }
0888: this .installListeners();
0889: }
0890: this .setToolTipText(this .getTitle());
0891: }
0892:
0893: /*
0894: * (non-Javadoc)
0895: *
0896: * @see java.awt.Component#removeNotify()
0897: */
0898: @Override
0899: public void removeNotify() {
0900: // System.out.println("Uninstalling");
0901: SwingUtilities.getRootPane(this ).putClientProperty(
0902: HAS_BEEN_UNINSTALLED, Boolean.TRUE);
0903: super .removeNotify();
0904:
0905: this .uninstall();
0906: this .window = null;
0907: //
0908: // // Fix for defect 189 - memory leak on disposed frames
0909: // if (menuBar != null) {
0910: // MenuBarUI menuBarUI = menuBar.getUI();
0911: // if (menuBarUI instanceof SubstanceMenuBarUI) {
0912: // ((SubstanceMenuBarUI) menuBarUI).uninstallUI(menuBar);
0913: // }
0914: // }
0915: }
0916:
0917: /**
0918: * Adds any sub-Components contained in the <code>SubstanceTitlePane</code>.
0919: */
0920: private void installSubcomponents() {
0921: int decorationStyle = this .getWindowDecorationStyle();
0922: if (decorationStyle == JRootPane.FRAME) {
0923: this .createActions();
0924: this .menuBar = this .createMenuBar();
0925: this .add(this .menuBar);
0926: this .createButtons();
0927: this .add(this .minimizeButton);
0928: this .add(this .toggleButton);
0929: this .add(this .closeButton);
0930:
0931: if (SubstanceTitlePane.canHaveHeapStatusPanel) {
0932: this .heapStatusPanel = new HeapStatusPanel(this );
0933: this .add(this .heapStatusPanel);
0934: boolean isHeapStatusPanelShowing = Boolean.TRUE
0935: .equals(this .rootPane
0936: .getClientProperty(SubstanceLookAndFeel.HEAP_STATUS_PANEL));
0937: this .heapStatusPanel
0938: .setVisible(isHeapStatusPanelShowing);
0939: this .heapStatusPanel.setPreferredSize(new Dimension(80,
0940: this .getPreferredSize().height));
0941: this .heapStatusPanel
0942: .setToolTipText(SubstanceCoreUtilities
0943: .getResourceBundle(rootPane).getString(
0944: "Tooltip.heapStatusPanel"));
0945: this .heapStatusPanel
0946: .addMouseListener(new MouseAdapter() {
0947: @Override
0948: public void mouseClicked(MouseEvent e) {
0949: System.gc();
0950: }
0951: });
0952:
0953: HeapStatusThread.registerPanel(this .heapStatusPanel);
0954: }
0955: } else {
0956: if ((decorationStyle == JRootPane.PLAIN_DIALOG)
0957: || (decorationStyle == JRootPane.INFORMATION_DIALOG)
0958: || (decorationStyle == JRootPane.ERROR_DIALOG)
0959: || (decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG)
0960: || (decorationStyle == JRootPane.FILE_CHOOSER_DIALOG)
0961: || (decorationStyle == JRootPane.QUESTION_DIALOG)
0962: || (decorationStyle == JRootPane.WARNING_DIALOG)) {
0963: this .createActions();
0964: this .createButtons();
0965: this .add(this .closeButton);
0966: }
0967: }
0968: }
0969:
0970: /**
0971: * Installs the fonts and necessary properties.
0972: */
0973: private void installDefaults() {
0974: this .setFont(UIManager.getFont("InternalFrame.titleFont", this
0975: .getLocale()));
0976: }
0977:
0978: /**
0979: * Returns the <code>JMenuBar</code> displaying the appropriate system
0980: * menu items.
0981: *
0982: * @return <code>JMenuBar</code> displaying the appropriate system menu
0983: * items.
0984: */
0985: protected JMenuBar createMenuBar() {
0986: this .menuBar = new SubstanceMenuBar();
0987: this .menuBar.setFocusable(false);
0988: this .menuBar.setBorderPainted(true);
0989: this .menuBar.add(this .createMenu());
0990: this .menuBar.setOpaque(false);
0991: // support for RTL
0992: this .menuBar.applyComponentOrientation(this .rootPane
0993: .getComponentOrientation());
0994: return this .menuBar;
0995: }
0996:
0997: /**
0998: * Create the <code>Action</code>s that get associated with the buttons
0999: * and menu items.
1000: */
1001: private void createActions() {
1002: this .closeAction = new CloseAction();
1003: if (this .getWindowDecorationStyle() == JRootPane.FRAME) {
1004: this .iconifyAction = new IconifyAction();
1005: this .restoreAction = new RestoreAction();
1006: this .maximizeAction = new MaximizeAction();
1007: }
1008: }
1009:
1010: /**
1011: * Returns the <code>JMenu</code> displaying the appropriate menu items
1012: * for manipulating the Frame.
1013: *
1014: * @return <code>JMenu</code> displaying the appropriate menu items for
1015: * manipulating the Frame.
1016: */
1017: private JMenu createMenu() {
1018: JMenu menu = new JMenu("");
1019: menu.setOpaque(false);
1020: menu.setBackground(null);
1021: if (this .getWindowDecorationStyle() == JRootPane.FRAME) {
1022: this .addMenuItems(menu);
1023: }
1024: return menu;
1025: }
1026:
1027: /**
1028: * Adds the necessary <code>JMenuItem</code>s to the specified menu.
1029: *
1030: * @param menu
1031: * Menu.
1032: */
1033: private void addMenuItems(JMenu menu) {
1034: menu.add(this .restoreAction);
1035:
1036: menu.add(this .iconifyAction);
1037:
1038: if (Toolkit.getDefaultToolkit().isFrameStateSupported(
1039: Frame.MAXIMIZED_BOTH)) {
1040: menu.add(this .maximizeAction);
1041: }
1042:
1043: if (SubstanceLookAndFeel.toShowExtraElements()) {
1044: menu.addSeparator();
1045: JMenu skinMenu = new JMenu(SubstanceCoreUtilities
1046: .getResourceBundle(rootPane).getString(
1047: "SystemMenu.skins"));
1048: Map<String, SkinInfo> allSkins = SubstanceLookAndFeel
1049: .getAllSkins();
1050: for (Map.Entry<String, SkinInfo> skinEntry : allSkins
1051: .entrySet()) {
1052: final String skinClassName = skinEntry.getValue()
1053: .getClassName();
1054: JMenuItem jmiSkin = new JMenuItem(skinEntry.getKey());
1055: jmiSkin.addActionListener(new ActionListener() {
1056: public void actionPerformed(ActionEvent e) {
1057: SwingUtilities.invokeLater(new Runnable() {
1058: public void run() {
1059: SubstanceLookAndFeel
1060: .setSkin(skinClassName);
1061: }
1062: });
1063: }
1064: });
1065:
1066: // try {
1067: // SubstanceSkin skin = ((SubstanceSkin) Class.forName(
1068: // skinClassName).newInstance());
1069: // Container cont = rootPane.getParent();
1070: // if (cont instanceof Frame) {
1071: // Image icon = ((Frame) cont).getIconImage();
1072: // if (icon != null) {
1073: // jmiSkin.setIcon(new ImageIcon(SubstanceImageCreator
1074: // .getThemeImage(new ImageIcon(icon), skin
1075: // .getTheme())));
1076: // }
1077: // }
1078: // } catch (Exception exc) {
1079: // }
1080: //
1081: skinMenu.add(jmiSkin);
1082: }
1083: menu.add(skinMenu);
1084:
1085: JMenu themeMenu = new JMenu(SubstanceCoreUtilities
1086: .getResourceBundle(rootPane).getString(
1087: "SystemMenu.themes"));
1088: ButtonGroup bgTheme = new ButtonGroup();
1089: JMenu brightThemes = new JMenu(SubstanceCoreUtilities
1090: .getResourceBundle(rootPane).getString(
1091: "SystemMenu.themesBright"));
1092: JMenu coldThemes = new JMenu(SubstanceCoreUtilities
1093: .getResourceBundle(rootPane).getString(
1094: "SystemMenu.themesCold"));
1095: JMenu darkThemes = new JMenu(SubstanceCoreUtilities
1096: .getResourceBundle(rootPane).getString(
1097: "SystemMenu.themesDark"));
1098: JMenu invertedThemes = SubstanceLookAndFeel
1099: .toEnableInvertedThemes() ? new JMenu(
1100: SubstanceCoreUtilities.getResourceBundle(rootPane)
1101: .getString("SystemMenu.themesInverted"))
1102: : null;
1103: JMenu negatedThemes = SubstanceLookAndFeel
1104: .toEnableNegatedThemes() ? new JMenu(
1105: SubstanceCoreUtilities.getResourceBundle(rootPane)
1106: .getString("SystemMenu.themesNegated"))
1107: : null;
1108: JMenu mixedThemes = SubstanceLookAndFeel.hasMixedThemes() ? new JMenu(
1109: SubstanceCoreUtilities.getResourceBundle(rootPane)
1110: .getString("SystemMenu.themesMixed"))
1111: : null;
1112:
1113: themeMenu.add(brightThemes);
1114: themeMenu.add(coldThemes);
1115: themeMenu.add(darkThemes);
1116: if (invertedThemes != null)
1117: themeMenu.add(invertedThemes);
1118: if (negatedThemes != null)
1119: themeMenu.add(negatedThemes);
1120: if (mixedThemes != null)
1121: themeMenu.add(mixedThemes);
1122:
1123: this .themeMenuHandler = new TraitMenuHandler();
1124: Map<String, ThemeInfo> allThemes = SubstanceLookAndFeel
1125: .getAllThemes();
1126: for (Map.Entry<String, ThemeInfo> themeEntry : allThemes
1127: .entrySet()) {
1128: final ThemeInfo themeInfo = themeEntry.getValue();
1129: final String themeClassName = themeInfo.getClassName();
1130: JRadioButtonMenuItem jmiTheme = new JRadioButtonMenuItem(
1131: themeEntry.getKey());
1132: this .themeMenuHandler.addTraitButton(themeInfo
1133: .getDisplayName(), jmiTheme);
1134:
1135: jmiTheme.addActionListener(new ActionListener() {
1136: public void actionPerformed(ActionEvent e) {
1137: SwingUtilities.invokeLater(new Runnable() {
1138: public void run() {
1139: SubstanceLookAndFeel
1140: .setCurrentTheme(themeInfo);
1141: // update all existing root panes
1142: for (Frame frame : Frame.getFrames()) {
1143: SwingUtilities
1144: .updateComponentTreeUI(frame);
1145: }
1146: }
1147: });
1148: }
1149: });
1150: try {
1151: switch (themeInfo.getThemeKind()) {
1152: case MIXED:
1153: MixedThemeInfo mixedThemeInfo = (MixedThemeInfo) themeInfo;
1154: String[] themeClassNames = mixedThemeInfo
1155: .getThemeClassNames();
1156: SubstanceTheme[] themeInstances = new SubstanceTheme[themeClassNames.length];
1157: for (int i = 0; i < themeClassNames.length; i++) {
1158: Class<?> themeClass = Class
1159: .forName(themeClassNames[i]);
1160: themeInstances[i] = (SubstanceTheme) themeClass
1161: .newInstance();
1162: }
1163:
1164: SubstanceTheme mixTheme = new SubstanceMixTheme(
1165: themeInstances);
1166: jmiTheme.setIcon(SubstanceImageCreator
1167: .getThemeIcon(mixTheme));
1168: break;
1169: default:
1170: Class<?> themeClass = Class
1171: .forName(themeClassName);
1172: SubstanceTheme theme = (SubstanceTheme) themeClass
1173: .newInstance();
1174: if (themeInfo.getThemeKind() == ThemeKind.INVERTED)
1175: theme = new SubstanceInvertedTheme(theme);
1176: if (themeInfo.getThemeKind() == ThemeKind.NEGATED)
1177: theme = new SubstanceNegatedTheme(theme);
1178: jmiTheme.setIcon(SubstanceImageCreator
1179: .getThemeIcon(theme));
1180: }
1181: } catch (Exception exc) {
1182: continue;
1183: }
1184: if (themeEntry.getKey().equals(
1185: SubstanceLookAndFeel.getCurrentThemeName())) {
1186: jmiTheme.setSelected(true);
1187: }
1188: switch (themeEntry.getValue().getThemeKind()) {
1189: case BRIGHT:
1190: brightThemes.add(jmiTheme);
1191: break;
1192: case COLD:
1193: coldThemes.add(jmiTheme);
1194: break;
1195: case DARK:
1196: darkThemes.add(jmiTheme);
1197: break;
1198: case INVERTED:
1199: invertedThemes.add(jmiTheme);
1200: break;
1201: case NEGATED:
1202: negatedThemes.add(jmiTheme);
1203: break;
1204: case MIXED:
1205: mixedThemes.add(jmiTheme);
1206: break;
1207: }
1208: bgTheme.add(jmiTheme);
1209: }
1210:
1211: themeMenu.setIcon(SubstanceImageCreator.getThemeIcon(null));
1212: menu.add(themeMenu);
1213:
1214: this .themeChangeListener = new ThemeChangeListener() {
1215: public void themeChanged() {
1216: themeMenuHandler
1217: .selectTraitButton(SubstanceLookAndFeel
1218: .getTheme());
1219: }
1220: };
1221: SubstanceLookAndFeel
1222: .registerThemeChangeListener(this .themeChangeListener);
1223:
1224: JMenu watermarkMenu = new JMenu(SubstanceCoreUtilities
1225: .getResourceBundle(rootPane).getString(
1226: "SystemMenu.watermarks"));
1227: ButtonGroup bgWatermark = new ButtonGroup();
1228: this .watermarkMenuHandler = new TraitMenuHandler();
1229: Map<String, WatermarkInfo> allWatermarks = SubstanceLookAndFeel
1230: .getAllWatermarks();
1231: for (Map.Entry<String, WatermarkInfo> watermarkEntry : allWatermarks
1232: .entrySet()) {
1233: final String watermarkClassName = watermarkEntry
1234: .getValue().getClassName();
1235: JRadioButtonMenuItem jmiWatermark = new JRadioButtonMenuItem(
1236: watermarkEntry.getKey());
1237: this .watermarkMenuHandler.addTraitButton(watermarkEntry
1238: .getValue().getDisplayName(), jmiWatermark);
1239: jmiWatermark.addActionListener(new ActionListener() {
1240: public void actionPerformed(ActionEvent e) {
1241: SwingUtilities.invokeLater(new Runnable() {
1242: public void run() {
1243: SubstanceLookAndFeel
1244: .setCurrentWatermark(watermarkClassName);
1245: // update all existing root panes
1246: for (Frame frame : Frame.getFrames()) {
1247: SwingUtilities
1248: .updateComponentTreeUI(frame);
1249: }
1250: }
1251: });
1252: }
1253: });
1254: if (watermarkEntry.getKey().equals(
1255: SubstanceLookAndFeel.getCurrentWatermarkName())) {
1256: jmiWatermark.setSelected(true);
1257: }
1258:
1259: try {
1260: Class<?> watermarkClass = Class
1261: .forName(watermarkClassName);
1262: SubstanceWatermark watermark = (SubstanceWatermark) watermarkClass
1263: .newInstance();
1264: jmiWatermark.setIcon(SubstanceImageCreator
1265: .getWatermarkIcon(watermark));
1266: } catch (Exception exc) {
1267: }
1268:
1269: bgWatermark.add(jmiWatermark);
1270: watermarkMenu.add(jmiWatermark);
1271: }
1272: menu.add(watermarkMenu);
1273: this .watermarkChangeListener = new WatermarkChangeListener() {
1274: public void watermarkChanged() {
1275: watermarkMenuHandler
1276: .selectTraitButton(SubstanceLookAndFeel
1277: .getCurrentWatermark());
1278: }
1279: };
1280: SubstanceLookAndFeel
1281: .registerWatermarkChangeListener(this .watermarkChangeListener);
1282:
1283: JMenu buttonShaperMenu = new JMenu(SubstanceCoreUtilities
1284: .getResourceBundle(rootPane).getString(
1285: "SystemMenu.buttonShapers"));
1286: ButtonGroup bgButtonShaper = new ButtonGroup();
1287: this .buttonShaperMenuHandler = new TraitMenuHandler();
1288: Map<String, ButtonShaperInfo> allButtonShapers = SubstanceLookAndFeel
1289: .getAllButtonShapers();
1290: for (Map.Entry<String, ButtonShaperInfo> buttonShaperEntry : allButtonShapers
1291: .entrySet()) {
1292: final String buttonShaperClassName = buttonShaperEntry
1293: .getValue().getClassName();
1294: JRadioButtonMenuItem jmiButtonShaper = new JRadioButtonMenuItem(
1295: buttonShaperEntry.getKey());
1296: this .buttonShaperMenuHandler.addTraitButton(
1297: buttonShaperEntry.getValue().getDisplayName(),
1298: jmiButtonShaper);
1299: jmiButtonShaper.addActionListener(new ActionListener() {
1300: public void actionPerformed(ActionEvent e) {
1301: SwingUtilities.invokeLater(new Runnable() {
1302: public void run() {
1303: SubstanceLookAndFeel
1304: .setCurrentButtonShaper(buttonShaperClassName);
1305: // update all existing root panes
1306: for (Frame frame : Frame.getFrames()) {
1307: SwingUtilities
1308: .updateComponentTreeUI(frame);
1309: }
1310: }
1311: });
1312: }
1313: });
1314: if (buttonShaperEntry.getKey().equals(
1315: SubstanceLookAndFeel
1316: .getCurrentButtonShaperName())) {
1317: jmiButtonShaper.setSelected(true);
1318: }
1319: bgButtonShaper.add(jmiButtonShaper);
1320: buttonShaperMenu.add(jmiButtonShaper);
1321: }
1322: menu.add(buttonShaperMenu);
1323: this .buttonShaperChangeListener = new ButtonShaperChangeListener() {
1324: public void buttonShaperChanged() {
1325: buttonShaperMenuHandler
1326: .selectTraitButton(SubstanceLookAndFeel
1327: .getCurrentButtonShaper());
1328: }
1329: };
1330: SubstanceLookAndFeel
1331: .registerButtonShaperChangeListener(this .buttonShaperChangeListener);
1332:
1333: JMenu borderPainterMenu = new JMenu(SubstanceCoreUtilities
1334: .getResourceBundle(rootPane).getString(
1335: "SystemMenu.borderPainters"));
1336: ButtonGroup bgBorderPainter = new ButtonGroup();
1337: this .borderPainterMenuHandler = new TraitMenuHandler();
1338: Map<String, BorderPainterInfo> allBorderPainters = SubstanceLookAndFeel
1339: .getAllBorderPainters();
1340: for (Map.Entry<String, BorderPainterInfo> borderPainterEntry : allBorderPainters
1341: .entrySet()) {
1342: final String borderPainterClassName = borderPainterEntry
1343: .getValue().getClassName();
1344: JRadioButtonMenuItem jmiBorderPainter = new JRadioButtonMenuItem(
1345: borderPainterEntry.getKey());
1346: this .borderPainterMenuHandler.addTraitButton(
1347: borderPainterEntry.getValue().getDisplayName(),
1348: jmiBorderPainter);
1349: jmiBorderPainter
1350: .addActionListener(new ActionListener() {
1351: public void actionPerformed(ActionEvent e) {
1352: SwingUtilities
1353: .invokeLater(new Runnable() {
1354: public void run() {
1355:
1356: SubstanceLookAndFeel
1357: .setCurrentBorderPainter(borderPainterClassName);
1358: // update all existing root panes
1359: for (Frame frame : Frame
1360: .getFrames()) {
1361: SwingUtilities
1362: .updateComponentTreeUI(frame);
1363: }
1364: }
1365: });
1366: }
1367: });
1368: if (borderPainterEntry.getKey().equals(
1369: SubstanceLookAndFeel
1370: .getCurrentBorderPainterName())) {
1371: jmiBorderPainter.setSelected(true);
1372: }
1373: bgBorderPainter.add(jmiBorderPainter);
1374: borderPainterMenu.add(jmiBorderPainter);
1375: }
1376: menu.add(borderPainterMenu);
1377: this .borderPainterChangeListener = new BorderPainterChangeListener() {
1378: public void borderPainterChanged() {
1379: borderPainterMenuHandler
1380: .selectTraitButton(SubstanceLookAndFeel
1381: .getCurrentBorderPainter());
1382: }
1383: };
1384: SubstanceLookAndFeel
1385: .registerBorderPainterChangeListener(this .borderPainterChangeListener);
1386:
1387: JMenu gradientPainterMenu = new JMenu(
1388: SubstanceCoreUtilities.getResourceBundle(rootPane)
1389: .getString("SystemMenu.gradientPainters"));
1390: ButtonGroup bgGradientPainter = new ButtonGroup();
1391: this .gradientPainterMenuHandler = new TraitMenuHandler();
1392: Map<String, GradientPainterInfo> allGradientPainters = SubstanceLookAndFeel
1393: .getAllGradientPainters();
1394: for (Map.Entry<String, GradientPainterInfo> gradientPainterEntry : allGradientPainters
1395: .entrySet()) {
1396: final String gradientPainterClassName = gradientPainterEntry
1397: .getValue().getClassName();
1398: JRadioButtonMenuItem jmiGradientPainter = new JRadioButtonMenuItem(
1399: gradientPainterEntry.getKey());
1400: this .gradientPainterMenuHandler.addTraitButton(
1401: gradientPainterEntry.getValue()
1402: .getDisplayName(), jmiGradientPainter);
1403: jmiGradientPainter
1404: .addActionListener(new ActionListener() {
1405: public void actionPerformed(ActionEvent e) {
1406: SwingUtilities
1407: .invokeLater(new Runnable() {
1408: public void run() {
1409: SubstanceLookAndFeel
1410: .setCurrentGradientPainter(gradientPainterClassName);
1411: // update all existing root panes
1412: for (Frame frame : Frame
1413: .getFrames()) {
1414: SwingUtilities
1415: .updateComponentTreeUI(frame);
1416: }
1417: }
1418: });
1419: }
1420: });
1421: if (gradientPainterEntry.getKey().equals(
1422: SubstanceLookAndFeel
1423: .getCurrentGradientPainterName())) {
1424: jmiGradientPainter.setSelected(true);
1425: }
1426: bgGradientPainter.add(jmiGradientPainter);
1427: gradientPainterMenu.add(jmiGradientPainter);
1428: }
1429: menu.add(gradientPainterMenu);
1430: this .gradientPainterChangeListener = new GradientPainterChangeListener() {
1431: public void gradientPainterChanged() {
1432: gradientPainterMenuHandler
1433: .selectTraitButton(SubstanceLookAndFeel
1434: .getCurrentGradientPainter());
1435: }
1436: };
1437: SubstanceLookAndFeel
1438: .registerGradientPainterChangeListener(this .gradientPainterChangeListener);
1439:
1440: if (SubstanceTitlePane.canHaveHeapStatusPanel
1441: && (!(this .rootPane
1442: .getClientProperty(SubstanceTitlePane.HEAP_STATUS_PANEL_PERMANENT) instanceof Boolean))) {
1443: this .heapStatusMenuItem = new JCheckBoxMenuItem(
1444: SubstanceCoreUtilities.getResourceBundle(
1445: rootPane).getString(
1446: "SystemMenu.showHeapStatus"));
1447: boolean isHeapStatusPanelShowing = Boolean.TRUE
1448: .equals(this .rootPane
1449: .getClientProperty(SubstanceLookAndFeel.HEAP_STATUS_PANEL));
1450: this .heapStatusMenuItem
1451: .setSelected(isHeapStatusPanelShowing);
1452: this .heapStatusMenuItem
1453: .addActionListener(new ActionListener() {
1454: public void actionPerformed(ActionEvent e) {
1455: SubstanceTitlePane.this .heapStatusPanel
1456: .setVisible(!SubstanceTitlePane.this .heapStatusPanel
1457: .isVisible());
1458: SubstanceTitlePane.this .rootPane
1459: .putClientProperty(
1460: SubstanceLookAndFeel.HEAP_STATUS_PANEL,
1461: Boolean
1462: .valueOf(SubstanceTitlePane.this .heapStatusPanel
1463: .isVisible()));
1464: }
1465: });
1466: menu.add(this .heapStatusMenuItem);
1467: }
1468: }
1469:
1470: menu.addSeparator();
1471:
1472: menu.add(this .closeAction);
1473: }
1474:
1475: /**
1476: * Returns a <code>JButton</code> appropriate for placement on the
1477: * TitlePane.
1478: *
1479: * @return Title button.
1480: */
1481: private JButton createTitleButton() {
1482: JButton button = new SubstanceTitleButton();
1483:
1484: button.setFocusPainted(false);
1485: button.setFocusable(false);
1486: button.setOpaque(true);
1487:
1488: return button;
1489: }
1490:
1491: /**
1492: * Creates the Buttons that will be placed on the TitlePane.
1493: */
1494: private void createButtons() {
1495: SubstanceTheme iconTheme = SubstanceLookAndFeel.getTheme()
1496: .getActiveTitlePaneTheme();
1497:
1498: this .closeButton = this .createTitleButton();
1499: this .closeButton.setAction(this .closeAction);
1500: this .closeButton.setText(null);
1501: this .closeButton.putClientProperty("paintActive", Boolean.TRUE);
1502: this .closeButton.setBorder(null);
1503: // this.closeButton.setToolTipText(SubstanceLookAndFeel
1504: // .getLabelBundle().getString(
1505: // "SystemMenu.close"));
1506:
1507: Icon closeIcon = new TransitionAwareIcon(closeButton,
1508: new TransitionAwareIcon.Delegate() {
1509: public Icon getThemeIcon(SubstanceTheme theme) {
1510: return SubstanceIconFactory.getTitlePaneIcon(
1511: SubstanceIconFactory.IconKind.CLOSE,
1512: theme);
1513: }
1514: });
1515: this .closeButton.setIcon(closeIcon);
1516:
1517: this .closeButton.setFocusable(false);
1518: this .closeButton.putClientProperty(
1519: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
1520:
1521: this .closeButton.putClientProperty(
1522: SubstanceButtonUI.IS_TITLE_CLOSE_BUTTON, Boolean.TRUE);
1523:
1524: if (this .getWindowDecorationStyle() == JRootPane.FRAME) {
1525: // Icon maximizeIcon = SubstanceIconFactory.getTitlePaneIcon(
1526: // SubstanceIconFactory.IconKind.MAXIMIZE, iconTheme);
1527:
1528: // Icon restoreSizeIcon = SubstanceIconFactory.getTitlePaneIcon(
1529: // SubstanceIconFactory.IconKind.RESTORE, SubstanceLookAndFeel
1530: // .getColorScheme());
1531:
1532: this .minimizeButton = this .createTitleButton();
1533: this .minimizeButton.setAction(this .iconifyAction);
1534: this .minimizeButton.setText(null);
1535: this .minimizeButton.putClientProperty("paintActive",
1536: Boolean.TRUE);
1537: this .minimizeButton.setBorder(null);
1538:
1539: Icon minIcon = new TransitionAwareIcon(this .minimizeButton,
1540: new TransitionAwareIcon.Delegate() {
1541: public Icon getThemeIcon(SubstanceTheme theme) {
1542: return SubstanceIconFactory
1543: .getTitlePaneIcon(
1544: SubstanceIconFactory.IconKind.MINIMIZE,
1545: theme);
1546: }
1547: });
1548: this .minimizeButton.setIcon(minIcon);
1549:
1550: this .minimizeButton.setFocusable(false);
1551: this .minimizeButton.putClientProperty(
1552: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
1553: this .minimizeButton.setToolTipText(SubstanceCoreUtilities
1554: .getResourceBundle(rootPane).getString(
1555: "SystemMenu.iconify"));
1556:
1557: this .toggleButton = this .createTitleButton();
1558: this .toggleButton.setAction(this .restoreAction);
1559: this .toggleButton.putClientProperty("paintActive",
1560: Boolean.TRUE);
1561: this .toggleButton.setBorder(null);
1562: this .toggleButton.setText(null);
1563:
1564: Icon maxIcon = new TransitionAwareIcon(this .toggleButton,
1565: new TransitionAwareIcon.Delegate() {
1566: public Icon getThemeIcon(SubstanceTheme theme) {
1567: return SubstanceIconFactory
1568: .getTitlePaneIcon(
1569: SubstanceIconFactory.IconKind.MAXIMIZE,
1570: theme);
1571: }
1572: });
1573: this .toggleButton.setIcon(maxIcon);
1574:
1575: this .toggleButton.setToolTipText(SubstanceCoreUtilities
1576: .getResourceBundle(rootPane).getString(
1577: "SystemMenu.maximize"));
1578: this .toggleButton.setFocusable(false);
1579: this .toggleButton.putClientProperty(
1580: SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
1581:
1582: }
1583: syncCloseButtonTooltip();
1584: }
1585:
1586: /**
1587: * Returns the <code>LayoutManager</code> that should be installed on the
1588: * <code>SubstanceTitlePane</code>.
1589: *
1590: * @return Layout manager.
1591: */
1592: private LayoutManager createLayout() {
1593: return new TitlePaneLayout();
1594: }
1595:
1596: /**
1597: * Updates state dependant upon the Window's active state.
1598: *
1599: * @param isActive
1600: * if <code>true</code>, the window is in active state.
1601: */
1602: private void setActive(boolean isActive) {
1603: Boolean activeB = isActive ? Boolean.TRUE : Boolean.FALSE;
1604:
1605: if (this .getWindowDecorationStyle() == JRootPane.FRAME) {
1606: this .closeButton.putClientProperty("paintActive", activeB);
1607: this .minimizeButton.putClientProperty("paintActive",
1608: activeB);
1609: this .toggleButton.putClientProperty("paintActive", activeB);
1610: }
1611: this .getRootPane().repaint();
1612: }
1613:
1614: /**
1615: * Sets the state of the Window.
1616: *
1617: * @param state
1618: * Window state.
1619: */
1620: private void setState(int state) {
1621: this .setState(state, false);
1622: }
1623:
1624: /**
1625: * Sets the state of the window. If <code>updateRegardless</code> is true
1626: * and the state has not changed, this will update anyway.
1627: *
1628: * @param state
1629: * Window state.
1630: * @param updateRegardless
1631: * if <code>true</code>, the update is done in any case.
1632: */
1633: private void setState(int state, boolean updateRegardless) {
1634: Window w = this .getWindow();
1635:
1636: if ((w != null)
1637: && (this .getWindowDecorationStyle() == JRootPane.FRAME)) {
1638: if ((this .state == state) && !updateRegardless) {
1639: return;
1640: }
1641: Frame frame = this .getFrame();
1642:
1643: if (frame != null) {
1644: JRootPane rootPane = this .getRootPane();
1645:
1646: if (((state & Frame.MAXIMIZED_BOTH) != 0)
1647: && ((rootPane.getBorder() == null) || (rootPane
1648: .getBorder() instanceof UIResource))
1649: && frame.isShowing()) {
1650: rootPane.setBorder(null);
1651: } else {
1652: if ((state & Frame.MAXIMIZED_BOTH) == 0) {
1653: // This is a croak, if state becomes bound, this can
1654: // be nuked.
1655: this .rootPaneUI.installBorder(rootPane);
1656: }
1657: }
1658: if (frame.isResizable()) {
1659: // special handling of mixed dark themes
1660: SubstanceTheme iconTheme = SubstanceLookAndFeel
1661: .getTheme().getActiveTitlePaneTheme();
1662: // if (iconTheme.getKind() == ThemeKind.DARK_MIXED)
1663: // iconTheme = ((SubstanceMixDarkBiTheme) iconTheme)
1664: // .getOriginalDarkTheme();
1665:
1666: if ((state & Frame.MAXIMIZED_BOTH) != 0) {
1667: Icon restoreIcon = new TransitionAwareIcon(
1668: this .toggleButton,
1669: new TransitionAwareIcon.Delegate() {
1670: public Icon getThemeIcon(
1671: SubstanceTheme theme) {
1672: return SubstanceIconFactory
1673: .getTitlePaneIcon(
1674: SubstanceIconFactory.IconKind.RESTORE,
1675: theme);
1676: }
1677: });
1678: this .updateToggleButton(this .restoreAction,
1679: restoreIcon);
1680: this .toggleButton
1681: .setToolTipText(SubstanceCoreUtilities
1682: .getResourceBundle(rootPane)
1683: .getString("SystemMenu.restore"));
1684: this .maximizeAction.setEnabled(false);
1685: this .restoreAction.setEnabled(true);
1686: } else {
1687: Icon maxIcon = new TransitionAwareIcon(
1688: this .toggleButton,
1689: new TransitionAwareIcon.Delegate() {
1690: public Icon getThemeIcon(
1691: SubstanceTheme theme) {
1692: return SubstanceIconFactory
1693: .getTitlePaneIcon(
1694: SubstanceIconFactory.IconKind.MAXIMIZE,
1695: theme);
1696: }
1697: });
1698: this .updateToggleButton(this .maximizeAction,
1699: maxIcon);
1700: this .toggleButton
1701: .setToolTipText(SubstanceCoreUtilities
1702: .getResourceBundle(rootPane)
1703: .getString(
1704: "SystemMenu.maximize"));
1705: this .maximizeAction.setEnabled(true);
1706: this .restoreAction.setEnabled(false);
1707: }
1708: if ((this .toggleButton.getParent() == null)
1709: || (this .minimizeButton.getParent() == null)) {
1710: this .add(this .toggleButton);
1711: this .add(this .minimizeButton);
1712: this .revalidate();
1713: this .repaint();
1714: }
1715: this .toggleButton.setText(null);
1716: } else {
1717: this .maximizeAction.setEnabled(false);
1718: this .restoreAction.setEnabled(false);
1719: if (this .toggleButton.getParent() != null) {
1720: this .remove(this .toggleButton);
1721: this .revalidate();
1722: this .repaint();
1723: }
1724: }
1725: } else {
1726: // Not contained in a Frame
1727: this .maximizeAction.setEnabled(false);
1728: this .restoreAction.setEnabled(false);
1729: this .iconifyAction.setEnabled(false);
1730: this .remove(this .toggleButton);
1731: this .remove(this .minimizeButton);
1732: this .revalidate();
1733: this .repaint();
1734: }
1735: this .closeAction.setEnabled(true);
1736: this .state = state;
1737: }
1738: }
1739:
1740: /**
1741: * Updates the toggle button to contain the Icon <code>icon</code>, and
1742: * Action <code>action</code>.
1743: *
1744: * @param action
1745: * Action.
1746: * @param icon
1747: * Icon.
1748: */
1749: private void updateToggleButton(Action action, Icon icon) {
1750: this .toggleButton.setAction(action);
1751: this .toggleButton.setIcon(icon);
1752: this .toggleButton.setText(null);
1753: }
1754:
1755: /**
1756: * Returns the Frame rendering in. This will return null if the
1757: * <code>JRootPane</code> is not contained in a <code>Frame</code>.
1758: *
1759: * @return Frame.
1760: */
1761: private Frame getFrame() {
1762: Window window = this .getWindow();
1763:
1764: if (window instanceof Frame) {
1765: return (Frame) window;
1766: }
1767: return null;
1768: }
1769:
1770: /**
1771: * Returns the <code>Window</code> the <code>JRootPane</code> is
1772: * contained in. This will return null if there is no parent ancestor of the
1773: * <code>JRootPane</code>.
1774: *
1775: * @return Window.
1776: */
1777: private Window getWindow() {
1778: return this .window;
1779: }
1780:
1781: /**
1782: * Returns the String to display as the title.
1783: *
1784: * @return Display title.
1785: */
1786: private String getTitle() {
1787: Window w = this .getWindow();
1788:
1789: if (w instanceof Frame) {
1790: return ((Frame) w).getTitle();
1791: }
1792: if (w instanceof Dialog) {
1793: return ((Dialog) w).getTitle();
1794: }
1795: return null;
1796: }
1797:
1798: /*
1799: * (non-Javadoc)
1800: *
1801: * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
1802: */
1803: @Override
1804: public void paintComponent(Graphics g) {
1805: // long start = System.nanoTime();
1806: // As state isn't bound, we need a convenience place to check
1807: // if it has changed. Changing the state typically changes the
1808: if (this .getFrame() != null) {
1809: this .setState(this .getFrame().getExtendedState());
1810: }
1811: final JRootPane rootPane = this .getRootPane();
1812: Window window = this .getWindow();
1813: boolean leftToRight = (window == null) ? rootPane
1814: .getComponentOrientation().isLeftToRight() : window
1815: .getComponentOrientation().isLeftToRight();
1816: boolean isSelected = (window == null) ? true : window
1817: .isActive();
1818: final int width = this .getWidth();
1819: final int height = this .getHeight();
1820:
1821: final SubstanceTheme theme = isSelected ? SubstanceLookAndFeel
1822: .getTheme().getActiveTitlePaneTheme()
1823: : SubstanceLookAndFeel.getTheme()
1824: .getDefaultTitlePaneTheme();
1825:
1826: int xOffset = 0;
1827: String theTitle = this .getTitle();
1828: int leftEnd;
1829: int rightEnd;
1830: if (leftToRight) {
1831: // offset of border
1832: xOffset = 5;
1833:
1834: leftEnd = (this .menuBar == null) ? 0 : (this .menuBar
1835: .getWidth() + 10);
1836: xOffset += leftEnd;
1837:
1838: // find the leftmost button for the right end
1839: AbstractButton leftmostButton = null;
1840:
1841: if ((this .minimizeButton != null)
1842: && (this .minimizeButton.getParent() != null)
1843: && (this .minimizeButton.getBounds().width != 0)) {
1844: leftmostButton = this .minimizeButton;
1845: } else {
1846: if ((this .toggleButton != null)
1847: && (this .toggleButton.getParent() != null)
1848: && (this .toggleButton.getBounds().width != 0)) {
1849: leftmostButton = this .toggleButton;
1850: } else {
1851: if ((this .closeButton != null)
1852: && (this .closeButton.getParent() != null)) {
1853: leftmostButton = this .closeButton;
1854: }
1855: }
1856: }
1857:
1858: rightEnd = this .getWidth();
1859: if (leftmostButton != null) {
1860: Rectangle rect = leftmostButton.getBounds();
1861: rightEnd = rect.getBounds().x - 5;
1862: if ((this .heapStatusPanel != null)
1863: && (this .heapStatusPanel.isVisible()))
1864: rightEnd = this .heapStatusPanel.getBounds().x - 5;
1865: rightEnd--;
1866: }
1867:
1868: if (theTitle != null) {
1869: FontMetrics fm = rootPane.getFontMetrics(g.getFont());
1870: int titleWidth = rightEnd - leftEnd - 20;
1871: String clippedTitle = SubstanceCoreUtilities
1872: .clipString(fm, titleWidth, theTitle);
1873: // show tooltip with full title only if necessary
1874: if (theTitle.equals(clippedTitle))
1875: this .setToolTipText(null);
1876: else
1877: this .setToolTipText(theTitle);
1878: theTitle = clippedTitle;
1879: }
1880: } else {
1881: // RTL support
1882:
1883: xOffset = width - 5;
1884:
1885: rightEnd = (this .menuBar == null) ? width - 5 : width - 5
1886: - this .menuBar.getWidth() - 10;
1887:
1888: // find the rightmost button for the left transition band
1889: AbstractButton rightmostButton = null;
1890:
1891: if ((this .minimizeButton != null)
1892: && (this .minimizeButton.getParent() != null)
1893: && (this .minimizeButton.getBounds().width != 0)) {
1894: rightmostButton = this .minimizeButton;
1895: } else {
1896: if ((this .toggleButton != null)
1897: && (this .toggleButton.getParent() != null)
1898: && (this .toggleButton.getBounds().width != 0)) {
1899: rightmostButton = this .toggleButton;
1900: } else {
1901: if ((this .closeButton != null)
1902: && (this .closeButton.getParent() != null)) {
1903: rightmostButton = this .closeButton;
1904: }
1905: }
1906: }
1907:
1908: leftEnd = 5;
1909: if (rightmostButton != null) {
1910: Rectangle rect = rightmostButton.getBounds();
1911: leftEnd = (int) rect.getBounds().getMaxX() + 5;
1912: if ((this .heapStatusPanel != null)
1913: && (this .heapStatusPanel.isVisible()))
1914: leftEnd = (int) this .heapStatusPanel.getBounds()
1915: .getMaxX() + 5;
1916: leftEnd++;
1917: }
1918:
1919: if (theTitle != null) {
1920: FontMetrics fm = rootPane.getFontMetrics(g.getFont());
1921: int titleWidth = rightEnd - leftEnd - 20;
1922: String clippedTitle = SubstanceCoreUtilities
1923: .clipString(fm, titleWidth, theTitle);
1924: // show tooltip with full title only if necessary
1925: if (theTitle.equals(clippedTitle)) {
1926: this .setToolTipText(null);
1927: } else {
1928: this .setToolTipText(theTitle);
1929: }
1930: theTitle = clippedTitle;
1931: xOffset = rightEnd - fm.stringWidth(theTitle);
1932: }
1933: }
1934:
1935: Graphics2D graphics = (Graphics2D) g.create();
1936: Font font = SubstanceLookAndFeel.getFontPolicy().getFontSet(
1937: "Substance", null).getWindowTitleFont();
1938: graphics.setFont(font);
1939:
1940: SubstanceTextPainter textPainter = SubstanceLookAndFeel
1941: .getCurrentTextPainter();
1942: textPainter.init(this , null, true);
1943:
1944: if (textPainter.needsBackgroundImage()) {
1945: textPainter
1946: .attachCallback(new SubstanceTextPainter.BackgroundPaintingCallback() {
1947: public void paintBackground(Graphics g) {
1948: SubstanceDecorationUtilities
1949: .paintDecorationBackground(g,
1950: SubstanceTitlePane.this ,
1951: false);
1952: }
1953: });
1954: } else {
1955: SubstanceDecorationUtilities.paintDecorationBackground(
1956: graphics, SubstanceTitlePane.this , false);
1957: }
1958:
1959: // draw the title (if needed)
1960: if (theTitle != null) {
1961: FontMetrics fm = rootPane
1962: .getFontMetrics(graphics.getFont());
1963: int yOffset = ((height - fm.getHeight()) / 2)
1964: + fm.getAscent();
1965:
1966: SubstanceCoreUtilities.paintTextWithDropShadow(this ,
1967: graphics, theme.getForegroundColor(), theTitle,
1968: width, height, xOffset, yOffset);
1969: }
1970: textPainter.renderSurface(graphics);
1971:
1972: GhostPaintingUtils.paintGhostImages(this , graphics);
1973:
1974: // long end = System.nanoTime();
1975: // System.out.println(end - start);
1976: graphics.dispose();
1977: }
1978:
1979: // public Color getTitlePaneForegroundColor(SubstanceTheme theme) {
1980: // return SubstanceCoreUtilities.isThemeDark(theme) ? theme
1981: // .getColorScheme().getForegroundColor()
1982: // : new Color(
1983: // SubstanceColorUtilities
1984: // .getInterpolatedRGB(theme.getColorScheme()
1985: // .getUltraDarkColor(), theme
1986: // .getColorScheme()
1987: // .getForegroundColor(), 0.5));
1988: // }
1989:
1990: /**
1991: * Actions used to <code>close</code> the <code>Window</code>.
1992: */
1993: private class CloseAction extends AbstractAction {
1994: /**
1995: * Creates a new close action.
1996: */
1997: public CloseAction() {
1998: super (SubstanceCoreUtilities.getResourceBundle(rootPane)
1999: .getString("SystemMenu.close"),
2000: SubstanceImageCreator
2001: .getCloseIcon(SubstanceLookAndFeel
2002: .getTheme()));
2003: }
2004:
2005: public void actionPerformed(ActionEvent e) {
2006: Window window = SubstanceTitlePane.this .getWindow();
2007:
2008: if (window != null) {
2009: window.dispatchEvent(new WindowEvent(window,
2010: WindowEvent.WINDOW_CLOSING));
2011: }
2012: }
2013: }
2014:
2015: /**
2016: * Actions used to <code>iconfiy</code> the <code>Frame</code>.
2017: */
2018: private class IconifyAction extends AbstractAction {
2019: /**
2020: * Creates a new iconify action.
2021: */
2022: public IconifyAction() {
2023: super (SubstanceCoreUtilities.getResourceBundle(rootPane)
2024: .getString("SystemMenu.iconify"),
2025: SubstanceImageCreator
2026: .getMinimizeIcon(SubstanceLookAndFeel
2027: .getTheme()));
2028: }
2029:
2030: public void actionPerformed(ActionEvent e) {
2031: Frame frame = SubstanceTitlePane.this .getFrame();
2032: if (frame != null) {
2033: frame.setExtendedState(SubstanceTitlePane.this .state
2034: | Frame.ICONIFIED);
2035: }
2036: }
2037: }
2038:
2039: /**
2040: * Actions used to <code>restore</code> the <code>Frame</code>.
2041: */
2042: private class RestoreAction extends AbstractAction {
2043: /**
2044: * Creates a new restore action.
2045: */
2046: public RestoreAction() {
2047: super (SubstanceCoreUtilities.getResourceBundle(rootPane)
2048: .getString("SystemMenu.restore"),
2049: SubstanceImageCreator
2050: .getRestoreIcon(SubstanceLookAndFeel
2051: .getTheme()));
2052: }
2053:
2054: public void actionPerformed(ActionEvent e) {
2055: Frame frame = SubstanceTitlePane.this .getFrame();
2056:
2057: if (frame == null) {
2058: return;
2059: }
2060:
2061: if ((SubstanceTitlePane.this .state & Frame.ICONIFIED) != 0) {
2062: frame.setExtendedState(SubstanceTitlePane.this .state
2063: & ~Frame.ICONIFIED);
2064: } else {
2065: frame.setExtendedState(SubstanceTitlePane.this .state
2066: & ~Frame.MAXIMIZED_BOTH);
2067: }
2068: }
2069: }
2070:
2071: /**
2072: * Actions used to <code>restore</code> the <code>Frame</code>.
2073: */
2074: private class MaximizeAction extends AbstractAction {
2075: /**
2076: * Creates a new maximize action.
2077: */
2078: public MaximizeAction() {
2079: super (SubstanceCoreUtilities.getResourceBundle(rootPane)
2080: .getString("SystemMenu.maximize"),
2081: SubstanceImageCreator
2082: .getMaximizeIcon(SubstanceLookAndFeel
2083: .getTheme()));
2084: }
2085:
2086: public void actionPerformed(ActionEvent e) {
2087: Frame frame = SubstanceTitlePane.this .getFrame();
2088: if (frame != null) {
2089: if (frame instanceof JFrame) {
2090: SubstanceRootPaneUI rpUI = (SubstanceRootPaneUI) ((JFrame) frame)
2091: .getRootPane().getUI();
2092: rpUI.setMaximized();
2093: }
2094: frame.setExtendedState(SubstanceTitlePane.this .state
2095: | Frame.MAXIMIZED_BOTH);
2096: }
2097: }
2098: }
2099:
2100: /**
2101: * Class responsible for drawing the system menu. Looks up the image to draw
2102: * from the Frame associated with the <code>JRootPane</code>.
2103: */
2104: public class SubstanceMenuBar extends JMenuBar {
2105: @Override
2106: public void paint(Graphics g) {
2107: Frame frame = SubstanceTitlePane.this .getFrame();
2108:
2109: Image image = (frame != null) ? frame.getIconImage() : null;
2110:
2111: if (image != null) {
2112: int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
2113: double coef = Math.max((double) iSize
2114: / (double) image.getWidth(null), (double) iSize
2115: / (double) image.getHeight(null));
2116: if (coef < 1.0) {
2117: // fix for defect 255 - large icons need to be properly
2118: // scaled down. While we can use
2119: // RenderingHints.VALUE_INTERPOLATION_BILINEAR, it will not
2120: // work good on large icons (such as 128*128), resulting in
2121: // bad images. Here, we use multi-step scaling by Romain
2122: // Guy.
2123: BufferedImage bi = SubstanceCoreUtilities
2124: .getBlankImage(image.getWidth(null), image
2125: .getHeight(null));
2126: bi.getGraphics().drawImage(image, 0, 0, null);
2127: g.drawImage(LafWidgetUtilities.createThumbnail(bi,
2128: iSize), 0, 0, null);
2129: } else
2130: g.drawImage(image, 0, 0, null);
2131: } else {
2132: Icon icon = UIManager.getIcon("InternalFrame.icon");
2133:
2134: if (icon != null) {
2135: icon.paintIcon(this , g, 0, 0);
2136: }
2137: }
2138: }
2139:
2140: @Override
2141: public Dimension getMinimumSize() {
2142: return this .getPreferredSize();
2143: }
2144:
2145: @Override
2146: public Dimension getPreferredSize() {
2147: Dimension size = super .getPreferredSize();
2148:
2149: int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
2150: return new Dimension(Math.max(iSize, size.width), Math.max(
2151: size.height, iSize));
2152: }
2153: }
2154:
2155: /**
2156: * Layout manager for the title pane.
2157: *
2158: * @author Kirill Graphics
2159: */
2160: private class TitlePaneLayout implements LayoutManager {
2161: /*
2162: * (non-Javadoc)
2163: *
2164: * @see java.awt.LayoutManager#addLayoutComponent(java.lang.String,
2165: * java.awt.Component)
2166: */
2167: public void addLayoutComponent(String name, Component c) {
2168: }
2169:
2170: /*
2171: * (non-Javadoc)
2172: *
2173: * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
2174: */
2175: public void removeLayoutComponent(Component c) {
2176: }
2177:
2178: /*
2179: * (non-Javadoc)
2180: *
2181: * @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
2182: */
2183: public Dimension preferredLayoutSize(Container c) {
2184: int height = this .computeHeight();
2185: return new Dimension(height, height);
2186: }
2187:
2188: /**
2189: * Computes title pane height.
2190: *
2191: * @return Title pane height.
2192: */
2193: private int computeHeight() {
2194: FontMetrics fm = SubstanceTitlePane.this .rootPane
2195: .getFontMetrics(SubstanceTitlePane.this .getFont());
2196: int fontHeight = fm.getHeight();
2197: fontHeight += 7;
2198: int iconHeight = 0;
2199: if (SubstanceTitlePane.this .getWindowDecorationStyle() == JRootPane.FRAME) {
2200: iconHeight = SubstanceSizeUtils.getTitlePaneIconSize();
2201: }
2202:
2203: int finalHeight = Math.max(fontHeight, iconHeight);
2204: return finalHeight;
2205: }
2206:
2207: /*
2208: * (non-Javadoc)
2209: *
2210: * @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
2211: */
2212: public Dimension minimumLayoutSize(Container c) {
2213: return this .preferredLayoutSize(c);
2214: }
2215:
2216: /*
2217: * (non-Javadoc)
2218: *
2219: * @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
2220: */
2221: public void layoutContainer(Container c) {
2222: boolean leftToRight = (SubstanceTitlePane.this .window == null) ? SubstanceTitlePane.this
2223: .getRootPane().getComponentOrientation()
2224: .isLeftToRight()
2225: : SubstanceTitlePane.this .window
2226: .getComponentOrientation().isLeftToRight();
2227:
2228: int w = SubstanceTitlePane.this .getWidth();
2229: int x;
2230: int y = 3;
2231: int spacing;
2232: int buttonHeight;
2233: int buttonWidth;
2234:
2235: if ((SubstanceTitlePane.this .closeButton != null)
2236: && (SubstanceTitlePane.this .closeButton.getIcon() != null)) {
2237: buttonHeight = SubstanceTitlePane.this .closeButton
2238: .getIcon().getIconHeight();
2239: buttonWidth = SubstanceTitlePane.this .closeButton
2240: .getIcon().getIconWidth();
2241: } else {
2242: buttonHeight = SubstanceSizeUtils
2243: .getTitlePaneIconSize();
2244: buttonWidth = SubstanceSizeUtils.getTitlePaneIconSize();
2245: }
2246:
2247: y = (getHeight() - buttonHeight) / 2;
2248:
2249: // assumes all buttons have the same dimensions
2250: // these dimensions include the borders
2251:
2252: x = leftToRight ? w : 0;
2253:
2254: spacing = 5;
2255: x = leftToRight ? spacing : w - buttonWidth - spacing;
2256: if (SubstanceTitlePane.this .menuBar != null) {
2257: SubstanceTitlePane.this .menuBar.setBounds(x, y,
2258: buttonWidth, buttonHeight);
2259: }
2260:
2261: x = leftToRight ? w : 0;
2262: spacing = 4;
2263: x += leftToRight ? -spacing - buttonWidth : spacing;
2264: if (SubstanceTitlePane.this .closeButton != null) {
2265: SubstanceTitlePane.this .closeButton.setBounds(x, y,
2266: buttonWidth, buttonHeight);
2267: }
2268:
2269: if (!leftToRight)
2270: x += buttonWidth;
2271:
2272: if (SubstanceTitlePane.this .getWindowDecorationStyle() == JRootPane.FRAME) {
2273: if (Toolkit.getDefaultToolkit().isFrameStateSupported(
2274: Frame.MAXIMIZED_BOTH)) {
2275: if (SubstanceTitlePane.this .toggleButton
2276: .getParent() != null) {
2277: spacing = 10;
2278: x += leftToRight ? -spacing - buttonWidth
2279: : spacing;
2280: SubstanceTitlePane.this .toggleButton.setBounds(
2281: x, y, buttonWidth, buttonHeight);
2282: if (!leftToRight) {
2283: x += buttonWidth;
2284: }
2285: }
2286: }
2287:
2288: if ((SubstanceTitlePane.this .minimizeButton != null)
2289: && (SubstanceTitlePane.this .minimizeButton
2290: .getParent() != null)) {
2291: spacing = 2;
2292: x += leftToRight ? -spacing - buttonWidth : spacing;
2293: SubstanceTitlePane.this .minimizeButton.setBounds(x,
2294: y, buttonWidth, buttonHeight);
2295: if (!leftToRight) {
2296: x += buttonWidth;
2297: }
2298: }
2299:
2300: if ((SubstanceTitlePane.this .heapStatusPanel != null)
2301: && SubstanceTitlePane.this .heapStatusPanel
2302: .isVisible()) {
2303: spacing = 5;
2304: x += leftToRight ? (-spacing - SubstanceTitlePane.this .heapStatusPanel
2305: .getPreferredSize().width)
2306: : spacing;
2307: SubstanceTitlePane.this .heapStatusPanel.setBounds(
2308: x, 1,
2309: SubstanceTitlePane.this .heapStatusPanel
2310: .getPreferredSize().width,
2311: SubstanceTitlePane.this .getHeight() - 3);
2312: }
2313: }
2314: // buttonsWidth = leftToRight ? w - x : x;
2315: }
2316:
2317: }
2318:
2319: /**
2320: * PropertyChangeListener installed on the Window. Updates the necessary
2321: * state as the state of the Window changes.
2322: */
2323: private class PropertyChangeHandler implements
2324: PropertyChangeListener {
2325: public void propertyChange(PropertyChangeEvent pce) {
2326: String name = pce.getPropertyName();
2327:
2328: // Frame.state isn't currently bound.
2329: if ("resizable".equals(name) || "state".equals(name)) {
2330: Frame frame = SubstanceTitlePane.this .getFrame();
2331:
2332: if (frame != null) {
2333: SubstanceTitlePane.this .setState(frame
2334: .getExtendedState(), true);
2335: }
2336: if ("resizable".equals(name)) {
2337: SubstanceTitlePane.this .getRootPane().repaint();
2338: }
2339: } else {
2340: if ("title".equals(name)) {
2341: SubstanceTitlePane.this .repaint();
2342: SubstanceTitlePane.this .setToolTipText((String) pce
2343: .getNewValue());
2344: } else {
2345: if ("componentOrientation".equals(name)
2346: || "iconImage".equals(name)) {
2347: SubstanceTitlePane.this .revalidate();
2348: SubstanceTitlePane.this .repaint();
2349: }
2350: }
2351: }
2352: }
2353: }
2354:
2355: /**
2356: * WindowListener installed on the Window, updates the state as necessary.
2357: */
2358: private class WindowHandler extends WindowAdapter {
2359: @Override
2360: public void windowActivated(WindowEvent ev) {
2361: SubstanceTitlePane.this .setActive(true);
2362: }
2363:
2364: @Override
2365: public void windowDeactivated(WindowEvent ev) {
2366: SubstanceTitlePane.this .setActive(false);
2367: }
2368: }
2369:
2370: /**
2371: * Sets indication whether frame title panes can show the heap status panel.
2372: *
2373: * @param canHaveHeapStatusPanel
2374: * if <code>true</code>, title panes can show the heap status
2375: * panel.
2376: */
2377: public static void setCanHaveHeapStatusPanel(
2378: boolean canHaveHeapStatusPanel) {
2379: SubstanceTitlePane.canHaveHeapStatusPanel = canHaveHeapStatusPanel;
2380: }
2381:
2382: /**
2383: * Returns indication whether frame title panes can show the heap status
2384: * panel.
2385: *
2386: * @return <code>true</code> if the frame title panes can show the heap
2387: * status panel, <code>false</code> otherwise.
2388: */
2389: public static boolean getCanHaveHeapStatusPanel() {
2390: return SubstanceTitlePane.canHaveHeapStatusPanel;
2391: }
2392:
2393: /**
2394: * Sets location for heap status logfile. Relevant if
2395: * {@link #setCanHaveHeapStatusPanel(boolean)} was called with
2396: * <code>true</code>.
2397: *
2398: * @param heapStatusLogfileName
2399: * Logfile for the heap status panel.
2400: */
2401: public static void setHeapStatusLogfileName(
2402: String heapStatusLogfileName) {
2403: SubstanceTitlePane.heapStatusLogfileName = heapStatusLogfileName;
2404: }
2405:
2406: /**
2407: * Makes the heap status panel appear / disappear permanently on the
2408: * associated title pane and removes the corresponding check box menu items
2409: * from the system menu.
2410: *
2411: * @param isVisible
2412: * if <code>true</code>, the heap status panel will be
2413: * permanently shown, if <code>false</code>, the heap status
2414: * panel will be permanently hidden.
2415: */
2416: public void setHeapStatusPanePermanentVisibility(boolean isVisible) {
2417: if (!(this .rootPane
2418: .getClientProperty(SubstanceTitlePane.HEAP_STATUS_PANEL_PERMANENT) instanceof Boolean)) {
2419: this .menuBar.getMenu(0).remove(this .heapStatusMenuItem);
2420: this .heapStatusMenuItem = null;
2421: }
2422: this .heapStatusPanel.setVisible(isVisible);
2423: this .rootPane.putClientProperty(
2424: SubstanceTitlePane.HEAP_STATUS_PANEL_PERMANENT, Boolean
2425: .valueOf(isVisible));
2426: this .rootPane.putClientProperty(
2427: SubstanceLookAndFeel.HEAP_STATUS_PANEL, Boolean
2428: .valueOf(isVisible));
2429: if (!isVisible) {
2430: HeapStatusThread.unregisterPanel(this .heapStatusPanel);
2431: } else {
2432: HeapStatusThread.registerPanel(this .heapStatusPanel);
2433: }
2434: this .repaint();
2435: }
2436:
2437: protected static class ThemeChanger implements ActionListener {
2438: protected SubstanceTheme newTheme;
2439:
2440: public ThemeChanger(SubstanceTheme newTheme) {
2441: super ();
2442: this .newTheme = newTheme;
2443: }
2444:
2445: public void actionPerformed(ActionEvent e) {
2446: SwingUtilities.invokeLater(new Runnable() {
2447: public void run() {
2448: SubstanceLookAndFeel.setCurrentTheme(newTheme);
2449: // update all existing root panes
2450: for (Frame frame : Frame.getFrames()) {
2451: SwingUtilities.updateComponentTreeUI(frame);
2452: }
2453: }
2454: });
2455: }
2456: }
2457:
2458: protected static class AnimationChanger implements ActionListener {
2459: protected AnimationKind newAnimationKind;
2460:
2461: public AnimationChanger(AnimationKind newAnimationKind) {
2462: super ();
2463: this .newAnimationKind = newAnimationKind;
2464: }
2465:
2466: public void actionPerformed(ActionEvent e) {
2467: SwingUtilities.invokeLater(new Runnable() {
2468: public void run() {
2469: UIManager.put(LafWidget.ANIMATION_KIND,
2470: newAnimationKind);
2471: }
2472: });
2473: }
2474: }
2475:
2476: protected static class FocusKindChanger implements ActionListener {
2477: protected FocusKind newFocusKind;
2478:
2479: public FocusKindChanger(FocusKind newFocusKind) {
2480: super ();
2481: this .newFocusKind = newFocusKind;
2482: }
2483:
2484: public void actionPerformed(ActionEvent e) {
2485: SwingUtilities.invokeLater(new Runnable() {
2486: public void run() {
2487: UIManager.put(SubstanceLookAndFeel.FOCUS_KIND,
2488: newFocusKind);
2489: }
2490: });
2491: }
2492: }
2493:
2494: /**
2495: * Synchronizes the tooltip of the close button.
2496: */
2497: protected void syncCloseButtonTooltip() {
2498: if (SubstanceCoreUtilities.isRootPaneModified(this
2499: .getRootPane())) {
2500: this .closeButton.setToolTipText(SubstanceCoreUtilities
2501: .getResourceBundle(rootPane).getString(
2502: "SystemMenu.close")
2503: + " ["
2504: + SubstanceCoreUtilities
2505: .getResourceBundle(rootPane).getString(
2506: "Tooltip.contentsNotSaved") + "]");
2507: } else {
2508: this .closeButton.setToolTipText(SubstanceCoreUtilities
2509: .getResourceBundle(rootPane).getString(
2510: "SystemMenu.close"));
2511: }
2512: this .closeButton.repaint();
2513: }
2514:
2515: public static void dump(Component comp, int level) {
2516: StringBuffer sb = new StringBuffer();
2517: for (int i = 0; i < level; i++)
2518: sb.append(" ");
2519: sb.append(comp.toString());
2520: System.out.println(sb);
2521: if (comp instanceof Container) {
2522: Container cont = (Container) comp;
2523: for (int i = 0; i < cont.getComponentCount(); i++) {
2524: dump(cont.getComponent(i), level + 1);
2525: }
2526: }
2527: }
2528: }
|