2
* Copyright (c) 2005-2010 Substance Kirill Grouchnikov. All Rights Reserved.
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions are met:
7
* o Redistributions of source code must retain the above copyright notice,
8
* this list of conditions and the following disclaimer.
10
* o Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
14
* o Neither the name of Substance Kirill Grouchnikov nor the names of
15
* its contributors may be used to endorse or promote products derived
16
* from this software without specific prior written permission.
18
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
20
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
22
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
25
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
package org.pushingpixels.substance.internal.utils;
32
import org.pushingpixels.lafwidget.animation.effects.GhostPaintingUtils;
33
import org.pushingpixels.lafwidget.utils.RenderingUtils;
34
import org.pushingpixels.lafwidget.utils.TrackableThread;
35
import org.pushingpixels.substance.api.DecorationAreaType;
36
import org.pushingpixels.substance.api.SubstanceColorScheme;
37
import org.pushingpixels.substance.api.SubstanceConstants.SubstanceWidgetType;
38
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
39
import org.pushingpixels.substance.api.SubstanceSkin;
40
import org.pushingpixels.substance.api.skin.SkinInfo;
41
import org.pushingpixels.substance.internal.painter.BackgroundPaintingUtils;
42
import org.pushingpixels.substance.internal.ui.SubstanceButtonUI;
43
import org.pushingpixels.substance.internal.ui.SubstanceRootPaneUI;
44
import org.pushingpixels.substance.internal.utils.icon.SubstanceIconFactory;
45
import org.pushingpixels.substance.internal.utils.icon.TransitionAwareIcon;
48
import javax.swing.plaf.UIResource;
50
import java.awt.event.*;
51
import java.awt.image.BufferedImage;
52
import java.beans.PropertyChangeEvent;
53
import java.beans.PropertyChangeListener;
54
import java.io.FileWriter;
55
import java.io.IOException;
56
import java.io.PrintWriter;
57
import java.lang.ref.WeakReference;
58
import java.text.SimpleDateFormat;
62
* Title pane for <b>Substance</b> look and feel.
64
* @author Kirill Grouchnikov
66
public class SubstanceTitlePane extends JComponent {
68
* PropertyChangeListener added to the JRootPane.
70
private PropertyChangeListener propertyChangeListener;
73
* JMenuBar, typically renders the system menu items.
75
protected JMenuBar menuBar;
78
* Action used to close the Window.
80
private Action closeAction;
83
* Action used to iconify the Frame.
85
private Action iconifyAction;
88
* Action to restore the Frame size.
90
private Action restoreAction;
93
* Action to restore the Frame size.
95
private Action maximizeAction;
98
* Button used to maximize or restore the frame.
100
protected JButton toggleButton;
103
* Button used to minimize the frame
105
protected JButton minimizeButton;
108
* Button used to close the frame.
110
protected JButton closeButton;
113
* Listens for changes in the state of the Window listener to update the
114
* state of the widgets.
116
private WindowListener windowListener;
119
* Window we're currently in.
121
protected Window window;
124
* JRootPane rendering for.
126
protected JRootPane rootPane;
129
* Buffered Frame.state property. As state isn't bound, this is kept to
130
* determine when to avoid updating widgets.
135
* SubstanceRootPaneUI that created us.
137
private SubstanceRootPaneUI rootPaneUI;
140
* The logfile name for the heap status panel. Can be <code>null</code> - in
141
* this case the {@link HeapStatusThread} will not write heap information.
143
private static String heapStatusLogfileName;
146
* The heap status panel of <code>this</code> title pane.
148
protected HeapStatusPanel heapStatusPanel;
151
* The heap status toggle menu item of <code>this</code> title pane.
153
protected JCheckBoxMenuItem heapStatusMenuItem;
156
* Listens on changes to <code>componentOrientation</code> and
157
* {@link SubstanceLookAndFeel#WINDOW_MODIFIED} properties.
159
protected PropertyChangeListener propertyListener;
162
* Client property to mark every child to be either leading or trailing. The
163
* value must be one of {@link ExtraComponentKind}.
165
* @see #markExtraComponent(JComponent, ExtraComponentKind)
166
* @see #getTitleTextRectangle(int)
168
protected static final String EXTRA_COMPONENT_KIND = "substancelaf.internal.titlePane.extraComponentKind";
171
* The application icon to be displayed.
173
protected Image appIcon;
176
* Enumerates the types of children components.
178
* @author Kirill Grouchnikov
180
protected enum ExtraComponentKind {
182
* Leading child components (left on LTR and right on RTL).
187
* Trailing child components (right on LTR and left on RTL).
192
* Middeling child compoennt, will jostle for position with the title text
193
* subclasses are responsible for the placement
199
* Panel that shows heap status and allows running the garbage collector.
201
* @author Kirill Grouchnikov
203
public static class HeapStatusPanel extends JPanel {
205
* The current heap size in kilobytes.
207
private int currHeapSizeKB;
210
* The current used portion of heap in kilobytes.
212
private int currTakenHeapSizeKB;
215
* History of used heap portion (in percents). Each value is in 0.0-1.0
218
private LinkedList<Double> graphValues;
221
* Creates new heap status panel.
223
public HeapStatusPanel() {
224
this.graphValues = new LinkedList<Double>();
225
HeapStatusThread.getInstance();
229
* Updates the values for <code>this</code> heap status panel.
231
* @param currHeapSizeKB
232
* The current heap size in kilobytes.
233
* @param currTakenHeapSizeKB
234
* The current used portion of heap in kilobytes.
236
public synchronized void updateStatus(int currHeapSizeKB,
237
int currTakenHeapSizeKB) {
238
this.currHeapSizeKB = currHeapSizeKB;
239
this.currTakenHeapSizeKB = currTakenHeapSizeKB;
240
double newGraphValue = (double) currTakenHeapSizeKB
241
/ (double) currHeapSizeKB;
242
this.graphValues.addLast(newGraphValue);
249
* @see javax.swing.JComponent#paint(java.awt.Graphics)
252
public synchronized void paint(Graphics g) {
253
Graphics2D graphics = (Graphics2D) g.create();
255
SubstanceColorScheme scheme = SubstanceCoreUtilities.getSkin(this)
256
.getActiveColorScheme(
257
DecorationAreaType.PRIMARY_TITLE_PANE);
259
graphics.setColor(scheme.getDarkColor());
260
int w = this.getWidth();
261
int h = this.getHeight();
263
graphics.drawRect(0, 0, w - 1, h - 1);
265
graphics.setColor(scheme.getExtraLightColor());
266
graphics.fillRect(1, 1, w - 2, h - 2);
268
while (this.graphValues.size() > (w - 2))
269
this.graphValues.removeFirst();
271
int xOff = w - this.graphValues.size() - 1;
272
graphics.setColor(scheme.getMidColor());
274
for (double value : this.graphValues) {
275
int valueH = (int) (value * (h - 2));
276
graphics.drawLine(xOff + count, h - 1 - valueH, xOff + count,
281
graphics.setFont(UIManager.getFont("Panel.font"));
282
FontMetrics fm = graphics.getFontMetrics();
284
StringBuffer longFormat = new StringBuffer();
285
Formatter longFormatter = new Formatter(longFormat);
286
longFormatter.format("%.1fMB / %.1fMB",
287
this.currTakenHeapSizeKB / 1024.f,
288
this.currHeapSizeKB / 1024.f);
289
int strW = fm.stringWidth(longFormat.toString());
290
int strH = fm.getAscent() + fm.getDescent();
292
graphics.setColor(scheme.getForegroundColor());
293
RenderingUtils.installDesktopHints(graphics, this);
294
if (strW < (w - 5)) {
295
graphics.drawString(longFormat.toString(), (w - strW) / 2,
298
String shortFormat = (this.currTakenHeapSizeKB / 1024)
299
+ "MB / " + (this.currHeapSizeKB / 1024) + "MB";
300
strW = fm.stringWidth(shortFormat);
301
graphics.drawString(shortFormat, (w - strW) / 2,
309
* Returns the preferred width of this panel.
311
* @return Preferred width of this panel.
313
public int getPreferredWidth() {
314
BufferedImage dummy = new BufferedImage(1, 1,
315
BufferedImage.TYPE_INT_ARGB);
316
Graphics2D g2d = dummy.createGraphics();
317
RenderingUtils.installDesktopHints(g2d, this);
318
g2d.setFont(UIManager.getFont("Panel.font"));
319
FontMetrics fm = g2d.getFontMetrics();
320
int result = fm.stringWidth("100.9MB / 200.9MB");
327
* Thread for heap status panel.
329
public static class HeapStatusThread extends TrackableThread {
331
* Current heap size in kilobytes.
333
private int heapSizeKB;
336
* Current used portion of heap in kilobytes.
338
private int takenHeapSizeKB;
341
* All heap status panels.
343
private static Set<WeakReference<HeapStatusPanel>> panels = new HashSet<WeakReference<HeapStatusPanel>>();
346
* Single instance of <code>this</code> thread.
348
private static HeapStatusThread instance;
351
* Formatter object (for logfile).
353
private SimpleDateFormat format;
356
* Signifies whether a stop request has been issued on <code>this</code>
357
* thread using the {@link #requestStop()} call.
359
private boolean isStopRequested;
362
* Simple constructor. Defined private for singleton.
364
* @see #getInstance()
366
private HeapStatusThread() {
367
this.format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss.SSS");
368
this.isStopRequested = false;
369
this.setName("Substance heap status");
373
* Gets singleton instance of <code>this</code> thread.
375
* @return Singleton instance of <code>this</code> thread.
377
public synchronized static HeapStatusThread getInstance() {
378
if (HeapStatusThread.instance == null) {
379
HeapStatusThread.instance = new HeapStatusThread();
380
HeapStatusThread.instance.start();
382
return HeapStatusThread.instance;
386
* Registers new heap status panel with <code>this</code> thread.
391
public static synchronized void registerPanel(HeapStatusPanel panel) {
392
panels.add(new WeakReference<HeapStatusPanel>(panel));
396
* Unregisters new heap status panel from <code>this</code> thread.
401
public static synchronized void unregisterPanel(HeapStatusPanel panel) {
402
for (Iterator<WeakReference<HeapStatusPanel>> it = panels
403
.iterator(); it.hasNext();) {
404
WeakReference<HeapStatusPanel> ref = it.next();
405
HeapStatusPanel currPanel = ref.get();
406
if (panel == currPanel) {
414
* Updates the values of heap status.
416
private synchronized void updateHeapCounts() {
417
long heapSize = Runtime.getRuntime().totalMemory();
418
long heapFreeSize = Runtime.getRuntime().freeMemory();
420
this.heapSizeKB = (int) (heapSize / 1024);
421
this.takenHeapSizeKB = (int) ((heapSize - heapFreeSize) / 1024);
427
* @see java.lang.Thread#run()
431
while (!this.isStopRequested) {
433
// update every 0.5 seconds
435
} catch (InterruptedException ignore) {
437
if (!SubstanceWidgetManager.getInstance().isAllowedAnywhere(
438
SubstanceWidgetType.TITLE_PANE_HEAP_STATUS))
440
this.updateHeapCounts();
441
for (Iterator<WeakReference<HeapStatusPanel>> it = panels
442
.iterator(); it.hasNext();) {
443
WeakReference<HeapStatusPanel> refPanel = it.next();
444
HeapStatusPanel panel = refPanel.get();
451
panel.updateStatus(this.heapSizeKB, this.takenHeapSizeKB);
453
// see if need to put info in log file
454
if (SubstanceTitlePane.heapStatusLogfileName != null) {
455
PrintWriter pw = null;
457
pw = new PrintWriter(new FileWriter(
458
SubstanceTitlePane.heapStatusLogfileName, true));
459
pw.println(this.format.format(new Date()) + " "
460
+ this.takenHeapSizeKB + "KB / "
461
+ this.heapSizeKB + "KB");
462
} catch (IOException ignore) {
473
protected void requestStop() {
474
this.isStopRequested = true;
475
HeapStatusThread.instance = null;
480
* Creates a new title pane.
487
public SubstanceTitlePane(JRootPane root, SubstanceRootPaneUI ui) {
488
this.rootPane = root;
489
this.rootPaneUI = ui;
493
this.installSubcomponents();
494
this.installDefaults();
496
this.setLayout(this.createLayout());
498
this.setToolTipText(this.getTitle());
500
SubstanceLookAndFeel.setDecorationType(this,
501
DecorationAreaType.PRIMARY_TITLE_PANE);
502
this.setForeground(SubstanceColorUtilities
503
.getForegroundColor(SubstanceCoreUtilities.getSkin(this)
504
.getBackgroundColorScheme(
505
DecorationAreaType.PRIMARY_TITLE_PANE)));
506
// SubstanceColorSchemeUtilities
507
// .getColorScheme(this, ComponentState.ACTIVE)));
511
* Uninstalls the necessary state.
513
public void uninstall() {
514
this.uninstallListeners();
517
HeapStatusThread.unregisterPanel(this.heapStatusPanel);
519
// Swing bug (?) - the updateComponentTree never gets to the
520
// system menu (and in our case we have radio menu items with
521
// rollover listeners). Fix for defect 109 - memory leak on skin
523
if ((this.menuBar != null) && (this.menuBar.getMenuCount() > 0)) {
524
this.menuBar.getUI().uninstallUI(this.menuBar);
525
SubstanceCoreUtilities.uninstallMenu(this.menuBar.getMenu(0));
528
if (this.heapStatusPanel != null) {
529
for (MouseListener listener : this.heapStatusPanel
530
.getMouseListeners())
531
this.heapStatusPanel.removeMouseListener(listener);
532
HeapStatusThread.unregisterPanel(this.heapStatusPanel);
533
this.remove(this.heapStatusPanel);
536
if (this.menuBar != null)
537
this.menuBar.removeAll();
542
* Installs the necessary listeners.
544
private void installListeners() {
545
if (this.window != null) {
546
this.windowListener = new WindowHandler();
547
this.window.addWindowListener(this.windowListener);
548
this.propertyChangeListener = new PropertyChangeHandler();
549
this.window.addPropertyChangeListener(this.propertyChangeListener);
552
// Property change listener for pulsating close button
553
// when window has been marked as changed.
554
// Fix for defect 109 - memory leak on skin change.
555
this.propertyListener = new PropertyChangeListener() {
557
public void propertyChange(final PropertyChangeEvent evt) {
558
if (SubstanceLookAndFeel.WINDOW_MODIFIED.equals(evt
559
.getPropertyName())) {
560
syncCloseButtonTooltip();
561
// if (Boolean.TRUE.equals(evt.getNewValue())) {
562
// SubstanceTitlePane.this.closeButton
563
// .setToolTipText(SubstanceLookAndFeel
564
// .getLabelBundle().getString(
565
// "SystemMenu.close")
567
// + SubstanceLookAndFeel
570
// "Tooltip.contentsNotSaved")
573
// SubstanceTitlePane.this.closeButton
574
// .setToolTipText(SubstanceLookAndFeel
575
// .getLabelBundle().getString(
576
// "SystemMenu.close"));
578
// SubstanceTitlePane.this.closeButton.repaint();
581
if ("componentOrientation".equals(evt.getPropertyName())) {
582
SwingUtilities.invokeLater(new Runnable() {
585
if (SubstanceTitlePane.this.menuBar != null) {
586
SubstanceTitlePane.this.menuBar
587
.applyComponentOrientation((ComponentOrientation) evt
595
// Wire it on the frame itself and its root pane.
596
this.rootPane.addPropertyChangeListener(this.propertyListener);
597
if (this.getFrame() != null)
598
this.getFrame().addPropertyChangeListener(this.propertyListener);
602
* Uninstalls the necessary listeners.
604
private void uninstallListeners() {
605
if (this.window != null) {
606
this.window.removeWindowListener(this.windowListener);
607
this.windowListener = null;
609
.removePropertyChangeListener(this.propertyChangeListener);
610
this.propertyChangeListener = null;
613
// Fix for defect 109 - memory leak on skin change.
614
this.rootPane.removePropertyChangeListener(this.propertyListener);
615
if (this.getFrame() != null)
616
this.getFrame().removePropertyChangeListener(this.propertyListener);
617
this.propertyListener = null;
622
* Returns the <code>JRootPane</code> this was created for.
625
public JRootPane getRootPane() {
626
return this.rootPane;
630
* Returns the decoration style of the <code>JRootPane</code>.
632
* @return Decoration style of the <code>JRootPane</code>.
634
protected int getWindowDecorationStyle() {
635
return this.getRootPane().getWindowDecorationStyle();
641
* @see java.awt.Component#addNotify()
644
public void addNotify() {
647
this.uninstallListeners();
649
this.window = SwingUtilities.getWindowAncestor(this);
650
if (this.window != null) {
651
this.setActive(this.window.isActive());
652
if (this.window instanceof Frame) {
653
this.setState(((Frame) this.window).getExtendedState());
657
if (this.getComponentCount() == 0) {
658
// fix for issue 385 - add the sub-components uninstalled
659
// in the removeNotify. This happens when a decorated
660
// dialog has been disposed and then reshown.
661
this.installSubcomponents();
663
this.installListeners();
665
this.setToolTipText(this.getTitle());
666
this.updateAppIcon();
672
* @see java.awt.Component#removeNotify()
675
public void removeNotify() {
676
super.removeNotify();
683
* Adds any sub-Components contained in the <code>SubstanceTitlePane</code>.
685
private void installSubcomponents() {
686
int decorationStyle = this.getWindowDecorationStyle();
687
if (decorationStyle == JRootPane.FRAME) {
688
this.createActions();
689
this.menuBar = this.createMenuBar();
690
if (this.menuBar != null) {
691
this.add(this.menuBar);
693
this.createButtons();
694
this.add(this.minimizeButton);
695
this.add(this.toggleButton);
696
this.add(this.closeButton);
698
this.heapStatusPanel = new HeapStatusPanel();
699
this.markExtraComponent(this.heapStatusPanel,
700
ExtraComponentKind.TRAILING);
701
this.add(this.heapStatusPanel);
702
boolean isHeapStatusPanelShowing = SubstanceWidgetManager
703
.getInstance().isAllowed(rootPane,
704
SubstanceWidgetType.TITLE_PANE_HEAP_STATUS);
705
this.heapStatusPanel.setVisible(isHeapStatusPanelShowing);
706
this.heapStatusPanel.setPreferredSize(new Dimension(80, this
707
.getPreferredSize().height));
708
this.heapStatusPanel.setToolTipText(SubstanceCoreUtilities
709
.getResourceBundle(rootPane).getString(
710
"Tooltip.heapStatusPanel"));
711
this.heapStatusPanel.addMouseListener(new MouseAdapter() {
713
public void mouseClicked(MouseEvent e) {
718
HeapStatusThread.registerPanel(this.heapStatusPanel);
720
if ((decorationStyle == JRootPane.PLAIN_DIALOG)
721
|| (decorationStyle == JRootPane.INFORMATION_DIALOG)
722
|| (decorationStyle == JRootPane.ERROR_DIALOG)
723
|| (decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG)
724
|| (decorationStyle == JRootPane.FILE_CHOOSER_DIALOG)
725
|| (decorationStyle == JRootPane.QUESTION_DIALOG)
726
|| (decorationStyle == JRootPane.WARNING_DIALOG)) {
727
this.createActions();
728
this.menuBar = this.createMenuBar();
729
if (this.menuBar != null) {
730
this.add(this.menuBar);
732
this.createButtons();
733
this.add(this.closeButton);
739
* Installs the fonts and necessary properties.
741
private void installDefaults() {
742
this.setFont(UIManager.getFont("InternalFrame.titleFont", this
747
* Returns the <code>JMenuBar</code> displaying the appropriate system menu
750
* @return <code>JMenuBar</code> displaying the appropriate system menu
753
protected JMenuBar createMenuBar() {
754
this.menuBar = new SubstanceMenuBar();
755
this.menuBar.setFocusable(false);
756
this.menuBar.setBorderPainted(true);
757
this.menuBar.add(this.createMenu());
758
this.menuBar.setOpaque(false);
760
this.menuBar.applyComponentOrientation(this.rootPane
761
.getComponentOrientation());
763
this.markExtraComponent(this.menuBar, ExtraComponentKind.LEADING);
769
* Create the <code>Action</code>s that get associated with the buttons and
772
private void createActions() {
773
this.closeAction = new CloseAction();
774
if (this.getWindowDecorationStyle() == JRootPane.FRAME) {
775
this.iconifyAction = new IconifyAction();
776
this.restoreAction = new RestoreAction();
777
this.maximizeAction = new MaximizeAction();
782
* Returns the <code>JMenu</code> displaying the appropriate menu items for
783
* manipulating the Frame.
785
* @return <code>JMenu</code> displaying the appropriate menu items for
786
* manipulating the Frame.
788
private JMenu createMenu() {
789
JMenu menu = new JMenu("");
790
menu.setOpaque(false);
791
menu.setBackground(null);
792
//if (this.getWindowDecorationStyle() == JRootPane.FRAME) {
793
this.addMenuItems(menu);
795
menu.addMouseListener(new MouseAdapter() {
797
public void mouseClicked(MouseEvent e) {
798
if (e.getClickCount() > 1) {
799
closeAction.actionPerformed(new ActionEvent(e.getSource(),
800
ActionEvent.ACTION_PERFORMED, null,
801
EventQueue.getMostRecentEventTime(), e.getModifiers()));
809
* Adds the necessary <code>JMenuItem</code>s to the specified menu.
814
private void addMenuItems(JMenu menu) {
815
if (this.getWindowDecorationStyle() == JRootPane.FRAME) {
816
menu.add(this.restoreAction);
818
menu.add(this.iconifyAction);
820
if (Toolkit.getDefaultToolkit().isFrameStateSupported(
821
Frame.MAXIMIZED_BOTH)) {
822
menu.add(this.maximizeAction);
825
if (SubstanceCoreUtilities.toShowExtraWidgets(rootPane)) {
827
JMenu skinMenu = new JMenu(SubstanceCoreUtilities
828
.getResourceBundle(rootPane).getString("SystemMenu.skins"));
829
Map<String, SkinInfo> allSkins = SubstanceLookAndFeel.getAllSkins();
830
for (Map.Entry<String, SkinInfo> skinEntry : allSkins.entrySet()) {
831
final String skinClassName = skinEntry.getValue()
833
JMenuItem jmiSkin = new JMenuItem(skinEntry.getKey());
834
jmiSkin.addActionListener(new ActionListener() {
836
public void actionPerformed(ActionEvent e) {
837
SwingUtilities.invokeLater(new Runnable() {
840
SubstanceLookAndFeel.setSkin(skinClassName);
846
skinMenu.add(jmiSkin);
854
menu.add(this.closeAction);
858
* Returns a <code>JButton</code> appropriate for placement on the
861
* @return Title button.
863
private JButton createTitleButton() {
864
JButton button = new SubstanceTitleButton();
866
button.setFocusPainted(false);
867
button.setFocusable(false);
868
button.setOpaque(true);
870
this.markExtraComponent(button, ExtraComponentKind.TRAILING);
876
* Creates the Buttons that will be placed on the TitlePane.
878
private void createButtons() {
879
this.closeButton = this.createTitleButton();
880
this.closeButton.setAction(this.closeAction);
881
this.closeButton.setText(null);
882
this.closeButton.setBorder(null);
883
// this.closeButton.setToolTipText(SubstanceLookAndFeel
884
// .getLabelBundle().getString(
885
// "SystemMenu.close"));
887
Icon closeIcon = new TransitionAwareIcon(closeButton,
888
new TransitionAwareIcon.Delegate() {
890
public Icon getColorSchemeIcon(SubstanceColorScheme scheme) {
891
return SubstanceIconFactory
893
SubstanceIconFactory.IconKind.CLOSE,
895
SubstanceCoreUtilities
897
.getBackgroundColorScheme(
898
DecorationAreaType.PRIMARY_TITLE_PANE));
900
}, "substance.titlePane.closeIcon");
901
this.closeButton.setIcon(closeIcon);
903
this.closeButton.setFocusable(false);
904
this.closeButton.putClientProperty(SubstanceLookAndFeel.FLAT_PROPERTY,
907
this.closeButton.putClientProperty(
908
SubstanceButtonUI.IS_TITLE_CLOSE_BUTTON, Boolean.TRUE);
910
if (this.getWindowDecorationStyle() == JRootPane.FRAME) {
911
this.minimizeButton = this.createTitleButton();
912
this.minimizeButton.setAction(this.iconifyAction);
913
this.minimizeButton.setText(null);
914
this.minimizeButton.setBorder(null);
916
Icon minIcon = new TransitionAwareIcon(this.minimizeButton,
917
new TransitionAwareIcon.Delegate() {
919
public Icon getColorSchemeIcon(
920
SubstanceColorScheme scheme) {
921
return SubstanceIconFactory
923
SubstanceIconFactory.IconKind.MINIMIZE,
925
SubstanceCoreUtilities
927
.getBackgroundColorScheme(
928
DecorationAreaType.PRIMARY_TITLE_PANE));
930
}, "substance.titlePane.minIcon");
931
this.minimizeButton.setIcon(minIcon);
933
this.minimizeButton.setFocusable(false);
934
this.minimizeButton.putClientProperty(
935
SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
936
this.minimizeButton.setToolTipText(SubstanceCoreUtilities
937
.getResourceBundle(rootPane)
938
.getString("SystemMenu.iconify"));
940
this.toggleButton = this.createTitleButton();
941
this.toggleButton.setAction(this.restoreAction);
942
this.toggleButton.setBorder(null);
943
this.toggleButton.setText(null);
945
Icon maxIcon = new TransitionAwareIcon(this.toggleButton,
946
new TransitionAwareIcon.Delegate() {
948
public Icon getColorSchemeIcon(
949
SubstanceColorScheme scheme) {
950
return SubstanceIconFactory
952
SubstanceIconFactory.IconKind.MAXIMIZE,
954
SubstanceCoreUtilities
956
.getBackgroundColorScheme(
957
DecorationAreaType.PRIMARY_TITLE_PANE));
959
}, "substance.titlePane.maxIcon");
960
this.toggleButton.setIcon(maxIcon);
962
this.toggleButton.setToolTipText(SubstanceCoreUtilities
963
.getResourceBundle(rootPane).getString(
964
"SystemMenu.maximize"));
965
this.toggleButton.setFocusable(false);
966
this.toggleButton.putClientProperty(
967
SubstanceLookAndFeel.FLAT_PROPERTY, Boolean.TRUE);
970
syncCloseButtonTooltip();
974
* Returns the <code>LayoutManager</code> that should be installed on the
975
* <code>SubstanceTitlePane</code>.
977
* @return Layout manager.
979
protected LayoutManager createLayout() {
980
return new TitlePaneLayout();
984
* Updates state dependant upon the Window's active state.
987
* if <code>true</code>, the window is in active state.
989
void setActive(boolean isActive) {
990
if (getRootPane() != null) {
991
this.getRootPane().repaint();
996
* Sets the state of the Window.
1001
private void setState(int state) {
1002
this.setState(state, false);
1006
* Sets the state of the window. If <code>updateRegardless</code> is true
1007
* and the state has not changed, this will update anyway.
1011
* @param updateRegardless
1012
* if <code>true</code>, the update is done in any case.
1014
private void setState(int state, boolean updateRegardless) {
1015
Window w = this.getWindow();
1017
if ((w != null) && (this.getWindowDecorationStyle() == JRootPane.FRAME)) {
1018
if ((this.state == state) && !updateRegardless) {
1021
Frame frame = this.getFrame();
1023
if (frame != null) {
1024
final JRootPane rootPane = this.getRootPane();
1026
if (((state & Frame.MAXIMIZED_BOTH) != 0)
1027
&& ((rootPane.getBorder() == null) || (rootPane
1028
.getBorder() instanceof UIResource))
1029
&& frame.isShowing()) {
1030
rootPane.setBorder(null);
1032
if ((state & Frame.MAXIMIZED_BOTH) == 0) {
1033
// This is a croak, if state becomes bound, this can
1035
this.rootPaneUI.installBorder(rootPane);
1038
if (frame.isResizable()) {
1039
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
1040
Icon restoreIcon = new TransitionAwareIcon(
1042
new TransitionAwareIcon.Delegate() {
1044
public Icon getColorSchemeIcon(
1045
SubstanceColorScheme scheme) {
1046
return SubstanceIconFactory
1048
SubstanceIconFactory.IconKind.RESTORE,
1050
SubstanceCoreUtilities
1053
.getBackgroundColorScheme(
1054
DecorationAreaType.PRIMARY_TITLE_PANE));
1056
}, "substance.titlePane.restoreIcon");
1058
.updateToggleButton(this.restoreAction,
1060
this.toggleButton.setToolTipText(SubstanceCoreUtilities
1061
.getResourceBundle(rootPane).getString(
1062
"SystemMenu.restore"));
1063
this.maximizeAction.setEnabled(false);
1064
this.restoreAction.setEnabled(true);
1066
Icon maxIcon = new TransitionAwareIcon(
1068
new TransitionAwareIcon.Delegate() {
1070
public Icon getColorSchemeIcon(
1071
SubstanceColorScheme scheme) {
1072
return SubstanceIconFactory
1074
SubstanceIconFactory.IconKind.MAXIMIZE,
1076
SubstanceCoreUtilities
1079
.getBackgroundColorScheme(
1080
DecorationAreaType.PRIMARY_TITLE_PANE));
1082
}, "substance.titlePane.maxIcon");
1083
this.updateToggleButton(this.maximizeAction, maxIcon);
1084
this.toggleButton.setToolTipText(SubstanceCoreUtilities
1085
.getResourceBundle(rootPane).getString(
1086
"SystemMenu.maximize"));
1087
this.maximizeAction.setEnabled(true);
1088
this.restoreAction.setEnabled(false);
1090
if ((this.toggleButton.getParent() == null)
1091
|| (this.minimizeButton.getParent() == null)) {
1092
this.add(this.toggleButton);
1093
this.add(this.minimizeButton);
1097
this.toggleButton.setText(null);
1099
this.maximizeAction.setEnabled(false);
1100
this.restoreAction.setEnabled(false);
1101
if (this.toggleButton.getParent() != null) {
1102
this.remove(this.toggleButton);
1108
// Not contained in a Frame
1109
this.maximizeAction.setEnabled(false);
1110
this.restoreAction.setEnabled(false);
1111
this.iconifyAction.setEnabled(false);
1112
this.remove(this.toggleButton);
1113
this.remove(this.minimizeButton);
1117
this.closeAction.setEnabled(true);
1123
* Updates the toggle button to contain the Icon <code>icon</code>, and
1124
* Action <code>action</code>.
1131
private void updateToggleButton(Action action, Icon icon) {
1132
this.toggleButton.setAction(action);
1133
this.toggleButton.setIcon(icon);
1134
this.toggleButton.setText(null);
1138
* Returns the Frame rendering in. This will return null if the
1139
* <code>JRootPane</code> is not contained in a <code>Frame</code>.
1143
private Frame getFrame() {
1144
Window window = this.getWindow();
1146
if (window instanceof Frame) {
1147
return (Frame) window;
1153
* Returns the <code>Window</code> the <code>JRootPane</code> is contained
1154
* in. This will return null if there is no parent ancestor of the
1155
* <code>JRootPane</code>.
1159
private Window getWindow() {
1164
* Returns the String to display as the title.
1166
* @return Display title.
1168
private String getTitle() {
1169
Window w = this.getWindow();
1171
if (w instanceof Frame) {
1172
return ((Frame) w).getTitle();
1174
if (w instanceof Dialog) {
1175
return ((Dialog) w).getTitle();
1180
public DecorationAreaType getThisDecorationType() {
1181
DecorationAreaType dat = SubstanceLookAndFeel.getDecorationType(this);
1182
if (dat == DecorationAreaType.PRIMARY_TITLE_PANE) {
1183
return SubstanceCoreUtilities.isPaintRootPaneActivated(getRootPane())
1184
? DecorationAreaType.PRIMARY_TITLE_PANE
1185
: DecorationAreaType.PRIMARY_TITLE_PANE_INACTIVE;
1186
} else if (dat == DecorationAreaType.SECONDARY_TITLE_PANE) {
1187
return SubstanceCoreUtilities.isPaintRootPaneActivated(getRootPane())
1188
? DecorationAreaType.SECONDARY_TITLE_PANE
1189
: DecorationAreaType.SECONDARY_TITLE_PANE_INACTIVE;
1199
* @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
1202
public void paintComponent(Graphics g) {
1203
// long start = System.nanoTime();
1204
// As state isn't bound, we need a convenience place to check
1205
// if it has changed. Changing the state typically changes the
1206
if (this.getFrame() != null) {
1207
this.setState(this.getFrame().getExtendedState());
1209
final JRootPane rootPane = this.getRootPane();
1210
Window window = this.getWindow();
1211
boolean leftToRight = (window == null) ? rootPane
1212
.getComponentOrientation().isLeftToRight() : window
1213
.getComponentOrientation().isLeftToRight();
1214
int width = this.getWidth();
1215
int height = this.getHeight();
1217
SubstanceSkin skin = SubstanceCoreUtilities.getSkin(rootPane);
1219
SubstanceCoreUtilities
1220
.traceSubstanceApiUsage(this,
1221
"Substance delegate used when Substance is not the current LAF");
1223
DecorationAreaType decorationType = getThisDecorationType();
1224
SubstanceColorScheme scheme = skin
1225
.getEnabledColorScheme(decorationType);
1228
String theTitle = this.getTitle();
1230
if (theTitle != null) {
1231
FontMetrics fm = rootPane.getFontMetrics(g.getFont());
1232
Rectangle titleTextRect = this.getTitleTextRectangle(fm.stringWidth(theTitle));
1233
int titleWidth = titleTextRect.width;
1234
String clippedTitle = SubstanceCoreUtilities.clipString(fm,
1235
titleWidth, theTitle);
1236
// show tooltip with full title only if necessary
1237
if (theTitle.equals(clippedTitle)) {
1238
this.setToolTipText(null);
1240
this.setToolTipText(theTitle);
1242
theTitle = clippedTitle;
1244
xOffset = titleTextRect.x;
1246
xOffset = titleTextRect.x + titleTextRect.width
1247
- fm.stringWidth(theTitle);
1250
Graphics2D graphics = (Graphics2D) g.create();
1251
Font font = SubstanceLookAndFeel.getFontPolicy().getFontSet(
1252
"Substance", null).getWindowTitleFont();
1253
graphics.setFont(font);
1255
BackgroundPaintingUtils
1256
.update(graphics, SubstanceTitlePane.this, false, decorationType);
1257
// DecorationPainterUtils.paintDecorationBackground(graphics,
1258
// SubstanceTitlePane.this, false);
1260
// draw the title (if needed)
1261
if (theTitle != null) {
1262
FontMetrics fm = rootPane.getFontMetrics(graphics.getFont());
1263
int yOffset = ((height - fm.getHeight()) / 2) + fm.getAscent();
1265
SubstanceTextUtilities.paintTextWithDropShadow(this, graphics,
1266
SubstanceColorUtilities.getForegroundColor(scheme),
1267
theTitle, width, height, xOffset, yOffset);
1270
GhostPaintingUtils.paintGhostImages(this, graphics);
1272
// long end = System.nanoTime();
1273
// System.out.println(end - start);
1278
* Computes the rectangle of the title text. This method looks at all the
1279
* children components of the title pane, grouping them by leading and
1280
* trailing (based on {@link #EXTRA_COMPONENT_KIND} client property). The
1281
* title text rectangle is the space between the leading group and the
1284
* @return Rectangle of the title text.
1285
* @throws IllegalStateException
1286
* If at least one child component of this title pane is not
1287
* marked with the {@link #EXTRA_COMPONENT_KIND} client
1289
* @see #markExtraComponent(JComponent, ExtraComponentKind)
1290
* @see #EXTRA_COMPONENT_KIND
1292
protected Rectangle getTitleTextRectangle(int preferredWidth) {
1293
JRootPane rootPane = this.getRootPane();
1294
Window window = this.getWindow();
1295
boolean leftToRight = (window == null) ? rootPane
1296
.getComponentOrientation().isLeftToRight() : window
1297
.getComponentOrientation().isLeftToRight();
1300
int maxLeadingX = 0;
1301
int minTrailingX = this.getWidth();
1302
int maxMiddlingX = maxLeadingX;
1303
int minMiddlingX = minTrailingX;
1305
for (int i = 0; i < this.getComponentCount(); i++) {
1306
Component child = this.getComponent(i);
1307
if (!child.isVisible())
1309
if (child instanceof JComponent) {
1310
ExtraComponentKind kind = (ExtraComponentKind) ((JComponent) child)
1311
.getClientProperty(EXTRA_COMPONENT_KIND);
1313
throw new IllegalStateException("Title pane child "
1314
+ child.getClass().getName()
1315
+ " is not marked as leading or trailing");
1319
int cx = child.getX() + child.getWidth();
1320
if (cx > maxLeadingX)
1325
if (cx < minMiddlingX)
1327
cx = child.getX() + child.getWidth();
1328
if (cx > maxMiddlingX)
1333
if (cx < minTrailingX)
1339
minMiddlingX = Math.max(minMiddlingX, maxLeadingX);
1340
maxMiddlingX = Math.min(maxMiddlingX, minTrailingX);
1341
int leadWidth = minMiddlingX - maxLeadingX - 15;
1342
int trailWidth = minTrailingX - maxMiddlingX - 15;
1344
if (maxMiddlingX <= minMiddlingX) {
1345
start = maxLeadingX + 10;
1346
end = minTrailingX - 5;
1347
} else if ((preferredWidth > leadWidth)
1348
&& ((trailWidth > leadWidth) || (preferredWidth <= trailWidth)))
1350
start = maxMiddlingX + 10;
1351
end = minTrailingX - 5;
1353
start = maxLeadingX + 10;
1354
end = minMiddlingX - 5;
1356
return new Rectangle(start, 0, end - start, this.getHeight());
1358
int minLeadingX = this.getWidth();
1359
int maxTrailingX = 0;
1360
int maxMiddlingX = maxTrailingX;
1361
int minMiddlingX = minLeadingX;
1364
for (int i = 0; i < this.getComponentCount(); i++) {
1365
Component child = this.getComponent(i);
1366
if (!child.isVisible())
1368
if (child instanceof JComponent) {
1369
ExtraComponentKind kind = (ExtraComponentKind) ((JComponent) child)
1370
.getClientProperty(EXTRA_COMPONENT_KIND);
1372
throw new IllegalStateException("Title pane child "
1373
+ child.getClass().getName()
1374
+ " is not marked as leading or trailing");
1379
int cx = child.getX();
1380
if (cx < minLeadingX)
1385
if (cx < minMiddlingX)
1387
cx = child.getX() + child.getWidth();
1388
if (cx > maxMiddlingX)
1392
cx = child.getX() + child.getWidth();
1393
if (cx > maxTrailingX)
1400
minMiddlingX = Math.max(minMiddlingX, maxTrailingX);
1401
maxMiddlingX = Math.min(maxMiddlingX, minLeadingX);
1402
int leadWidth = minLeadingX - maxMiddlingX- 15;
1403
int trailWidth = minMiddlingX - maxTrailingX - 15;
1405
if (maxMiddlingX <= minMiddlingX) {
1406
start = maxTrailingX + 5;
1407
end = minLeadingX - 10;
1408
} else if ((preferredWidth > leadWidth)
1409
&& ((trailWidth > leadWidth) || (preferredWidth <= trailWidth)))
1411
start = maxTrailingX+ 10;
1412
end = minMiddlingX - 5;
1414
start = maxMiddlingX + 5;
1415
end = minLeadingX - 10;
1418
return new Rectangle(start, 0, end - start, this.getHeight());
1423
* Actions used to <code>close</code> the <code>Window</code>.
1425
private class CloseAction extends AbstractAction {
1427
* Creates a new close action.
1429
public CloseAction() {
1430
super(SubstanceCoreUtilities.getResourceBundle(rootPane).getString(
1431
"SystemMenu.close"), SubstanceImageCreator.getCloseIcon(
1432
SubstanceCoreUtilities.getSkin(rootPane)
1433
.getActiveColorScheme(getThisDecorationType()),
1434
SubstanceCoreUtilities.getSkin(rootPane)
1435
.getBackgroundColorScheme(getThisDecorationType())));
1439
public void actionPerformed(ActionEvent e) {
1440
Window window = SubstanceTitlePane.this.getWindow();
1442
if (window != null) {
1443
window.dispatchEvent(new WindowEvent(window,
1444
WindowEvent.WINDOW_CLOSING));
1450
* Actions used to <code>iconfiy</code> the <code>Frame</code>.
1452
private class IconifyAction extends AbstractAction {
1454
* Creates a new iconify action.
1456
public IconifyAction() {
1458
SubstanceCoreUtilities.getResourceBundle(rootPane)
1459
.getString("SystemMenu.iconify"),
1460
SubstanceImageCreator
1462
SubstanceCoreUtilities
1464
.getActiveColorScheme(
1465
getThisDecorationType()),
1466
SubstanceCoreUtilities
1468
.getBackgroundColorScheme(
1469
getThisDecorationType())));
1473
public void actionPerformed(ActionEvent e) {
1474
Frame frame = SubstanceTitlePane.this.getFrame();
1475
if (frame != null) {
1476
frame.setExtendedState(SubstanceTitlePane.this.state
1483
* Actions used to <code>restore</code> the <code>Frame</code>.
1485
private class RestoreAction extends AbstractAction {
1487
* Creates a new restore action.
1489
public RestoreAction() {
1491
SubstanceCoreUtilities.getResourceBundle(rootPane)
1492
.getString("SystemMenu.restore"),
1493
SubstanceImageCreator
1495
SubstanceCoreUtilities
1497
.getActiveColorScheme(
1498
getThisDecorationType()),
1499
SubstanceCoreUtilities
1501
.getBackgroundColorScheme(
1502
getThisDecorationType())));
1506
public void actionPerformed(ActionEvent e) {
1507
Frame frame = SubstanceTitlePane.this.getFrame();
1509
if (frame == null) {
1513
if ((SubstanceTitlePane.this.state & Frame.ICONIFIED) != 0) {
1514
frame.setExtendedState(SubstanceTitlePane.this.state
1515
& ~Frame.ICONIFIED);
1517
frame.setExtendedState(SubstanceTitlePane.this.state
1518
& ~Frame.MAXIMIZED_BOTH);
1524
* Actions used to <code>restore</code> the <code>Frame</code>.
1526
private class MaximizeAction extends AbstractAction {
1528
* Creates a new maximize action.
1530
public MaximizeAction() {
1532
SubstanceCoreUtilities.getResourceBundle(rootPane)
1533
.getString("SystemMenu.maximize"),
1534
SubstanceImageCreator
1536
SubstanceCoreUtilities
1538
.getActiveColorScheme(
1539
getThisDecorationType()),
1540
SubstanceCoreUtilities
1542
.getEnabledColorScheme(
1543
getThisDecorationType())));
1547
public void actionPerformed(ActionEvent e) {
1548
Frame frame = SubstanceTitlePane.this.getFrame();
1549
if (frame != null) {
1550
if (frame instanceof JFrame) {
1551
SubstanceRootPaneUI rpUI = (SubstanceRootPaneUI) ((JFrame) frame)
1552
.getRootPane().getUI();
1553
rpUI.setMaximized();
1555
frame.setExtendedState(SubstanceTitlePane.this.state
1556
| Frame.MAXIMIZED_BOTH);
1562
* Class responsible for drawing the system menu. Looks up the image to draw
1563
* from the Frame associated with the <code>JRootPane</code>.
1565
public class SubstanceMenuBar extends JMenuBar {
1567
public void paint(Graphics g) {
1568
if (appIcon != null) {
1569
g.drawImage(appIcon, 0, 0, null);
1571
Icon icon = UIManager.getIcon("InternalFrame.icon");
1573
icon.paintIcon(this, g, 0, 0);
1579
public Dimension getMinimumSize() {
1580
return this.getPreferredSize();
1584
public Dimension getPreferredSize() {
1585
Dimension size = super.getPreferredSize();
1587
int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
1588
return new Dimension(Math.max(iSize, size.width), Math.max(
1589
size.height, iSize));
1594
* Layout manager for the title pane.
1596
* @author Kirill Graphics
1598
protected class TitlePaneLayout implements LayoutManager {
1602
* @see java.awt.LayoutManager#addLayoutComponent(java.lang.String,
1603
* java.awt.Component)
1606
public void addLayoutComponent(String name, Component c) {
1612
* @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component)
1615
public void removeLayoutComponent(Component c) {
1621
* @see java.awt.LayoutManager#preferredLayoutSize(java.awt.Container)
1624
public Dimension preferredLayoutSize(Container c) {
1625
int height = this.computeHeight();
1626
//noinspection SuspiciousNameCombination
1627
return new Dimension(height, height);
1631
* Computes title pane height.
1633
* @return Title pane height.
1635
private int computeHeight() {
1636
FontMetrics fm = SubstanceTitlePane.this.rootPane
1637
.getFontMetrics(SubstanceTitlePane.this.getFont());
1638
int fontHeight = fm.getHeight();
1640
int iconHeight = SubstanceSizeUtils.getTitlePaneIconSize();
1642
return Math.max(fontHeight, iconHeight);
1648
* @see java.awt.LayoutManager#minimumLayoutSize(java.awt.Container)
1651
public Dimension minimumLayoutSize(Container c) {
1652
return this.preferredLayoutSize(c);
1658
* @see java.awt.LayoutManager#layoutContainer(java.awt.Container)
1661
public void layoutContainer(Container c) {
1662
boolean leftToRight = (SubstanceTitlePane.this.window == null) ? SubstanceTitlePane.this
1663
.getRootPane().getComponentOrientation().isLeftToRight()
1664
: SubstanceTitlePane.this.window.getComponentOrientation()
1667
int w = SubstanceTitlePane.this.getWidth();
1674
if ((SubstanceTitlePane.this.closeButton != null)
1675
&& (SubstanceTitlePane.this.closeButton.getIcon() != null)) {
1676
buttonHeight = SubstanceTitlePane.this.closeButton.getIcon()
1678
buttonWidth = SubstanceTitlePane.this.closeButton.getIcon()
1681
buttonHeight = SubstanceSizeUtils.getTitlePaneIconSize();
1682
buttonWidth = SubstanceSizeUtils.getTitlePaneIconSize();
1685
y = (getHeight() - buttonHeight) / 2;
1687
// assumes all buttons have the same dimensions
1688
// these dimensions include the borders
1691
x = leftToRight ? spacing : w - buttonWidth - spacing;
1692
if (SubstanceTitlePane.this.menuBar != null) {
1693
SubstanceTitlePane.this.menuBar.setBounds(x, y, buttonWidth,
1695
// System.out.println(menuBar.getBounds());
1698
x = leftToRight ? w : 0;
1700
x += leftToRight ? -spacing - buttonWidth : spacing;
1701
if (SubstanceTitlePane.this.closeButton != null) {
1702
SubstanceTitlePane.this.closeButton.setBounds(x, y,
1703
buttonWidth, buttonHeight);
1709
if (SubstanceTitlePane.this.getWindowDecorationStyle() == JRootPane.FRAME) {
1710
if (Toolkit.getDefaultToolkit().isFrameStateSupported(
1711
Frame.MAXIMIZED_BOTH)) {
1712
if (SubstanceTitlePane.this.toggleButton.getParent() != null) {
1714
x += leftToRight ? -spacing - buttonWidth : spacing;
1715
SubstanceTitlePane.this.toggleButton.setBounds(x, y,
1716
buttonWidth, buttonHeight);
1723
if ((SubstanceTitlePane.this.minimizeButton != null)
1724
&& (SubstanceTitlePane.this.minimizeButton.getParent() != null)) {
1726
x += leftToRight ? -spacing - buttonWidth : spacing;
1727
SubstanceTitlePane.this.minimizeButton.setBounds(x, y,
1728
buttonWidth, buttonHeight);
1734
if ((SubstanceTitlePane.this.heapStatusPanel != null)
1735
&& SubstanceTitlePane.this.heapStatusPanel.isVisible()) {
1737
x += leftToRight ? (-spacing - SubstanceTitlePane.this.heapStatusPanel
1738
.getPreferredWidth())
1740
SubstanceTitlePane.this.heapStatusPanel.setBounds(x, 1,
1741
SubstanceTitlePane.this.heapStatusPanel
1742
.getPreferredWidth(),
1743
SubstanceTitlePane.this.getHeight() - 3);
1746
// buttonsWidth = leftToRight ? w - x : x;
1752
* PropertyChangeListener installed on the Window. Updates the necessary
1753
* state as the state of the Window changes.
1755
private class PropertyChangeHandler implements PropertyChangeListener {
1757
public void propertyChange(PropertyChangeEvent pce) {
1758
String name = pce.getPropertyName();
1760
// Frame.state isn't currently bound.
1761
if ("resizable".equals(name) || "state".equals(name)) {
1762
Frame frame = SubstanceTitlePane.this.getFrame();
1764
if (frame != null) {
1765
SubstanceTitlePane.this.setState(frame.getExtendedState(),
1768
if ("resizable".equals(name)) {
1769
SubstanceTitlePane.this.getRootPane().repaint();
1772
if ("title".equals(name)) {
1773
SubstanceTitlePane.this.repaint();
1774
SubstanceTitlePane.this.setToolTipText((String) pce
1776
} else if ("componentOrientation".equals(name)) {
1779
} else if ("iconImage".equals(name)) {
1789
* WindowListener installed on the Window, updates the state as necessary.
1791
private class WindowHandler extends WindowAdapter {
1793
public void windowActivated(WindowEvent ev) {
1794
SubstanceTitlePane.this.setActive(true);
1798
public void windowDeactivated(WindowEvent ev) {
1799
SubstanceTitlePane.this.setActive(false);
1804
* Sets location for heap status logfile.
1806
* @param heapStatusLogfileName
1807
* Logfile for the heap status panel.
1809
public static void setHeapStatusLogfileName(String heapStatusLogfileName) {
1810
SubstanceTitlePane.heapStatusLogfileName = heapStatusLogfileName;
1814
* Synchronizes the tooltip of the close button.
1816
protected void syncCloseButtonTooltip() {
1817
if (SubstanceCoreUtilities.isRootPaneModified(this.getRootPane())) {
1818
this.closeButton.setToolTipText(SubstanceCoreUtilities
1819
.getResourceBundle(rootPane).getString("SystemMenu.close")
1821
+ SubstanceCoreUtilities.getResourceBundle(rootPane)
1822
.getString("Tooltip.contentsNotSaved") + "]");
1824
this.closeButton.setToolTipText(SubstanceCoreUtilities
1825
.getResourceBundle(rootPane).getString("SystemMenu.close"));
1827
this.closeButton.repaint();
1831
* Marks the specified child component with the specified extra component
1838
* @see #getTitleTextRectangle(int)
1839
* @see #EXTRA_COMPONENT_KIND
1841
protected void markExtraComponent(JComponent comp, ExtraComponentKind kind) {
1842
comp.putClientProperty(EXTRA_COMPONENT_KIND, kind);
1846
* Updates the application icon.
1848
private void updateAppIcon() {
1849
Window window = getWindow();
1850
if (window == null) {
1851
this.appIcon = null;
1854
this.appIcon = null;
1855
while (window != null && appIcon == null) {
1856
java.util.List<Image> icons = window.getIconImages();
1858
if (icons.size() == 0) {
1859
window = window.getOwner();
1861
int prefSize = SubstanceSizeUtils.getTitlePaneIconSize();
1862
this.appIcon = SubstanceCoreUtilities.getScaledIconImage(icons,
1863
prefSize, prefSize);
1868
public AbstractButton getCloseButton() {
1869
return this.closeButton;