/*
* Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.plaf.metal;
import sun.swing.SwingUtilities2;
import sun.awt.SunToolkit;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.InternalFrameEvent;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.util.Locale;
import javax.accessibility.*;
/**
* Class that manages a JLF awt.Window-descendant class's title bar.
* <p>
* This class assumes it will be created with a particular window
* decoration style, and that if the style changes, a new one will
* be created.
*
* @author Terry Kellerman
* @since 1.4
*/
class MetalTitlePane extends JComponent {
private static final Border handyEmptyBorder = new EmptyBorder(0,0,0,0);
private static final int IMAGE_HEIGHT = 16;
private static final int IMAGE_WIDTH = 16;
/**
* PropertyChangeListener added to the JRootPane.
*/
private PropertyChangeListener propertyChangeListener;
/**
* JMenuBar, typically renders the system menu items.
*/
private JMenuBar menuBar;
/**
* Action used to close the Window.
*/
private Action closeAction;
/**
* Action used to iconify the Frame.
*/
private Action iconifyAction;
/**
* Action to restore the Frame size.
*/
private Action restoreAction;
/**
* Action to restore the Frame size.
*/
private Action maximizeAction;
/**
* Button used to maximize or restore the Frame.
*/
private JButton toggleButton;
/**
* Button used to maximize or restore the Frame.
*/
private JButton iconifyButton;
/**
* Button used to maximize or restore the Frame.
*/
private JButton closeButton;
/**
* Icon used for toggleButton when window is normal size.
*/
private Icon maximizeIcon;
/**
* Icon used for toggleButton when window is maximized.
*/
private Icon minimizeIcon;
/**
* Image used for the system menu icon
*/
private Image systemIcon;
/**
* Listens for changes in the state of the Window listener to update
* the state of the widgets.
*/
private WindowListener windowListener;
/**
* Window we're currently in.
*/
private Window window;
/**
* JRootPane rendering for.
*/
private JRootPane rootPane;
/**
* Room remaining in title for bumps.
*/
private int buttonsWidth;
/**
* Buffered Frame.state property. As state isn't bound, this is kept
* to determine when to avoid updating widgets.
*/
private int state;
/**
* MetalRootPaneUI that created us.
*/
private MetalRootPaneUI rootPaneUI;
// Colors
private Color inactiveBackground = UIManager.getColor("inactiveCaption");
private Color inactiveForeground = UIManager.getColor("inactiveCaptionText");
private Color inactiveShadow = UIManager.getColor("inactiveCaptionBorder");
private Color activeBumpsHighlight = MetalLookAndFeel.getPrimaryControlHighlight();
private Color activeBumpsShadow = MetalLookAndFeel.getPrimaryControlDarkShadow();
private Color activeBackground = null;
private Color activeForeground = null;
private Color activeShadow = null;
// Bumps
private MetalBumps activeBumps
= new MetalBumps( 0, 0,
activeBumpsHighlight,
activeBumpsShadow,
MetalLookAndFeel.getPrimaryControl() );
private MetalBumps inactiveBumps
= new MetalBumps( 0, 0,
MetalLookAndFeel.getControlHighlight(),
MetalLookAndFeel.getControlDarkShadow(),
MetalLookAndFeel.getControl() );
public MetalTitlePane(JRootPane root, MetalRootPaneUI ui) {
this.rootPane = root;
rootPaneUI = ui;
state = -1;
installSubcomponents();
determineColors();
installDefaults();
setLayout(createLayout());
}
/**
* Uninstalls the necessary state.
*/
private void uninstall() {
uninstallListeners();
window = null;
removeAll();
}
/**
* Installs the necessary listeners.
*/
private void installListeners() {
if (window != null) {
windowListener = createWindowListener();
window.addWindowListener(windowListener);
propertyChangeListener = createWindowPropertyChangeListener();
window.addPropertyChangeListener(propertyChangeListener);
}
}
/**
* Uninstalls the necessary listeners.
*/
private void uninstallListeners() {
if (window != null) {
window.removeWindowListener(windowListener);
window.removePropertyChangeListener(propertyChangeListener);
}
}
/**
* Returns the <code>WindowListener</code> to add to the
* <code>Window</code>.
*/
private WindowListener createWindowListener() {
return new WindowHandler();
}
/**
* Returns the <code>PropertyChangeListener</code> to install on
* the <code>Window</code>.
*/
private PropertyChangeListener createWindowPropertyChangeListener() {
return new PropertyChangeHandler();
}
/**
* Returns the <code>JRootPane</code> this was created for.
*/
public JRootPane getRootPane() {
return rootPane;
}
/**
* Returns the decoration style of the <code>JRootPane</code>.
*/
private int getWindowDecorationStyle() {
return getRootPane().getWindowDecorationStyle();
}
public void addNotify() {
super.addNotify();
uninstallListeners();
window = SwingUtilities.getWindowAncestor(this);
if (window != null) {
if (window instanceof Frame) {
setState(((Frame)window).getExtendedState());
}
else {
setState(0);
}
setActive(window.isActive());
installListeners();
updateSystemIcon();
}
}
public void removeNotify() {
super.removeNotify();
uninstallListeners();
window = null;
}
/**
* Adds any sub-Components contained in the <code>MetalTitlePane</code>.
*/
private void installSubcomponents() {
int decorationStyle = getWindowDecorationStyle();
if (decorationStyle == JRootPane.FRAME) {
createActions();
menuBar = createMenuBar();
add(menuBar);
createButtons();
add(iconifyButton);
add(toggleButton);
add(closeButton);
} else if (decorationStyle == JRootPane.PLAIN_DIALOG ||
decorationStyle == JRootPane.INFORMATION_DIALOG ||
decorationStyle == JRootPane.ERROR_DIALOG ||
decorationStyle == JRootPane.COLOR_CHOOSER_DIALOG ||
decorationStyle == JRootPane.FILE_CHOOSER_DIALOG ||
decorationStyle == JRootPane.QUESTION_DIALOG ||
decorationStyle == JRootPane.WARNING_DIALOG) {
createActions();
createButtons();
add(closeButton);
}
}
/**
* Determines the Colors to draw with.
*/
private void determineColors() {
switch (getWindowDecorationStyle()) {
case JRootPane.FRAME:
activeBackground = UIManager.getColor("activeCaption");
activeForeground = UIManager.getColor("activeCaptionText");
activeShadow = UIManager.getColor("activeCaptionBorder");
break;
case JRootPane.ERROR_DIALOG:
activeBackground = UIManager.getColor(
"OptionPane.errorDialog.titlePane.background");
activeForeground = UIManager.getColor(
"OptionPane.errorDialog.titlePane.foreground");
activeShadow = UIManager.getColor(
"OptionPane.errorDialog.titlePane.shadow");
break;
case JRootPane.QUESTION_DIALOG:
case JRootPane.COLOR_CHOOSER_DIALOG:
case JRootPane.FILE_CHOOSER_DIALOG:
activeBackground = UIManager.getColor(
"OptionPane.questionDialog.titlePane.background");
activeForeground = UIManager.getColor(
"OptionPane.questionDialog.titlePane.foreground");
activeShadow = UIManager.getColor(
"OptionPane.questionDialog.titlePane.shadow");
break;
case JRootPane.WARNING_DIALOG:
activeBackground = UIManager.getColor(
"OptionPane.warningDialog.titlePane.background");
activeForeground = UIManager.getColor(
"OptionPane.warningDialog.titlePane.foreground");
activeShadow = UIManager.getColor(
"OptionPane.warningDialog.titlePane.shadow");
break;
case JRootPane.PLAIN_DIALOG:
case JRootPane.INFORMATION_DIALOG:
default:
activeBackground = UIManager.getColor("activeCaption");
activeForeground = UIManager.getColor("activeCaptionText");
activeShadow = UIManager.getColor("activeCaptionBorder");
break;
}
activeBumps.setBumpColors(activeBumpsHighlight, activeBumpsShadow,
activeBackground);
}
/**
* Installs the fonts and necessary properties on the MetalTitlePane.
*/
private void installDefaults() {
setFont(UIManager.getFont("InternalFrame.titleFont", getLocale()));
}
/**
* Uninstalls any previously installed UI values.
*/
private void uninstallDefaults() {
}
/**
* Returns the <code>JMenuBar</code> displaying the appropriate
* system menu items.
*/
protected JMenuBar createMenuBar() {
menuBar = new SystemMenuBar();
menuBar.setFocusable(false);
menuBar.setBorderPainted(true);
menuBar.add(createMenu());
return menuBar;
}
/**
* Closes the Window.
*/
private void close() {
Window window = getWindow();
if (window != null) {
window.dispatchEvent(new WindowEvent(
window, WindowEvent.WINDOW_CLOSING));
}
}
/**
* Iconifies the Frame.
*/
private void iconify() {
Frame frame = getFrame();
if (frame != null) {
frame.setExtendedState(state | Frame.ICONIFIED);
}
}
/**
* Maximizes the Frame.
*/
private void maximize() {
Frame frame = getFrame();
if (frame != null) {
frame.setExtendedState(state | Frame.MAXIMIZED_BOTH);
}
}
/**
* Restores the Frame size.
*/
private void restore() {
Frame frame = getFrame();
if (frame == null) {
return;
}
if ((state & Frame.ICONIFIED) != 0) {
frame.setExtendedState(state & ~Frame.ICONIFIED);
} else {
frame.setExtendedState(state & ~Frame.MAXIMIZED_BOTH);
}
}
/**
* Create the <code>Action</code>s that get associated with the
* buttons and menu items.
*/
private void createActions() {
closeAction = new CloseAction();
if (getWindowDecorationStyle() == JRootPane.FRAME) {
iconifyAction = new IconifyAction();
restoreAction = new RestoreAction();
maximizeAction = new MaximizeAction();
}
}
/**
* Returns the <code>JMenu</code> displaying the appropriate menu items
* for manipulating the Frame.
*/
private JMenu createMenu() {
JMenu menu = new JMenu("");
if (getWindowDecorationStyle() == JRootPane.FRAME) {
addMenuItems(menu);
}
return menu;
}
/**
* Adds the necessary <code>JMenuItem</code>s to the passed in menu.
*/
private void addMenuItems(JMenu menu) {
Locale locale = getRootPane().getLocale();
JMenuItem mi = menu.add(restoreAction);
int mnemonic = MetalUtils.getInt("MetalTitlePane.restoreMnemonic", -1);
if (mnemonic != -1) {
mi.setMnemonic(mnemonic);
}
mi = menu.add(iconifyAction);
mnemonic = MetalUtils.getInt("MetalTitlePane.iconifyMnemonic", -1);
if (mnemonic != -1) {
mi.setMnemonic(mnemonic);
}
if (Toolkit.getDefaultToolkit().isFrameStateSupported(
Frame.MAXIMIZED_BOTH)) {
mi = menu.add(maximizeAction);
mnemonic =
MetalUtils.getInt("MetalTitlePane.maximizeMnemonic", -1);
if (mnemonic != -1) {
mi.setMnemonic(mnemonic);
}
}
menu.add(new JSeparator());
mi = menu.add(closeAction);
mnemonic = MetalUtils.getInt("MetalTitlePane.closeMnemonic", -1);
if (mnemonic != -1) {
mi.setMnemonic(mnemonic);
}
}
/**
* Returns a <code>JButton</code> appropriate for placement on the
* TitlePane.
*/
private JButton createTitleButton() {
JButton button = new JButton();
button.setFocusPainted(false);
button.setFocusable(false);
button.setOpaque(true);
return button;
}
/**
* Creates the Buttons that will be placed on the TitlePane.
*/
private void createButtons() {
closeButton = createTitleButton();
closeButton.setAction(closeAction);
closeButton.setText(null);
closeButton.putClientProperty("paintActive", Boolean.TRUE);
closeButton.setBorder(handyEmptyBorder);
closeButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
"Close");
closeButton.setIcon(UIManager.getIcon("InternalFrame.closeIcon"));
if (getWindowDecorationStyle() == JRootPane.FRAME) {
maximizeIcon = UIManager.getIcon("InternalFrame.maximizeIcon");
minimizeIcon = UIManager.getIcon("InternalFrame.minimizeIcon");
iconifyButton = createTitleButton();
iconifyButton.setAction(iconifyAction);
iconifyButton.setText(null);
iconifyButton.putClientProperty("paintActive", Boolean.TRUE);
iconifyButton.setBorder(handyEmptyBorder);
iconifyButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
"Iconify");
iconifyButton.setIcon(UIManager.getIcon("InternalFrame.iconifyIcon"));
toggleButton = createTitleButton();
toggleButton.setAction(restoreAction);
toggleButton.putClientProperty("paintActive", Boolean.TRUE);
toggleButton.setBorder(handyEmptyBorder);
toggleButton.putClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY,
"Maximize");
toggleButton.setIcon(maximizeIcon);
}
}
/**
* Returns the <code>LayoutManager</code> that should be installed on
* the <code>MetalTitlePane</code>.
*/
private LayoutManager createLayout() {
return new TitlePaneLayout();
}
/**
* Updates state dependant upon the Window's active state.
*/
private void setActive(boolean isActive) {
Boolean activeB = isActive ? Boolean.TRUE : Boolean.FALSE;
closeButton.putClientProperty("paintActive", activeB);
if (getWindowDecorationStyle() == JRootPane.FRAME) {
iconifyButton.putClientProperty("paintActive", activeB);
toggleButton.putClientProperty("paintActive", activeB);
}
// Repaint the whole thing as the Borders that are used have
// different colors for active vs inactive
getRootPane().repaint();
}
/**
* Sets the state of the Window.
*/
private void setState(int state) {
setState(state, false);
}
/**
* Sets the state of the window. If <code>updateRegardless</code> is
* true and the state has not changed, this will update anyway.
*/
private void setState(int state, boolean updateRegardless) {
Window w = getWindow();
if (w != null && getWindowDecorationStyle() == JRootPane.FRAME) {
if (this.state == state && !updateRegardless) {
return;
}
Frame frame = getFrame();
if (frame != null) {
JRootPane rootPane = getRootPane();
if (((state & Frame.MAXIMIZED_BOTH) != 0) &&
(rootPane.getBorder() == null ||
(rootPane.getBorder() instanceof UIResource)) &&
frame.isShowing()) {
rootPane.setBorder(null);
}
else if ((state & Frame.MAXIMIZED_BOTH) == 0) {
// This is a croak, if state becomes bound, this can
// be nuked.
rootPaneUI.installBorder(rootPane);
}
if (frame.isResizable()) {
if ((state & Frame.MAXIMIZED_BOTH) != 0) {
updateToggleButton(restoreAction, minimizeIcon);
maximizeAction.setEnabled(false);
restoreAction.setEnabled(true);
}
else {
updateToggleButton(maximizeAction, maximizeIcon);
maximizeAction.setEnabled(true);
restoreAction.setEnabled(false);
}
if (toggleButton.getParent() == null ||
iconifyButton.getParent() == null) {
add(toggleButton);
add(iconifyButton);
revalidate();
repaint();
}
toggleButton.setText(null);
}
else {
maximizeAction.setEnabled(false);
restoreAction.setEnabled(false);
if (toggleButton.getParent() != null) {
remove(toggleButton);
revalidate();
repaint();
}
}
}
else {
// Not contained in a Frame
maximizeAction.setEnabled(false);
restoreAction.setEnabled(false);
iconifyAction.setEnabled(false);
remove(toggleButton);
remove(iconifyButton);
revalidate();
repaint();
}
closeAction.setEnabled(true);
this.state = state;
}
}
/**
* Updates the toggle button to contain the Icon <code>icon</code>, and
* Action <code>action</code>.
*/
private void updateToggleButton(Action action, Icon icon) {
toggleButton.setAction(action);
toggleButton.setIcon(icon);
toggleButton.setText(null);
}
/**
* Returns the Frame rendering in. This will return null if the
* <code>JRootPane</code> is not contained in a <code>Frame</code>.
*/
private Frame getFrame() {
Window window = getWindow();
if (window instanceof Frame) {
return (Frame)window;
}
return null;
}
/**
* Returns the <code>Window</code> the <code>JRootPane</code> is
* contained in. This will return null if there is no parent ancestor
* of the <code>JRootPane</code>.
*/
private Window getWindow() {
return window;
}
/**
* Returns the String to display as the title.
*/
private String getTitle() {
Window w = getWindow();
if (w instanceof Frame) {
return ((Frame)w).getTitle();
}
else if (w instanceof Dialog) {
return ((Dialog)w).getTitle();
}
return null;
}
/**
* Renders the TitlePane.
*/
public void paintComponent(Graphics g) {
// As state isn't bound, we need a convenience place to check
// if it has changed. Changing the state typically changes the
if (getFrame() != null) {
setState(getFrame().getExtendedState());
}
JRootPane rootPane = getRootPane();
Window window = getWindow();
boolean leftToRight = (window == null) ?
rootPane.getComponentOrientation().isLeftToRight() :
window.getComponentOrientation().isLeftToRight();
boolean isSelected = (window == null) ? true : window.isActive();
int width = getWidth();
int height = getHeight();
Color background;
Color foreground;
Color darkShadow;
MetalBumps bumps;
if (isSelected) {
background = activeBackground;
foreground = activeForeground;
darkShadow = activeShadow;
bumps = activeBumps;
} else {
background = inactiveBackground;
foreground = inactiveForeground;
darkShadow = inactiveShadow;
bumps = inactiveBumps;
}
g.setColor(background);
g.fillRect(0, 0, width, height);
g.setColor( darkShadow );
g.drawLine ( 0, height - 1, width, height -1);
g.drawLine ( 0, 0, 0 ,0);
g.drawLine ( width - 1, 0 , width -1, 0);
int xOffset = leftToRight ? 5 : width - 5;
if (getWindowDecorationStyle() == JRootPane.FRAME) {
xOffset += leftToRight ? IMAGE_WIDTH + 5 : - IMAGE_WIDTH - 5;
}
String theTitle = getTitle();
if (theTitle != null) {
FontMetrics fm = SwingUtilities2.getFontMetrics(rootPane, g);
g.setColor(foreground);
int yOffset = ( (height - fm.getHeight() ) / 2 ) + fm.getAscent();
Rectangle rect = new Rectangle(0, 0, 0, 0);
if (iconifyButton != null && iconifyButton.getParent() != null) {
rect = iconifyButton.getBounds();
}
int titleW;
if( leftToRight ) {
if (rect.x == 0) {
rect.x = window.getWidth() - window.getInsets().right-2;
}
titleW = rect.x - xOffset - 4;
theTitle = SwingUtilities2.clipStringIfNecessary(
rootPane, fm, theTitle, titleW);
} else {
titleW = xOffset - rect.x - rect.width - 4;
theTitle = SwingUtilities2.clipStringIfNecessary(
rootPane, fm, theTitle, titleW);
xOffset -= SwingUtilities2.stringWidth(rootPane, fm,
theTitle);
}
int titleLength = SwingUtilities2.stringWidth(rootPane, fm,
theTitle);
SwingUtilities2.drawString(rootPane, g, theTitle, xOffset,
yOffset );
xOffset += leftToRight ? titleLength + 5 : -5;
}
int bumpXOffset;
int bumpLength;
if( leftToRight ) {
bumpLength = width - buttonsWidth - xOffset - 5;
bumpXOffset = xOffset;
} else {
bumpLength = xOffset - buttonsWidth - 5;
bumpXOffset = buttonsWidth + 5;
}
int bumpYOffset = 3;
int bumpHeight = getHeight() - (2 * bumpYOffset);
bumps.setBumpArea( bumpLength, bumpHeight );
bumps.paintIcon(this, g, bumpXOffset, bumpYOffset);
}
/**
* Actions used to <code>close</code> the <code>Window</code>.
*/
private class CloseAction extends AbstractAction {
public CloseAction() {
super(UIManager.getString("MetalTitlePane.closeTitle",
getLocale()));
}
public void actionPerformed(ActionEvent e) {
close();
}
}
/**
* Actions used to <code>iconfiy</code> the <code>Frame</code>.
*/
private class IconifyAction extends AbstractAction {
public IconifyAction() {
super(UIManager.getString("MetalTitlePane.iconifyTitle",
getLocale()));
}
public void actionPerformed(ActionEvent e) {
iconify();
}
}
/**
* Actions used to <code>restore</code> the <code>Frame</code>.
*/
private class RestoreAction extends AbstractAction {
public RestoreAction() {
super(UIManager.getString
("MetalTitlePane.restoreTitle", getLocale()));
}
public void actionPerformed(ActionEvent e) {
restore();
}
}
/**
* Actions used to <code>restore</code> the <code>Frame</code>.
*/
private class MaximizeAction extends AbstractAction {
public MaximizeAction() {
super(UIManager.getString("MetalTitlePane.maximizeTitle",
getLocale()));
}
public void actionPerformed(ActionEvent e) {
maximize();
}
}
/**
* Class responsible for drawing the system menu. Looks up the
* image to draw from the Frame associated with the
* <code>JRootPane</code>.
*/
private class SystemMenuBar extends JMenuBar {
public void paint(Graphics g) {
if (isOpaque()) {
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
}
if (systemIcon != null) {
g.drawImage(systemIcon, 0, 0, IMAGE_WIDTH, IMAGE_HEIGHT, null);
} else {
Icon icon = UIManager.getIcon("InternalFrame.icon");
if (icon != null) {
icon.paintIcon(this, g, 0, 0);
}
}
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
return new Dimension(Math.max(IMAGE_WIDTH, size.width),
Math.max(size.height, IMAGE_HEIGHT));
}
}
private class TitlePaneLayout implements LayoutManager {
public void addLayoutComponent(String name, Component c) {}
public void removeLayoutComponent(Component c) {}
public Dimension preferredLayoutSize(Container c) {
int height = computeHeight();
return new Dimension(height, height);
}
public Dimension minimumLayoutSize(Container c) {
return preferredLayoutSize(c);
}
private int computeHeight() {
FontMetrics fm = rootPane.getFontMetrics(getFont());
int fontHeight = fm.getHeight();
fontHeight += 7;
int iconHeight = 0;
if (getWindowDecorationStyle() == JRootPane.FRAME) {
iconHeight = IMAGE_HEIGHT;
}
int finalHeight = Math.max( fontHeight, iconHeight );
return finalHeight;
}
public void layoutContainer(Container c) {
boolean leftToRight = (window == null) ?
getRootPane().getComponentOrientation().isLeftToRight() :
window.getComponentOrientation().isLeftToRight();
int w = getWidth();
int x;
int y = 3;
int spacing;
int buttonHeight;
int buttonWidth;
if (closeButton != null && closeButton.getIcon() != null) {
buttonHeight = closeButton.getIcon().getIconHeight();
buttonWidth = closeButton.getIcon().getIconWidth();
}
else {
buttonHeight = IMAGE_HEIGHT;
buttonWidth = IMAGE_WIDTH;
}
// assumes all buttons have the same dimensions
// these dimensions include the borders
x = leftToRight ? w : 0;
spacing = 5;
x = leftToRight ? spacing : w - buttonWidth - spacing;
if (menuBar != null) {
menuBar.setBounds(x, y, buttonWidth, buttonHeight);
}
x = leftToRight ? w : 0;
spacing = 4;
x += leftToRight ? -spacing -buttonWidth : spacing;
if (closeButton != null) {
/**代码未完, 请加载全部代码(NowJava.com).**/