I implemented the one keyboard action for the JOptionPane (which is
closing the dialog on ESC) and fixed some smaller issues a came upon. I
also fixed two mistakes I made while copy+pasting the getActionMap()
method from BasicTreeUI to BasicTableUI and BasicToolBarUI.
2006-07-26 Roman Kennke <[EMAIL PROTECTED]>
* javax/swing/JOptionPane.java
(createDialog): Add property change handler for closing
the dialog when the value property changes.
(ValuePropertyHandler): New inner helper class.
* javax/swing/plaf/basic/BasicOptionPaneUI.java
(OptionPaneCloseAction): New class.
(messageForeground): Removed field.
(messageBorder): Removed field.
(buttonBorder): Removed field.
(addIcon): Configure the new label.
(addMessageComponents): Configure newly created labels.
(burstStringInto): Likewise.
(createButtonArea): Install border here.
(createMessageArea): Install border and foreground here.
(createSeparator): Added comment and removed
NotImplementedException.
(installComponents): Don't install the UI defaults for the
message and button area here. This is moved to the
corresponding create* methods. Adjusted comment about
separator.
(installDefaults): Removed initialization of removed fields.
(installKeyboardActions): Implemented.
(getActionMap): New helper method.
(createDefaultActions): New helper method.
(uninstallDefaults): Removed de-initialization of removed fields.
(uninstallKeyboardActions): Implemented.
(configureLabel): New helper method.
* javax/swing/plaf/basic/BasicTableUI.java
(getActionMap): Fixed the UI property names.
* javax/swing/plaf/basic/BasicToolBarUI.java
(getActionMap): Fixed the UI property names.
/Roman
Index: javax/swing/JOptionPane.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JOptionPane.java,v
retrieving revision 1.30
diff -u -1 -2 -r1.30 JOptionPane.java
--- javax/swing/JOptionPane.java 16 May 2006 11:49:23 -0000 1.30
+++ javax/swing/JOptionPane.java 26 Jul 2006 13:32:12 -0000
@@ -39,24 +39,26 @@
package javax.swing;
import java.awt.AWTEvent;
import java.awt.ActiveEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.MenuComponent;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionAdapter;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.swing.plaf.OptionPaneUI;
/**
* This class creates different types of JDialogs and JInternalFrames that can
* ask users for input or pass on information. JOptionPane can be used by
* calling one of the show static methods or by creating an instance of
* JOptionPane and calling createDialog or createInternalFrame.
*/
@@ -369,29 +371,67 @@
if (toUse == null)
toUse = getRootFrame();
JDialog dialog = new JDialog(toUse, title);
inputValue = UNINITIALIZED_VALUE;
value = UNINITIALIZED_VALUE;
dialog.getContentPane().add(this);
dialog.setModal(true);
dialog.setResizable(false);
dialog.pack();
dialog.setLocationRelativeTo(parentComponent);
-
+
+ addPropertyChangeListener(new ValuePropertyHandler(dialog));
return dialog;
}
/**
+ * Handles changes of the value property. Whenever this property changes,
+ * the JOptionPane dialog should be closed.
+ */
+ private static class ValuePropertyHandler
+ implements PropertyChangeListener
+ {
+ /**
+ * The dialog to close.
+ */
+ JDialog dialog;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param d the dialog to be closed
+ */
+ ValuePropertyHandler(JDialog d)
+ {
+ dialog = d;
+ }
+
+ /**
+ * Receives notification when any of the properties change.
+ */
+ public void propertyChange(PropertyChangeEvent p)
+ {
+ String prop = p.getPropertyName();
+ Object val = p.getNewValue();
+ if (prop.equals(VALUE_PROPERTY) && val != null
+ && val != UNINITIALIZED_VALUE)
+ {
+ dialog.setVisible(false);
+ }
+ }
+ }
+
+ /**
* This method creates a new JInternalFrame that is in the JLayeredPane
* which contains the parentComponent given. If no suitable JLayeredPane
* can be found from the parentComponent given, a RuntimeException will be
* thrown.
*
* @param parentComponent The parent to find a JDesktopPane from.
* @param title The title of the JInternalFrame.
*
* @return A new JInternalFrame based on the JOptionPane configuration.
*
* @throws RuntimeException If no suitable JDesktopPane is found.
*
Index: javax/swing/plaf/basic/BasicOptionPaneUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicOptionPaneUI.java,v
retrieving revision 1.32
diff -u -1 -2 -r1.32 BasicOptionPaneUI.java
--- javax/swing/plaf/basic/BasicOptionPaneUI.java 4 Jul 2006 18:00:48 -0000 1.32
+++ javax/swing/plaf/basic/BasicOptionPaneUI.java 26 Jul 2006 13:32:13 -0000
@@ -29,71 +29,90 @@
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module. An independent module is a module which is not derived from
or based on this library. If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so. If you do not wish to do so, delete this
exception statement from your version. */
package javax.swing.plaf.basic;
-import gnu.classpath.NotImplementedException;
-
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
+import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Polygon;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.Icon;
+import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.LookAndFeel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.Border;
+import javax.swing.plaf.ActionMapUIResource;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.OptionPaneUI;
/**
* This class is the UI delegate for JOptionPane in the Basic Look and Feel.
*/
public class BasicOptionPaneUI extends OptionPaneUI
{
/**
+ * Implements the "close" keyboard action.
+ */
+ static class OptionPaneCloseAction
+ extends AbstractAction
+ {
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JOptionPane op = (JOptionPane) event.getSource();
+ op.setValue(new Integer(JOptionPane.CLOSED_OPTION));
+ }
+
+ }
+
+ /**
* This is a helper class that listens to the buttons located at the bottom
* of the JOptionPane.
*
* @specnote Apparently this class was intended to be protected,
* but was made public by a compiler bug and is now
* public for compatibility.
*/
public class ButtonActionListener implements ActionListener
{
/** The index of the option this button represents. */
protected int buttonIndex;
@@ -453,33 +472,24 @@
/** The minimum dimensions of the JOptionPane. */
protected Dimension minimumSize;
/** The propertyChangeListener for the JOptionPane. */
protected PropertyChangeListener propertyChangeListener;
/** The JOptionPane this UI delegate is used for. */
protected JOptionPane optionPane;
/** The size of the icons. */
private static final int ICON_SIZE = 36;
- /** The foreground color for the message area. */
- private transient Color messageForeground;
-
- /** The border around the message area. */
- private transient Border messageBorder;
-
- /** The border around the button area. */
- private transient Border buttonBorder;
-
/** The string used to describe OK buttons. */
private static final String OK_STRING = "OK";
/** The string used to describe Yes buttons. */
private static final String YES_STRING = "Yes";
/** The string used to describe No buttons. */
private static final String NO_STRING = "No";
/** The string used to describe Cancel buttons. */
private static final String CANCEL_STRING = "Cancel";
@@ -691,24 +701,25 @@
/**
* This method adds the appropriate icon the given container.
*
* @param top The container to add an icon to.
*/
protected void addIcon(Container top)
{
JLabel iconLabel = null;
Icon icon = getIcon();
if (icon != null)
{
iconLabel = new JLabel(icon);
+ configureLabel(iconLabel);
top.add(iconLabel, BorderLayout.WEST);
}
}
/**
* A helper method that returns an instance of GridBagConstraints to be used
* for creating the message area.
*
* @return An instance of GridBagConstraints.
*/
private static GridBagConstraints createConstraints()
{
@@ -752,43 +763,48 @@
for (int i = 0; i < arr.length; i++)
addMessageComponents(container, cons, arr[i], maxll,
internallyCreated);
return;
}
else if (msg instanceof Component)
{
container.add((Component) msg, cons);
cons.gridy++;
}
else if (msg instanceof Icon)
{
- container.add(new JLabel((Icon) msg), cons);
+ JLabel label = new JLabel((Icon) msg);
+ configureLabel(label);
+ container.add(label, cons);
cons.gridy++;
}
else
{
// Undocumented behaviour.
// if msg.toString().length greater than maxll
// it will create a box and burst the string.
// otherwise, it will just create a label and re-call
// this method with the label o.O
if (msg.toString().length() > maxll || msg.toString().contains("\n"))
{
Box tmp = new Box(BoxLayout.Y_AXIS);
burstStringInto(tmp, msg.toString(), maxll);
addMessageComponents(container, cons, tmp, maxll, true);
}
else
- addMessageComponents(container, cons, new JLabel(msg.toString()),
- maxll, true);
+ {
+ JLabel label = new JLabel(msg.toString());
+ configureLabel(label);
+ addMessageComponents(container, cons, label, maxll, true);
+ }
}
}
/**
* This method creates instances of d (recursively if necessary based on
* maxll) and adds to c.
*
* @param c The container to add to.
* @param d The string to burst.
* @param maxll The max line length.
*/
protected void burstStringInto(Container c, String d, int maxll)
@@ -801,36 +817,41 @@
String remainder;
if (newlineIndex >= 0 && newlineIndex < maxll)
{
line = d.substring(0, newlineIndex);
remainder = d.substring(newlineIndex + 1);
}
else
{
line = d.substring(0, maxll);
remainder = d.substring(maxll);
}
JLabel label = new JLabel(line);
+ configureLabel(label);
c.add(label);
// If there is nothing left to burst, then we can stop.
if (remainder.length() == 0)
return;
// Recursively call ourselves to burst the remainder of the string,
if (remainder.length() > maxll || remainder.contains("\n"))
burstStringInto(c, remainder, maxll);
else
- // Add the remainder to the container and be done.
- c.add(new JLabel(remainder));
+ {
+ // Add the remainder to the container and be done.
+ JLabel l = new JLabel(remainder);
+ configureLabel(l);
+ c.add(l);
+ }
}
/**
* This method returns true if the given JOptionPane contains custom
* components.
*
* @param op The JOptionPane to check.
*
* @return True if the JOptionPane contains custom components.
*/
public boolean containsCustomComponents(JOptionPane op)
{
@@ -848,24 +869,27 @@
{
return new ButtonActionListener(buttonIndex);
}
/**
* This method creates the button area.
*
* @return A new Button Area.
*/
protected Container createButtonArea()
{
JPanel buttonPanel = new JPanel();
+ Border b = UIManager.getBorder("OptionPane.buttonAreaBorder");
+ if (b != null)
+ buttonPanel.setBorder(b);
buttonPanel.setLayout(createLayoutManager());
addButtonComponents(buttonPanel, getButtons(), getInitialValueIndex());
return buttonPanel;
}
/**
* This method creates a new LayoutManager for the button area.
*
* @return A new LayoutManager for the button area.
*/
@@ -873,24 +897,28 @@
{
return new ButtonAreaLayout(getSizeButtonsToSameWidth(), 6);
}
/**
* This method creates the message area.
*
* @return A new message area.
*/
protected Container createMessageArea()
{
JPanel messageArea = new JPanel();
+ Border messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder");
+ if (messageBorder != null)
+ messageArea.setBorder(messageBorder);
+
messageArea.setLayout(new BorderLayout());
addIcon(messageArea);
JPanel rightSide = new JPanel();
rightSide.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
rightSide.setLayout(new GridBagLayout());
GridBagConstraints con = createConstraints();
addMessageComponents(rightSide, con, getMessage(),
getMaxCharactersPerLineCount(), false);
if (optionPane.getWantsInput())
@@ -926,28 +954,28 @@
protected PropertyChangeListener createPropertyChangeListener()
{
return new PropertyChangeHandler();
}
/**
* This method creates a Container that will separate the message and button
* areas.
*
* @return A Container that will separate the message and button areas.
*/
protected Container createSeparator()
- throws NotImplementedException
{
- // FIXME: Figure out what this method is supposed to return and where
- // this should be added to the OptionPane.
+ // The reference implementation returns null here. When overriding
+ // to return something non-null, the component gets added between
+ // the message area and the button area. See installComponents().
return null;
}
/**
* This method creates a new BasicOptionPaneUI for the given component.
*
* @param x The component to create a UI for.
*
* @return A new BasicOptionPaneUI.
*/
public static ComponentUI createUI(JComponent x)
{
@@ -1130,90 +1158,103 @@
* @return Whether all buttons should have the same width.
*/
protected boolean getSizeButtonsToSameWidth()
{
return true;
}
/**
* This method installs components for the JOptionPane.
*/
protected void installComponents()
{
- // reset it.
- hasCustomComponents = false;
- Container msg = createMessageArea();
- if (msg != null)
- {
- ((JComponent) msg).setBorder(messageBorder);
- msg.setForeground(messageForeground);
- messageAreaContainer = msg;
- optionPane.add(msg);
- }
+ // First thing is the message area.
+ optionPane.add(createMessageArea());
- // FIXME: Figure out if the separator should be inserted here or what
- // this thing is supposed to do. Note: The JDK does NOT insert another
- // component at this place. The JOptionPane only has two panels in it
- // and there actually are applications that depend on this beeing so.
+ // Add separator when createSeparator() is overridden to return
+ // something other than null.
Container sep = createSeparator();
if (sep != null)
optionPane.add(sep);
- Container button = createButtonArea();
- if (button != null)
- {
- ((JComponent) button).setBorder(buttonBorder);
- buttonContainer = button;
- optionPane.add(button);
- }
-
- optionPane.setBorder(BorderFactory.createEmptyBorder(12, 12, 11, 11));
- optionPane.invalidate();
+ // Last thing is the button area.
+ optionPane.add(createButtonArea());
}
/**
* This method installs defaults for the JOptionPane.
*/
protected void installDefaults()
{
LookAndFeel.installColorsAndFont(optionPane, "OptionPane.background",
"OptionPane.foreground",
"OptionPane.font");
LookAndFeel.installBorder(optionPane, "OptionPane.border");
optionPane.setOpaque(true);
- messageBorder = UIManager.getBorder("OptionPane.messageAreaBorder");
- messageForeground = UIManager.getColor("OptionPane.messageForeground");
- buttonBorder = UIManager.getBorder("OptionPane.buttonAreaBorder");
-
minimumSize = UIManager.getDimension("OptionPane.minimumSize");
// FIXME: Image icons don't seem to work properly right now.
// Once they do, replace the synthetic icons with these ones.
/*
warningIcon = (IconUIResource) defaults.getIcon("OptionPane.warningIcon");
infoIcon = (IconUIResource) defaults.getIcon("OptionPane.informationIcon");
errorIcon = (IconUIResource) defaults.getIcon("OptionPane.errorIcon");
questionIcon = (IconUIResource) defaults.getIcon("OptionPane.questionIcon");
*/
}
/**
* This method installs keyboard actions for the JOptionpane.
*/
protected void installKeyboardActions()
- throws NotImplementedException
{
- // FIXME: implement.
+ // Install the input map.
+ Object[] bindings =
+ (Object[]) SharedUIDefaults.get("OptionPane.windowBindings");
+ InputMap inputMap = LookAndFeel.makeComponentInputMap(optionPane,
+ bindings);
+ SwingUtilities.replaceUIInputMap(optionPane,
+ JComponent.WHEN_IN_FOCUSED_WINDOW,
+ inputMap);
+
+ // FIXME: The JDK uses a LazyActionMap for parentActionMap
+ SwingUtilities.replaceUIActionMap(optionPane, getActionMap());
+ }
+
+ /**
+ * Fetches the action map from the UI defaults, or create a new one
+ * if the action map hasn't been initialized.
+ *
+ * @return the action map
+ */
+ private ActionMap getActionMap()
+ {
+ ActionMap am = (ActionMap) UIManager.get("OptionPane.actionMap");
+ if (am == null)
+ {
+ am = createDefaultActions();
+ UIManager.getLookAndFeelDefaults().put("OptionPane.actionMap", am);
+ }
+ return am;
+ }
+
+ private ActionMap createDefaultActions()
+ {
+ ActionMapUIResource am = new ActionMapUIResource();
+ Action action = new OptionPaneCloseAction();
+
+ am.put("close", action);
+ return am;
}
/**
* This method installs listeners for the JOptionPane.
*/
protected void installListeners()
{
propertyChangeListener = createPropertyChangeListener();
optionPane.addPropertyChangeListener(propertyChangeListener);
}
@@ -1308,45 +1349,42 @@
/**
* This method uninstalls the defaults for the JOptionPane.
*/
protected void uninstallDefaults()
{
optionPane.setFont(null);
optionPane.setForeground(null);
optionPane.setBackground(null);
minimumSize = null;
- messageBorder = null;
- buttonBorder = null;
- messageForeground = null;
-
// FIXME: ImageIcons don't seem to work properly
/*
warningIcon = null;
errorIcon = null;
questionIcon = null;
infoIcon = null;
*/
}
/**
* This method uninstalls keyboard actions for the JOptionPane.
*/
protected void uninstallKeyboardActions()
- throws NotImplementedException
{
- // FIXME: implement.
+ SwingUtilities.replaceUIInputMap(optionPane, JComponent.
+ WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
+ SwingUtilities.replaceUIActionMap(optionPane, null);
}
/**
* This method uninstalls listeners for the JOptionPane.
*/
protected void uninstallListeners()
{
optionPane.removePropertyChangeListener(propertyChangeListener);
propertyChangeListener = null;
}
/**
@@ -1354,13 +1392,29 @@
*
* @param c The JComponent to uninstall for.
*/
public void uninstallUI(JComponent c)
{
uninstallKeyboardActions();
uninstallListeners();
uninstallComponents();
uninstallDefaults();
optionPane = null;
}
+
+ /**
+ * Applies the proper UI configuration to labels that are added to
+ * the OptionPane.
+ *
+ * @param l the label to configure
+ */
+ private void configureLabel(JLabel l)
+ {
+ Color c = UIManager.getColor("OptionPane.messageForeground");
+ if (c != null)
+ l.setForeground(c);
+ Font f = UIManager.getFont("OptionPane.messageFont");
+ if (f != null)
+ l.setFont(f);
+ }
}
Index: javax/swing/plaf/basic/BasicTableUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicTableUI.java,v
retrieving revision 1.56
diff -u -1 -2 -r1.56 BasicTableUI.java
--- javax/swing/plaf/basic/BasicTableUI.java 26 Jul 2006 09:59:38 -0000 1.56
+++ javax/swing/plaf/basic/BasicTableUI.java 26 Jul 2006 13:32:14 -0000
@@ -497,25 +497,25 @@
/**
* Fetches the action map from the UI defaults, or create a new one
* if the action map hasn't been initialized.
*
* @return the action map
*/
private ActionMap getActionMap()
{
ActionMap am = (ActionMap) UIManager.get("Table.actionMap");
if (am == null)
{
am = createDefaultActions();
- UIManager.getLookAndFeelDefaults().put("Tree.actionMap", am);
+ UIManager.getLookAndFeelDefaults().put("Table.actionMap", am);
}
return am;
}
private ActionMap createDefaultActions()
{
ActionMapUIResource am = new ActionMapUIResource();
Action action = new TableAction();
am.put("cut", TransferHandler.getCutAction());
am.put("copy", TransferHandler.getCopyAction());
am.put("paste", TransferHandler.getPasteAction());
Index: javax/swing/plaf/basic/BasicToolBarUI.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/plaf/basic/BasicToolBarUI.java,v
retrieving revision 1.27
diff -u -1 -2 -r1.27 BasicToolBarUI.java
--- javax/swing/plaf/basic/BasicToolBarUI.java 26 Jul 2006 11:22:32 -0000 1.27
+++ javax/swing/plaf/basic/BasicToolBarUI.java 26 Jul 2006 13:32:14 -0000
@@ -663,29 +663,29 @@
// FIXME: The JDK uses a LazyActionMap for parentActionMap
SwingUtilities.replaceUIActionMap(toolBar, getActionMap());
}
/**
* Fetches the action map from the UI defaults, or create a new one
* if the action map hasn't been initialized.
*
* @return the action map
*/
private ActionMap getActionMap()
{
- ActionMap am = (ActionMap) UIManager.get("Table.actionMap");
+ ActionMap am = (ActionMap) UIManager.get("ToolBar.actionMap");
if (am == null)
{
am = createDefaultActions();
- UIManager.getLookAndFeelDefaults().put("Tree.actionMap", am);
+ UIManager.getLookAndFeelDefaults().put("ToolBar.actionMap", am);
}
return am;
}
private ActionMap createDefaultActions()
{
ActionMapUIResource am = new ActionMapUIResource();
Action action = new ToolBarAction();
am.put("navigateLeft", action);
am.put("navigateRight", action);
am.put("navigateUp", action);