| /* |
| * Copyright (c) 1997, 2008, 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 <code>JOptionPane</code>. |
| * <code>BasicMessagePaneUI</code> provides a means to place an icon, |
| * message and buttons into a <code>Container</code>. |
| * Generally, the layout will look like:<p> |
| * <pre> |
| * ------------------ |
| * | i | message | |
| * | c | message | |
| * | o | message | |
| * | n | message | |
| * ------------------ |
| * | buttons | |
| * |________________| |
| * </pre> |
| * icon is an instance of <code>Icon</code> that is wrapped inside a |
| * <code>JLabel</code>. The message is an opaque object and is tested |
| * for the following: if the message is a <code>Component</code> it is |
| * added to the <code>Container</code>, if it is an <code>Icon</code> |
| * it is wrapped inside a <code>JLabel</code> and added to the |
| * <code>Container</code> otherwise it is wrapped inside a <code>JLabel</code>. |
| * <p> |
| * The above layout is used when the option pane's |
| * <code>ComponentOrientation</code> property is horizontal, left-to-right. |
| * The layout will be adjusted appropriately for other orientations. |
| * <p> |
| * The <code>Container</code>, 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; |
| |
| /** |
| * <code>JOptionPane</code> 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 = java.security.AccessController.doPrivileged( |
| new GetPropertyAction("line.separator")); |
| 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 |
| * <code>JOptionPane</code>. |
| */ |
| 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 <code>c</code> is the <code>JOptionPane</code> the receiver |
| * is contained in, the preferred |
| * size that is returned is the maximum of the preferred size of |
| * the <code>LayoutManager</code> for the <code>JOptionPane</code>, and |
| * <code>getMinimumOptionPaneSize</code>. |
| */ |
| 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 |
| * <code>addIcon</code>. |
| */ |
| 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 <code>msg</code> and |
| * places it into <code>container</code>. If <code>msg</code> 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 <code>d</code> is an Object[], this |
| * method will be recursively invoked for the children. |
| * <code>internallyCreated</code> 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) { |
| 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 cBox = new JComboBox(); |
| |
| cBox.setName("OptionPane.comboBox"); |
| for(int counter = 0, maxCounter = sValues.length; |
| counter < maxCounter; counter++) { |
| cBox.addItem(sValues[counter]); |
| } |
| if (inputValue != null) { |
| cBox.setSelectedItem(inputValue); |
| } |
| inputComponent = cBox; |
| toAdd = cBox; |
| |
| } else { |
| JList list = new JList(sValues); |
| JScrollPane sp = new JScrollPane(list); |
| |
| sp.setName("OptionPane.scrollPane"); |
| list.setName("OptionPane.list"); |
| list.setVisibleRowCount(10); |
| list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); |
| if(inputValue != null) |
| list.setSelectedValue(inputValue, true); |
| list.addMouseListener(getHandler()); |
| toAdd = sp; |
| inputComponent = list; |
| } |
| |
| } else { |
| MultiplexingTextField tf = new MultiplexingTextField(20); |
| |
| tf.setName("OptionPane.textField"); |
| tf.setKeyStrokes(new KeyStroke[] { |
| KeyStroke.getKeyStroke("ENTER") } ); |
| if (inputValue != null) { |
| String inputString = inputValue.toString(); |
| tf.setText(inputString); |
| tf.setSelectionStart(0); |
| tf.setSelectionEnd(inputString.length()); |
| } |
| tf.addActionListener(getHandler()); |
| toAdd = inputComponent = tf; |
| } |
| |
| Object[] newMessage; |
| |
| if (message == null) { |
| newMessage = new Object[1]; |
| newMessage[0] = toAdd; |
| |
| } else { |
| newMessage = new Object[2]; |
| newMessage[0] = message; |
| newMessage[1] = toAdd; |
| } |
| return newMessage; |
| } |
| return optionPane.getMessage(); |
| } |
| return null; |
| } |
| |
| /** |
| * Creates and adds a JLabel representing the icon returned from |
| * <code>getIcon</code> to <code>top</code>. This is messaged from |
| * <code>createMessageArea</code> |
| */ |
| protected void addIcon(Container top) { |
| /* Create the icon. */ |
| Icon sideIcon = getIcon(); |
| |
| if (sideIcon != null) { |
| JLabel iconLabel = new JLabel(sideIcon); |
| |
| iconLabel.setName("OptionPane.iconLabel"); |
| iconLabel.setVerticalAlignment(SwingConstants.TOP); |
| top.add(iconLabel, BorderLayout.BEFORE_LINE_BEGINS); |
| } |
| } |
| |
| /** |
| * Returns the icon from the JOptionPane the receiver is providing |
| * the look and feel for, or the default icon as returned from |
| * <code>getDefaultIcon</code>. |
| */ |
| protected Icon getIcon() { |
| Icon mIcon = (optionPane == null ? null : optionPane.getIcon()); |
| |
| if(mIcon == null && optionPane != null) |
| mIcon = getIconForType(optionPane.getMessageType()); |
| return mIcon; |
| } |
| |
| /** |
| * Returns the icon to use for the passed in type. |
| */ |
| protected Icon getIconForType(int messageType) { |
| if(messageType < 0 || messageType > 3) |
| return null; |
| String propertyName = null; |
| switch(messageType) { |
| case 0: |
| propertyName = "OptionPane.errorIcon"; |
| break; |
| case 1: |
| propertyName = "OptionPane.informationIcon"; |
| break; |
| case 2: |
| propertyName = "OptionPane.warningIcon"; |
| break; |
| case 3: |
| propertyName = "OptionPane.questionIcon"; |
| break; |
| } |
| if (propertyName != null) { |
| return (Icon)DefaultLookup.get(optionPane, this, propertyName); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the maximum number of characters to place on a line. |
| */ |
| protected int getMaxCharactersPerLineCount() { |
| return optionPane.getMaxCharactersPerLineCount(); |
| } |
| |
| /** |
| * Recursively creates new JLabel instances to represent <code>d</code>. |
| * Each JLabel instance is added to <code>c</code>. |
| */ |
| protected void burstStringInto(Container c, String d, int maxll) { |
| // Primitive line wrapping |
| int len = d.length(); |
| if (len <= 0) |
| return; |
| if (len > maxll) { |
| int p = d.lastIndexOf(' ', maxll); |
| if (p <= 0) |
| p = d.indexOf(' ', maxll); |
| if (p > 0 && p < len) { |
| burstStringInto(c, d.substring(0, p), maxll); |
| burstStringInto(c, d.substring(p + 1), maxll); |
| return; |
| } |
| } |
| JLabel label = new JLabel(d, JLabel.LEFT); |
| label.setName("OptionPane.label"); |
| configureMessageLabel(label); |
| c.add(label); |
| } |
| |
| protected Container createSeparator() { |
| return null; |
| } |
| |
| /** |
| * Creates and returns a Container containing the buttons. The buttons |
| * are created by calling <code>getButtons</code>. |
| */ |
| protected Container createButtonArea() { |
| JPanel bottom = new JPanel(); |
| Border border = (Border)DefaultLookup.get(optionPane, this, |
| "OptionPane.buttonAreaBorder"); |
| bottom.setName("OptionPane.buttonArea"); |
| if (border != null) { |
| bottom.setBorder(border); |
| } |
| bottom.setLayout(new ButtonAreaLayout( |
| DefaultLookup.getBoolean(optionPane, this, |
| "OptionPane.sameSizeButtons", true), |
| DefaultLookup.getInt(optionPane, this, "OptionPane.buttonPadding", |
| 6), |
| DefaultLookup.getInt(optionPane, this, |
| "OptionPane.buttonOrientation", SwingConstants.CENTER), |
| DefaultLookup.getBoolean(optionPane, this, "OptionPane.isYesLast", |
| false))); |
| addButtonComponents(bottom, getButtons(), getInitialValueIndex()); |
| return bottom; |
| } |
| |
| /** |
| * Creates the appropriate object to represent each of the objects in |
| * <code>buttons</code> and adds it to <code>container</code>. This |
| * differs from addMessageComponents in that it will recurse on |
| * <code>buttons</code> and that if button is not a Component |
| * it will create an instance of JButton. |
| */ |
| protected void addButtonComponents(Container container, Object[] buttons, |
| int initialIndex) { |
| if (buttons != null && buttons.length > 0) { |
| boolean sizeButtonsToSame = getSizeButtonsToSameWidth(); |
| boolean createdAll = true; |
| int numButtons = buttons.length; |
| JButton[] createdButtons = null; |
| int maxWidth = 0; |
| |
| if (sizeButtonsToSame) { |
| createdButtons = new JButton[numButtons]; |
| } |
| |
| for(int counter = 0; counter < numButtons; counter++) { |
| Object button = buttons[counter]; |
| Component newComponent; |
| |
| if (button instanceof Component) { |
| createdAll = false; |
| newComponent = (Component)button; |
| container.add(newComponent); |
| hasCustomComponents = true; |
| |
| } else { |
| JButton aButton; |
| |
| if (button instanceof ButtonFactory) { |
| aButton = ((ButtonFactory)button).createButton(); |
| } |
| else if (button instanceof Icon) |
| aButton = new JButton((Icon)button); |
| else |
| aButton = new JButton(button.toString()); |
| |
| aButton.setName("OptionPane.button"); |
| aButton.setMultiClickThreshhold(DefaultLookup.getInt( |
| optionPane, this, "OptionPane.buttonClickThreshhold", |
| 0)); |
| configureButton(aButton); |
| |
| container.add(aButton); |
| |
| ActionListener buttonListener = createButtonActionListener(counter); |
| if (buttonListener != null) { |
| aButton.addActionListener(buttonListener); |
| } |
| newComponent = aButton; |
| } |
| if (sizeButtonsToSame && createdAll && |
| (newComponent instanceof JButton)) { |
| createdButtons[counter] = (JButton)newComponent; |
| maxWidth = Math.max(maxWidth, |
| newComponent.getMinimumSize().width); |
| } |
| if (counter == initialIndex) { |
| initialFocusComponent = newComponent; |
| if (initialFocusComponent instanceof JButton) { |
| JButton defaultB = (JButton)initialFocusComponent; |
| defaultB.addHierarchyListener(new HierarchyListener() { |
| public void hierarchyChanged(HierarchyEvent e) { |
| if ((e.getChangeFlags() & |
| HierarchyEvent.PARENT_CHANGED) != 0) { |
| JButton defaultButton = (JButton) e.getComponent(); |
| JRootPane root = |
| SwingUtilities.getRootPane(defaultButton); |
| if (root != null) { |
| root.setDefaultButton(defaultButton); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| ((ButtonAreaLayout)container.getLayout()). |
| setSyncAllWidths((sizeButtonsToSame && createdAll)); |
| /* Set the padding, windows seems to use 8 if <= 2 components, |
| otherwise 4 is used. It may actually just be the size of the |
| buttons is always the same, not sure. */ |
| if (DefaultLookup.getBoolean(optionPane, this, |
| "OptionPane.setButtonMargin", true) && sizeButtonsToSame && |
| createdAll) { |
| JButton aButton; |
| int padSize; |
| |
| padSize = (numButtons <= 2? 8 : 4); |
| |
| for(int counter = 0; counter < numButtons; counter++) { |
| aButton = createdButtons[counter]; |
| aButton.setMargin(new Insets(2, padSize, 2, padSize)); |
| } |
| } |
| } |
| } |
| |
| protected ActionListener createButtonActionListener(int buttonIndex) { |
| return new ButtonActionListener(buttonIndex); |
| } |
| |
| /** |
| * Returns the buttons to display from the JOptionPane the receiver is |
| * providing the look and feel for. If the JOptionPane has options |
| * set, they will be provided, otherwise if the optionType is |
| * YES_NO_OPTION, yesNoOptions is returned, if the type is |
| * YES_NO_CANCEL_OPTION yesNoCancelOptions is returned, otherwise |
| * defaultButtons are returned. |
| */ |
| protected Object[] getButtons() { |
| if (optionPane != null) { |
| Object[] suppliedOptions = optionPane.getOptions(); |
| |
| if (suppliedOptions == null) { |
| Object[] defaultOptions; |
| int type = optionPane.getOptionType(); |
| Locale l = optionPane.getLocale(); |
| int minimumWidth = |
| DefaultLookup.getInt(optionPane, this, |
| "OptionPane.buttonMinimumWidth",-1); |
| if (type == JOptionPane.YES_NO_OPTION) { |
| defaultOptions = new ButtonFactory[2]; |
| defaultOptions[0] = new ButtonFactory( |
| UIManager.getString("OptionPane.yesButtonText", l), |
| getMnemonic("OptionPane.yesButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.yesIcon"), minimumWidth); |
| defaultOptions[1] = new ButtonFactory( |
| UIManager.getString("OptionPane.noButtonText", l), |
| getMnemonic("OptionPane.noButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.noIcon"), minimumWidth); |
| } else if (type == JOptionPane.YES_NO_CANCEL_OPTION) { |
| defaultOptions = new ButtonFactory[3]; |
| defaultOptions[0] = new ButtonFactory( |
| UIManager.getString("OptionPane.yesButtonText", l), |
| getMnemonic("OptionPane.yesButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.yesIcon"), minimumWidth); |
| defaultOptions[1] = new ButtonFactory( |
| UIManager.getString("OptionPane.noButtonText",l), |
| getMnemonic("OptionPane.noButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.noIcon"), minimumWidth); |
| defaultOptions[2] = new ButtonFactory( |
| UIManager.getString("OptionPane.cancelButtonText",l), |
| getMnemonic("OptionPane.cancelButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.cancelIcon"), minimumWidth); |
| } else if (type == JOptionPane.OK_CANCEL_OPTION) { |
| defaultOptions = new ButtonFactory[2]; |
| defaultOptions[0] = new ButtonFactory( |
| UIManager.getString("OptionPane.okButtonText",l), |
| getMnemonic("OptionPane.okButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.okIcon"), minimumWidth); |
| defaultOptions[1] = new ButtonFactory( |
| UIManager.getString("OptionPane.cancelButtonText",l), |
| getMnemonic("OptionPane.cancelButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.cancelIcon"), minimumWidth); |
| } else { |
| defaultOptions = new ButtonFactory[1]; |
| defaultOptions[0] = new ButtonFactory( |
| UIManager.getString("OptionPane.okButtonText",l), |
| getMnemonic("OptionPane.okButtonMnemonic", l), |
| (Icon)DefaultLookup.get(optionPane, this, |
| "OptionPane.okIcon"), minimumWidth); |
| } |
| return defaultOptions; |
| |
| } |
| return suppliedOptions; |
| } |
| return null; |
| } |
| |
| private int getMnemonic(String key, Locale l) { |
| String value = (String)UIManager.get(key, l); |
| |
| if (value == null) { |
| return 0; |
| } |
| try { |
| return Integer.parseInt(value); |
| } |
| catch (NumberFormatException nfe) { } |
| return 0; |
| } |
| |
| /** |
| * Returns true, basic L&F wants all the buttons to have the same |
| * width. |
| */ |
| protected boolean getSizeButtonsToSameWidth() { |
| return true; |
| } |
| |
| /** |
| * Returns the initial index into the buttons to select. The index |
| * is calculated from the initial value from the JOptionPane and |
| * options of the JOptionPane or 0. |
| */ |
| protected int getInitialValueIndex() { |
| if (optionPane != null) { |
| Object iv = optionPane.getInitialValue(); |
| Object[] options = optionPane.getOptions(); |
| |
| if(options == null) { |
| return 0; |
| } |
| else if(iv != null) { |
| for(int counter = options.length - 1; counter >= 0; counter--){ |
| if(options[counter].equals(iv)) |
| return counter; |
| } |
| } |
| } |
| return -1; |
| } |
| |
| /** |
| * Sets the input value in the option pane the receiver is providing |
| * the look and feel for based on the value in the inputComponent. |
| */ |
| protected void resetInputValue() { |
| if(inputComponent != null && (inputComponent instanceof JTextField)) { |
| optionPane.setInputValue(((JTextField)inputComponent).getText()); |
| |
| } else if(inputComponent != null && |
| (inputComponent instanceof JComboBox)) { |
| optionPane.setInputValue(((JComboBox)inputComponent) |
| .getSelectedItem()); |
| } else if(inputComponent != null) { |
| optionPane.setInputValue(((JList)inputComponent) |
| .getSelectedValue()); |
| } |
| } |
| |
| |
| /** |
| * If inputComponent is non-null, the focus is requested on that, |
| * otherwise request focus on the default value |
| */ |
| public void selectInitialValue(JOptionPane op) { |
| if (inputComponent != null) |
| inputComponent.requestFocus(); |
| else { |
| if (initialFocusComponent != null) |
| initialFocusComponent.requestFocus(); |
| |
| if (initialFocusComponent instanceof JButton) { |
| JRootPane root = SwingUtilities.getRootPane(initialFocusComponent); |
| if (root != null) { |
| root.setDefaultButton((JButton)initialFocusComponent); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns true if in the last call to validateComponent the message |
| * or buttons contained a subclass of Component. |
| */ |
| public boolean containsCustomComponents(JOptionPane op) { |
| return hasCustomComponents; |
| } |
| |
| |
| /** |
| * <code>ButtonAreaLayout</code> behaves in a similar manner to |
| * <code>FlowLayout</code>. It lays out all components from left to |
| * right. If <code>syncAllWidths</code> is true, the widths of each |
| * component will be set to the largest preferred size width. |
| * |
| * This class should be treated as a "protected" inner class. |
| * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. |
| */ |
| public static class ButtonAreaLayout implements LayoutManager { |
| protected boolean syncAllWidths; |
| protected int padding; |
| /** If true, children are lumped together in parent. */ |
| protected boolean centersChildren; |
| private int orientation; |
| private boolean reverseButtons; |
| /** |
| * Indicates whether or not centersChildren should be used vs |
| * the orientation. This is done for backward compatability |
| * for subclassers. |
| */ |
| private boolean useOrientation; |
| |
| public ButtonAreaLayout(boolean syncAllWidths, int padding) { |
| this.syncAllWidths = syncAllWidths; |
| this.padding = padding; |
| centersChildren = true; |
| useOrientation = false; |
| } |
| |
| ButtonAreaLayout(boolean syncAllSizes, int padding, int orientation, |
| boolean reverseButtons) { |
| this(syncAllSizes, padding); |
| useOrientation = true; |
| this.orientation = orientation; |
| this.reverseButtons = reverseButtons; |
| } |
| |
| public void setSyncAllWidths(boolean newValue) { |
| syncAllWidths = newValue; |
| } |
| |
| public boolean getSyncAllWidths() { |
| return syncAllWidths; |
| } |
| |
| public void setPadding(int newPadding) { |
| this.padding = newPadding; |
| } |
| |
| public int getPadding() { |
| return padding; |
| } |
| |
| public void setCentersChildren(boolean newValue) { |
| centersChildren = newValue; |
| useOrientation = false; |
| } |
| |
| public boolean getCentersChildren() { |
| return centersChildren; |
| } |
| |
| private int getOrientation(Container container) { |
| if (!useOrientation) { |
| return SwingConstants.CENTER; |
| } |
| if (container.getComponentOrientation().isLeftToRight()) { |
| return orientation; |
| } |
| switch (orientation) { |
| case SwingConstants.LEFT: |
| return SwingConstants.RIGHT; |
| case SwingConstants.RIGHT: |
| return SwingConstants.LEFT; |
| case SwingConstants.CENTER: |
| return SwingConstants.CENTER; |
| } |
| return SwingConstants.LEFT; |
| } |
| |
| public void addLayoutComponent(String string, Component comp) { |
| } |
| |
| public void layoutContainer(Container container) { |
| Component[] children = container.getComponents(); |
| |
| if(children != null && children.length > 0) { |
| int numChildren = children.length; |
| Insets insets = container.getInsets(); |
| int maxWidth = 0; |
| int maxHeight = 0; |
| int totalButtonWidth = 0; |
| int x = 0; |
| int xOffset = 0; |
| boolean ltr = container.getComponentOrientation(). |
| isLeftToRight(); |
| boolean reverse = (ltr) ? reverseButtons : !reverseButtons; |
| |
| for(int counter = 0; counter < numChildren; counter++) { |
| Dimension pref = children[counter].getPreferredSize(); |
| maxWidth = Math.max(maxWidth, pref.width); |
| maxHeight = Math.max(maxHeight, pref.height); |
| totalButtonWidth += pref.width; |
| } |
| if (getSyncAllWidths()) { |
| totalButtonWidth = maxWidth * numChildren; |
| } |
| totalButtonWidth += (numChildren - 1) * padding; |
| |
| switch (getOrientation(container)) { |
| case SwingConstants.LEFT: |
| x = insets.left; |
| break; |
| case SwingConstants.RIGHT: |
| x = container.getWidth() - insets.right - totalButtonWidth; |
| break; |
| case SwingConstants.CENTER: |
| if (getCentersChildren() || numChildren < 2) { |
| x = (container.getWidth() - totalButtonWidth) / 2; |
| } |
| else { |
| x = insets.left; |
| if (getSyncAllWidths()) { |
| xOffset = (container.getWidth() - insets.left - |
| insets.right - totalButtonWidth) / |
| (numChildren - 1) + maxWidth; |
| } |
| else { |
| xOffset = (container.getWidth() - insets.left - |
| insets.right - totalButtonWidth) / |
| (numChildren - 1); |
| } |
| } |
| break; |
| } |
| |
| for (int counter = 0; counter < numChildren; counter++) { |
| int index = (reverse) ? numChildren - counter - 1 : |
| counter; |
| Dimension pref = children[index].getPreferredSize(); |
| |
| if (getSyncAllWidths()) { |
| children[index].setBounds(x, insets.top, |
| maxWidth, maxHeight); |
| } |
| else { |
| children[index].setBounds(x, insets.top, pref.width, |
| pref.height); |
| } |
| if (xOffset != 0) { |
| x += xOffset; |
| } |
| else { |
| x += children[index].getWidth() + padding; |
| } |
| } |
| } |
| } |
| |
| public Dimension minimumLayoutSize(Container c) { |
| if(c != null) { |
| Component[] children = c.getComponents(); |
| |
| if(children != null && children.length > 0) { |
| Dimension aSize; |
| int numChildren = children.length; |
| int height = 0; |
| Insets cInsets = c.getInsets(); |
| int extraHeight = cInsets.top + cInsets.bottom; |
| int extraWidth = cInsets.left + cInsets.right; |
| |
| if (syncAllWidths) { |
| int maxWidth = 0; |
| |
| for(int counter = 0; counter < numChildren; counter++){ |
| aSize = children[counter].getPreferredSize(); |
| height = Math.max(height, aSize.height); |
| maxWidth = Math.max(maxWidth, aSize.width); |
| } |
| return new Dimension(extraWidth + (maxWidth * numChildren) + |
| (numChildren - 1) * padding, |
| extraHeight + height); |
| } |
| else { |
| int totalWidth = 0; |
| |
| for(int counter = 0; counter < numChildren; counter++){ |
| aSize = children[counter].getPreferredSize(); |
| height = Math.max(height, aSize.height); |
| totalWidth += aSize.width; |
| } |
| totalWidth += ((numChildren - 1) * padding); |
| return new Dimension(extraWidth + totalWidth, extraHeight + height); |
| } |
| } |
| } |
| return new Dimension(0, 0); |
| } |
| |
| public Dimension preferredLayoutSize(Container c) { |
| return minimumLayoutSize(c); |
| } |
| |
| public void removeLayoutComponent(Component c) { } |
| } |
| |
| |
| /** |
| * This class should be treated as a "protected" inner class. |
| * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. |
| */ |
| public class PropertyChangeHandler implements PropertyChangeListener { |
| /** |
| * If the source of the PropertyChangeEvent <code>e</code> equals the |
| * optionPane and is one of the ICON_PROPERTY, MESSAGE_PROPERTY, |
| * OPTIONS_PROPERTY or INITIAL_VALUE_PROPERTY, |
| * validateComponent is invoked. |
| */ |
| public void propertyChange(PropertyChangeEvent e) { |
| getHandler().propertyChange(e); |
| } |
| } |
| |
| /** |
| * Configures any necessary colors/fonts for the specified label |
| * used representing the message. |
| */ |
| private void configureMessageLabel(JLabel label) { |
| Color color = (Color)DefaultLookup.get(optionPane, this, |
| "OptionPane.messageForeground"); |
| if (color != null) { |
| label.setForeground(color); |
| } |
| Font messageFont = (Font)DefaultLookup.get(optionPane, this, |
| "OptionPane.messageFont"); |
| if (messageFont != null) { |
| label.setFont(messageFont); |
| } |
| } |
| |
| /** |
| * Configures any necessary colors/fonts for the specified button |
| * used representing the button portion of the optionpane. |
| */ |
| private void configureButton(JButton button) { |
| Font buttonFont = (Font)DefaultLookup.get(optionPane, this, |
| "OptionPane.buttonFont"); |
| if (buttonFont != null) { |
| button.setFont(buttonFont); |
| } |
| } |
| |
| /** |
| * This class should be treated as a "protected" inner class. |
| * Instantiate it only within subclasses of {@code BasicOptionPaneUI}. |
| */ |
| public class ButtonActionListener implements ActionListener { |
| protected int buttonIndex; |
| |
| public ButtonActionListener(int buttonIndex) { |
| this.buttonIndex = buttonIndex; |
| } |
| |
| public void actionPerformed(ActionEvent e) { |
| if (optionPane != null) { |
| int optionType = optionPane.getOptionType(); |
| Object[] options = optionPane.getOptions(); |
| |
| /* If the option pane takes input, then store the input value |
| * if custom options were specified, if the option type is |
| * DEFAULT_OPTION, OR if option type is set to a predefined |
| * one and the user chose the affirmative answer. |
| */ |
| if (inputComponent != null) { |
| if (options != null || |
| optionType == JOptionPane.DEFAULT_OPTION || |
| ((optionType == JOptionPane.YES_NO_OPTION || |
| optionType == JOptionPane.YES_NO_CANCEL_OPTION || |
| optionType == JOptionPane.OK_CANCEL_OPTION) && |
| buttonIndex == 0)) { |
| resetInputValue(); |
| } |
| } |
| if (options == null) { |
| if (optionType == JOptionPane.OK_CANCEL_OPTION && |
| buttonIndex == 1) { |
| optionPane.setValue(Integer.valueOf(2)); |
| |
| } else { |
| optionPane.setValue(Integer.valueOf(buttonIndex)); |
| } |
| } else { |
| optionPane.setValue(options[buttonIndex]); |
| } |
| } |
| } |
| } |
| |
| |
| private class Handler implements ActionListener, MouseListener, |
| PropertyChangeListener { |
| // |
| // ActionListener |
| // |
| public void actionPerformed(ActionEvent e) { |
| optionPane.setInputValue(((JTextField)e.getSource()).getText()); |
| } |
| |
| |
| // |
| // MouseListener |
| // |
| public void mouseClicked(MouseEvent e) { |
| } |
| |
| public void mouseReleased(MouseEvent e) { |
| } |
| |
| public void mouseEntered(MouseEvent e) { |
| } |
| |
| public void mouseExited(MouseEvent e) { |
| } |
| |
| public void mousePressed(MouseEvent e) { |
| if (e.getClickCount() == 2) { |
| JList list = (JList)e.getSource(); |
| int index = list.locationToIndex(e.getPoint()); |
| |
| optionPane.setInputValue(list.getModel().getElementAt(index)); |
| optionPane.setValue(JOptionPane.OK_OPTION); |
| } |
| } |
| |
| // |
| // PropertyChangeListener |
| // |
| public void propertyChange(PropertyChangeEvent e) { |
| if(e.getSource() == optionPane) { |
| // Option Pane Auditory Cue Activation |
| // only respond to "ancestor" changes |
| // the idea being that a JOptionPane gets a JDialog when it is |
| // set to appear and loses it's JDialog when it is dismissed. |
| if ("ancestor" == e.getPropertyName()) { |
| JOptionPane op = (JOptionPane)e.getSource(); |
| boolean isComingUp; |
| |
| // if the old value is null, then the JOptionPane is being |
| // created since it didn't previously have an ancestor. |
| if (e.getOldValue() == null) { |
| isComingUp = true; |
| } else { |
| isComingUp = false; |
| } |
| |
| // figure out what to do based on the message type |
| switch (op.getMessageType()) { |
| case JOptionPane.PLAIN_MESSAGE: |
| if (isComingUp) { |
| BasicLookAndFeel.playSound(optionPane, |
| "OptionPane.informationSound"); |
| } |
| break; |
| case JOptionPane.QUESTION_MESSAGE: |
| if (isComingUp) { |
| BasicLookAndFeel.playSound(optionPane, |
| "OptionPane.questionSound"); |
| } |
| break; |
| case JOptionPane.INFORMATION_MESSAGE: |
| if (isComingUp) { |
| BasicLookAndFeel.playSound(optionPane, |
| "OptionPane.informationSound"); |
| } |
| break; |
| case JOptionPane.WARNING_MESSAGE: |
| if (isComingUp) { |
| BasicLookAndFeel.playSound(optionPane, |
| "OptionPane.warningSound"); |
| } |
| break; |
| case JOptionPane.ERROR_MESSAGE: |
| if (isComingUp) { |
| BasicLookAndFeel.playSound(optionPane, |
| "OptionPane.errorSound"); |
| } |
| break; |
| default: |
| System.err.println("Undefined JOptionPane type: " + |
| op.getMessageType()); |
| break; |
| } |
| } |
| // Visual activity |
| String changeName = e.getPropertyName(); |
| |
| if(changeName == JOptionPane.OPTIONS_PROPERTY || |
| changeName == JOptionPane.INITIAL_VALUE_PROPERTY || |
| changeName == JOptionPane.ICON_PROPERTY || |
| changeName == JOptionPane.MESSAGE_TYPE_PROPERTY || |
| changeName == JOptionPane.OPTION_TYPE_PROPERTY || |
| changeName == JOptionPane.MESSAGE_PROPERTY || |
| changeName == JOptionPane.SELECTION_VALUES_PROPERTY || |
| changeName == JOptionPane.INITIAL_SELECTION_VALUE_PROPERTY || |
| changeName == JOptionPane.WANTS_INPUT_PROPERTY) { |
| uninstallComponents(); |
| installComponents(); |
| optionPane.validate(); |
| } |
| else if (changeName == "componentOrientation") { |
| ComponentOrientation o = (ComponentOrientation)e.getNewValue(); |
| JOptionPane op = (JOptionPane)e.getSource(); |
| if (o != e.getOldValue()) { |
| op.applyComponentOrientation(o); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| // |
| // Classes used when optionPane.getWantsInput returns true. |
| // |
| |
| /** |
| * A JTextField that allows you to specify an array of KeyStrokes that |
| * that will have their bindings processed regardless of whether or |
| * not they are registered on the JTextField. This is used as we really |
| * want the ActionListener to be notified so that we can push the |
| * change to the JOptionPane, but we also want additional bindings |
| * (those of the JRootPane) to be processed as well. |
| */ |
| private static class MultiplexingTextField extends JTextField { |
| private KeyStroke[] strokes; |
| |
| MultiplexingTextField(int cols) { |
| super(cols); |
| } |
| |
| /** |
| * Sets the KeyStrokes that will be additional processed for |
| * ancestor bindings. |
| */ |
| void setKeyStrokes(KeyStroke[] strokes) { |
| this.strokes = strokes; |
| } |
| |
| protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, |
| int condition, boolean pressed) { |
| boolean processed = super.processKeyBinding(ks, e, condition, |
| pressed); |
| |
| if (processed && condition != JComponent.WHEN_IN_FOCUSED_WINDOW) { |
| for (int counter = strokes.length - 1; counter >= 0; |
| counter--) { |
| if (strokes[counter].equals(ks)) { |
| // Returning false will allow further processing |
| // of the bindings, eg our parent Containers will get a |
| // crack at them. |
| return false; |
| } |
| } |
| } |
| return processed; |
| } |
| } |
| |
| |
| |
| /** |
| * Registered in the ActionMap. Sets the value of the option pane |
| * to <code>JOptionPane.CLOSED_OPTION</code>. |
| */ |
| private static class Actions extends UIAction { |
| private static final String CLOSE = "close"; |
| |
| Actions(String key) { |
| super(key); |
| } |
| |
| public void actionPerformed(ActionEvent e) { |
| if (getName() == CLOSE) { |
| JOptionPane optionPane = (JOptionPane)e.getSource(); |
| |
| optionPane.setValue(Integer.valueOf(JOptionPane.CLOSED_OPTION)); |
| } |
| } |
| } |
| |
| |
| /** |
| * This class is used to create the default buttons. This indirection is |
| * used so that addButtonComponents can tell which Buttons were created |
| * by us vs subclassers or from the JOptionPane itself. |
| */ |
| private static class ButtonFactory { |
| private String text; |
| private int mnemonic; |
| private Icon icon; |
| private int minimumWidth = -1; |
| |
| ButtonFactory(String text, int mnemonic, Icon icon, int minimumWidth) { |
| this.text = text; |
| this.mnemonic = mnemonic; |
| this.icon = icon; |
| this.minimumWidth = minimumWidth; |
| } |
| |
| JButton createButton() { |
| JButton button; |
| |
| if (minimumWidth > 0) { |
| button = new ConstrainedButton(text, minimumWidth); |
| } else { |
| button = new JButton(text); |
| } |
| if (icon != null) { |
| button.setIcon(icon); |
| } |
| if (mnemonic != 0) { |
| button.setMnemonic(mnemonic); |
| } |
| return button; |
| } |
| |
| private static class ConstrainedButton extends JButton { |
| int minimumWidth; |
| |
| ConstrainedButton(String text, int minimumWidth) { |
| super(text); |
| this.minimumWidth = minimumWidth; |
| } |
| |
| public Dimension getMinimumSize() { |
| Dimension min = super.getMinimumSize(); |
| min.width = Math.max(min.width, minimumWidth); |
| return min; |
| } |
| |
| public Dimension getPreferredSize() { |
| Dimension pref = super.getPreferredSize(); |
| pref.width = Math.max(pref.width, minimumWidth); |
| return pref; |
| } |
| } |
| } |
| } |