/*
* Copyright (c) 1997, 2014, 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.basic;
import sun.swing.DefaultLookup;
import sun.swing.UIAction;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.OptionPaneUI;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Locale;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
/**
* Provides the basic look and feel for a JOptionPane.
* BasicMessagePaneUI provides a means to place an icon,
* message and buttons into a Container.
* Generally, the layout will look like:
*
* ------------------
* | i | message |
* | c | message |
* | o | message |
* | n | message |
* ------------------
* | buttons |
* |________________|
*
* icon is an instance of Icon that is wrapped inside a
* JLabel. The message is an opaque object and is tested
* for the following: if the message is a Component it is
* added to the Container, if it is an Icon
* it is wrapped inside a JLabel and added to the
* Container otherwise it is wrapped inside a JLabel.
*
* The above layout is used when the option pane's
* ComponentOrientation property is horizontal, left-to-right.
* The layout will be adjusted appropriately for other orientations.
*
* The Container, message, icon, and buttons are all
* determined from abstract methods.
*
* @author James Gosling
* @author Scott Violet
* @author Amy Fowler
*/
public class BasicOptionPaneUI extends OptionPaneUI {
public static final int MinimumWidth = 262;
public static final int MinimumHeight = 90;
private static String newline;
/**
* JOptionPane that the receiver is providing the
* look and feel for.
*/
protected JOptionPane optionPane;
protected Dimension minimumSize;
/** JComponent provide for input if optionPane.getWantsInput() returns
* true. */
protected JComponent inputComponent;
/** Component to receive focus when messaged with selectInitialValue. */
protected Component initialFocusComponent;
/** This is set to true in validateComponent if a Component is contained
* in either the message or the buttons. */
protected boolean hasCustomComponents;
protected PropertyChangeListener propertyChangeListener;
private Handler handler;
static {
newline = System.lineSeparator();
if (newline == null) {
newline = "\n";
}
}
static void loadActionMap(LazyActionMap map) {
map.put(new Actions(Actions.CLOSE));
BasicLookAndFeel.installAudioActionMap(map);
}
/**
* Creates a new BasicOptionPaneUI instance.
*/
public static ComponentUI createUI(JComponent x) {
return new BasicOptionPaneUI();
}
/**
* Installs the receiver as the L&F for the passed in
* JOptionPane.
*/
public void installUI(JComponent c) {
optionPane = (JOptionPane)c;
installDefaults();
optionPane.setLayout(createLayoutManager());
installComponents();
installListeners();
installKeyboardActions();
}
/**
* Removes the receiver from the L&F controller of the passed in split
* pane.
*/
public void uninstallUI(JComponent c) {
uninstallComponents();
optionPane.setLayout(null);
uninstallKeyboardActions();
uninstallListeners();
uninstallDefaults();
optionPane = null;
}
protected void installDefaults() {
LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
"OptionPane.foreground", "OptionPane.font");
LookAndFeel.installBorder(optionPane, "OptionPane.border");
minimumSize = UIManager.getDimension("OptionPane.minimumSize");
LookAndFeel.installProperty(optionPane, "opaque", Boolean.TRUE);
}
protected void uninstallDefaults() {
LookAndFeel.uninstallBorder(optionPane);
}
protected void installComponents() {
optionPane.add(createMessageArea());
Container separator = createSeparator();
if (separator != null) {
optionPane.add(separator);
}
optionPane.add(createButtonArea());
optionPane.applyComponentOrientation(optionPane.getComponentOrientation());
}
protected void uninstallComponents() {
hasCustomComponents = false;
inputComponent = null;
initialFocusComponent = null;
optionPane.removeAll();
}
protected LayoutManager createLayoutManager() {
return new BoxLayout(optionPane, BoxLayout.Y_AXIS);
}
protected void installListeners() {
if ((propertyChangeListener = createPropertyChangeListener()) != null) {
optionPane.addPropertyChangeListener(propertyChangeListener);
}
}
protected void uninstallListeners() {
if (propertyChangeListener != null) {
optionPane.removePropertyChangeListener(propertyChangeListener);
propertyChangeListener = null;
}
handler = null;
}
protected PropertyChangeListener createPropertyChangeListener() {
return getHandler();
}
private Handler getHandler() {
if (handler == null) {
handler = new Handler();
}
return handler;
}
protected void installKeyboardActions() {
InputMap map = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
SwingUtilities.replaceUIInputMap(optionPane, JComponent.
WHEN_IN_FOCUSED_WINDOW, map);
LazyActionMap.installLazyActionMap(optionPane, BasicOptionPaneUI.class,
"OptionPane.actionMap");
}
protected void uninstallKeyboardActions() {
SwingUtilities.replaceUIInputMap(optionPane, JComponent.
WHEN_IN_FOCUSED_WINDOW, null);
SwingUtilities.replaceUIActionMap(optionPane, null);
}
InputMap getInputMap(int condition) {
if (condition == JComponent.WHEN_IN_FOCUSED_WINDOW) {
Object[] bindings = (Object[])DefaultLookup.get(
optionPane, this, "OptionPane.windowBindings");
if (bindings != null) {
return LookAndFeel.makeComponentInputMap(optionPane, bindings);
}
}
return null;
}
/**
* Returns the minimum size the option pane should be. Primarily
* provided for subclassers wishing to offer a different minimum size.
*/
public Dimension getMinimumOptionPaneSize() {
if (minimumSize == null) {
return new Dimension(MinimumWidth, MinimumHeight);
}
return new Dimension(minimumSize.width,
minimumSize.height);
}
/**
* If c is the JOptionPane the receiver
* is contained in, the preferred
* size that is returned is the maximum of the preferred size of
* the LayoutManager for the JOptionPane, and
* getMinimumOptionPaneSize.
*/
public Dimension getPreferredSize(JComponent c) {
if (c == optionPane) {
Dimension ourMin = getMinimumOptionPaneSize();
LayoutManager lm = c.getLayout();
if (lm != null) {
Dimension lmSize = lm.preferredLayoutSize(c);
if (ourMin != null)
return new Dimension
(Math.max(lmSize.width, ourMin.width),
Math.max(lmSize.height, ourMin.height));
return lmSize;
}
return ourMin;
}
return null;
}
/**
* Messaged from installComponents to create a Container containing the
* body of the message. The icon is the created by calling
* addIcon.
*/
protected Container createMessageArea() {
JPanel top = new JPanel();
Border topBorder = (Border)DefaultLookup.get(optionPane, this,
"OptionPane.messageAreaBorder");
if (topBorder != null) {
top.setBorder(topBorder);
}
top.setLayout(new BorderLayout());
/* Fill the body. */
Container body = new JPanel(new GridBagLayout());
Container realBody = new JPanel(new BorderLayout());
body.setName("OptionPane.body");
realBody.setName("OptionPane.realBody");
if (getIcon() != null) {
JPanel sep = new JPanel();
sep.setName("OptionPane.separator");
sep.setPreferredSize(new Dimension(15, 1));
realBody.add(sep, BorderLayout.BEFORE_LINE_BEGINS);
}
realBody.add(body, BorderLayout.CENTER);
GridBagConstraints cons = new GridBagConstraints();
cons.gridx = cons.gridy = 0;
cons.gridwidth = GridBagConstraints.REMAINDER;
cons.gridheight = 1;
cons.anchor = DefaultLookup.getInt(optionPane, this,
"OptionPane.messageAnchor", GridBagConstraints.CENTER);
cons.insets = new Insets(0,0,3,0);
addMessageComponents(body, cons, getMessage(),
getMaxCharactersPerLineCount(), false);
top.add(realBody, BorderLayout.CENTER);
addIcon(top);
return top;
}
/**
* Creates the appropriate object to represent msg and
* places it into container. If msg is an
* instance of Component, it is added directly, if it is an Icon,
* a JLabel is created to represent it, otherwise a JLabel is
* created for the string, if d is an Object[], this
* method will be recursively invoked for the children.
* internallyCreated is true if Objc is an instance
* of Component and was created internally by this method (this is
* used to correctly set hasCustomComponents only if !internallyCreated).
*/
protected void addMessageComponents(Container container,
GridBagConstraints cons,
Object msg, int maxll,
boolean internallyCreated) {
if (msg == null) {
return;
}
if (msg instanceof Component) {
// To workaround problem where Gridbad will set child
// to its minimum size if its preferred size will not fit
// within allocated cells
if (msg instanceof JScrollPane || msg instanceof JPanel) {
cons.fill = GridBagConstraints.BOTH;
cons.weighty = 1;
} else {
cons.fill = GridBagConstraints.HORIZONTAL;
}
cons.weightx = 1;
container.add((Component) msg, cons);
cons.weightx = 0;
cons.weighty = 0;
cons.fill = GridBagConstraints.NONE;
cons.gridy++;
if (!internallyCreated) {
hasCustomComponents = true;
}
} else if (msg instanceof Object[]) {
Object [] msgs = (Object[]) msg;
for (Object o : msgs) {
addMessageComponents(container, cons, o, maxll, false);
}
} else if (msg instanceof Icon) {
JLabel label = new JLabel( (Icon)msg, SwingConstants.CENTER );
configureMessageLabel(label);
addMessageComponents(container, cons, label, maxll, true);
} else {
String s = msg.toString();
int len = s.length();
if (len <= 0) {
return;
}
int nl;
int nll = 0;
if ((nl = s.indexOf(newline)) >= 0) {
nll = newline.length();
} else if ((nl = s.indexOf("\r\n")) >= 0) {
nll = 2;
} else if ((nl = s.indexOf('\n')) >= 0) {
nll = 1;
}
if (nl >= 0) {
// break up newlines
if (nl == 0) {
@SuppressWarnings("serial") // anonymous class
JPanel breakPanel = new JPanel() {
public Dimension getPreferredSize() {
Font f = getFont();
if (f != null) {
return new Dimension(1, f.getSize() + 2);
}
return new Dimension(0, 0);
}
};
breakPanel.setName("OptionPane.break");
addMessageComponents(container, cons, breakPanel, maxll,
true);
} else {
addMessageComponents(container, cons, s.substring(0, nl),
maxll, false);
}
addMessageComponents(container, cons, s.substring(nl + nll), maxll,
false);
} else if (len > maxll) {
Container c = Box.createVerticalBox();
c.setName("OptionPane.verticalBox");
burstStringInto(c, s, maxll);
addMessageComponents(container, cons, c, maxll, true );
} else {
JLabel label;
label = new JLabel( s, JLabel.LEADING );
label.setName("OptionPane.label");
configureMessageLabel(label);
addMessageComponents(container, cons, label, maxll, true);
}
}
}
/**
* Returns the message to display from the JOptionPane the receiver is
* providing the look and feel for.
*/
protected Object getMessage() {
inputComponent = null;
if (optionPane != null) {
if (optionPane.getWantsInput()) {
/* Create a user component to capture the input. If the
selectionValues are non null the component and there
are < 20 values it'll be a combobox, if non null and
>= 20, it'll be a list, otherwise it'll be a textfield. */
Object message = optionPane.getMessage();
Object[] sValues = optionPane.getSelectionValues();
Object inputValue = optionPane
.getInitialSelectionValue();
JComponent toAdd;
if (sValues != null) {
if (sValues.length < 20) {
JComboBox