+ * Any existing value associated with the key will be overwritten.
+ *
+ * @param key the key (not null).
+ * @param value the value (null permitted).
+ */
+ public void putValue(String key, Object value)
+ {
+ Object old = getValue(key);
+ if ((old == null && value != null) || (old != null && !old.equals(value)))
+ {
+ store.put(key, value);
+ firePropertyChange(key, old, value);
+ }
+ }
+
+ /**
+ * Returns the flag that indicates whether or not the action is enabled.
+ *
+ * @return The flag.
+ *
+ * @see #setEnabled(boolean)
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * Sets the flag that indicates whether or not the action is enabled and, if
+ * the value of the flag changed from the previous setting, sends a
+ * {@link java.beans.PropertyChangeEvent} to all registered listeners (using
+ * the property name 'enabled').
+ *
+ * @param enabled the new flag value.
+ *
+ * @see #isEnabled()
+ */
+ public void setEnabled(boolean enabled)
+ {
+ if (enabled != this.enabled)
+ {
+ this.enabled = enabled;
+ firePropertyChange("enabled", !this.enabled, this.enabled);
+ }
+ }
+
+ /**
+ * Returns an array of the keys for the property values that have been
+ * defined via the {@link #putValue(String, Object)} method (or the class
+ * constructor).
+ *
+ * @return An array of keys.
+ */
+ public Object[] getKeys()
+ {
+ return store.keySet().toArray();
+ }
+
+ /**
+ * Sends a {@link PropertyChangeEvent} for the named property to all
+ * registered listeners.
+ *
+ * @param propertyName the property name.
+ * @param oldValue the old value of the property.
+ * @param newValue the new value of the property.
+ */
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue)
+ {
+ changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+ }
+
+ /**
+ * Sends a {@link PropertyChangeEvent} for the named property to all
+ * registered listeners. This private method is called by the
+ * {@link #setEnabled(boolean)} method.
+ *
+ * @param propertyName the property name.
+ * @param oldValue the old value of the property.
+ * @param newValue the new value of the property.
+ */
+ private void firePropertyChange(String propertyName, boolean oldValue,
+ boolean newValue)
+ {
+ changeSupport.firePropertyChange(propertyName, oldValue, newValue);
+ }
+
+ /**
+ * Registers a listener to receive {@link PropertyChangeEvent} notifications
+ * from this action.
+ *
+ * @param listener the listener.
+ *
+ * @see #removePropertyChangeListener(PropertyChangeListener)
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ changeSupport.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives
+ * {@link PropertyChangeEvent} notifications from this action.
+ *
+ * @param listener the listener.
+ *
+ * @see #addPropertyChangeListener(PropertyChangeListener)
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ changeSupport.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns all registered listeners.
+ *
+ * @return An array of listeners.
+ *
+ * @since 1.4
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return changeSupport.getPropertyChangeListeners();
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractButton.java b/libjava/classpath/javax/swing/AbstractButton.java
new file mode 100644
index 000000000..6cbc7751f
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractButton.java
@@ -0,0 +1,2652 @@
+/* AbstractButton.java -- Provides basic button functionality.
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.ItemSelectable;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.image.ImageObserver;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Enumeration;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleIcon;
+import javax.accessibility.AccessibleRelation;
+import javax.accessibility.AccessibleRelationSet;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ButtonUI;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.Position;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.View;
+
+
+/**
+ * Provides an abstract implementation of common button behaviour,
+ * data model and look & feel.
+ *
+ *
This class is supposed to serve as a base class for
+ * several kinds of buttons with similar but non-identical semantics:
+ * toggle buttons (radio buttons and checkboxes), simple push buttons,
+ * menu items, etc.
+ *
+ *
Buttons have many properties, some of which are stored in this class
+ * while others are delegated to the button's model. The following properties
+ * are available:
+ *
+ *
+ *
Property
Stored in
Bound?
+ *
+ *
action
button
no
+ *
actionCommand
model
no
+ *
borderPainted
button
yes
+ *
contentAreaFilled
button
yes
+ *
disabledIcon
button
yes
+ *
disabledSelectedIcon
button
yes
+ *
displayedMnemonicIndex
button
no
+ *
enabled
model
no
+ *
focusPainted
button
yes
+ *
horizontalAlignment
button
yes
+ *
horizontalTextPosition
button
yes
+ *
icon
button
yes
+ *
iconTextGap
button
no
+ *
label (same as text)
model
yes
+ *
margin
button
yes
+ *
multiClickThreshold
button
no
+ *
pressedIcon
button
yes
+ *
rolloverEnabled
button
yes
+ *
rolloverIcon
button
yes
+ *
rolloverSelectedIcon
button
yes
+ *
selected
model
no
+ *
selectedIcon
button
yes
+ *
selectedObjects
button
no
+ *
text
model
yes
+ *
UI
button
yes
+ *
verticalAlignment
button
yes
+ *
verticalTextPosition
button
yes
+ *
+ *
+ *
+ *
The various behavioral aspects of these properties follows:
+ *
+ *
+ *
+ *
When non-bound properties stored in the button change, the button
+ * fires ChangeEvents to its ChangeListeners.
+ *
+ *
When bound properties stored in the button change, the button fires
+ * PropertyChangeEvents to its PropertyChangeListeners
+ *
+ *
If any of the model's properties change, it fires a ChangeEvent to
+ * its ChangeListeners, which include the button.
+ *
+ *
If the button receives a ChangeEvent from its model, it will
+ * propagate the ChangeEvent to its ChangeListeners, with the ChangeEvent's
+ * "source" property set to refer to the button, rather than the model. The
+ * the button will request a repaint, to paint its updated state.
+ *
+ *
If the model's "selected" property changes, the model will fire an
+ * ItemEvent to its ItemListeners, which include the button, in addition to
+ * the ChangeEvent which models the property change. The button propagates
+ * ItemEvents directly to its ItemListeners.
+ *
+ *
If the model's armed and pressed properties are simultaneously
+ * true, the model will fire an ActionEvent to its
+ * ActionListeners, which include the button. The button will propagate
+ * this ActionEvent to its ActionListeners, with the ActionEvent's "source"
+ * property set to refer to the button, rather than the model.
+ *
+ *
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public abstract class AbstractButton extends JComponent
+ implements ItemSelectable, SwingConstants
+{
+ private static final long serialVersionUID = -937921345538462020L;
+
+ /**
+ * An extension of ChangeListener to be serializable.
+ */
+ protected class ButtonChangeListener
+ implements ChangeListener, Serializable
+ {
+ private static final long serialVersionUID = 1471056094226600578L;
+
+ /**
+ * The spec has no public/protected constructor for this class, so do we.
+ */
+ ButtonChangeListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notified when the target of the listener changes its state.
+ *
+ * @param ev the ChangeEvent describing the change
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ getEventHandler().stateChanged(ev);
+ }
+ }
+
+ /**
+ * The combined event handler for ActionEvent, ChangeEvent and
+ * ItemEvent. This combines ButtonChangeListener, ActionListener
+ */
+ private class EventHandler
+ implements ActionListener, ChangeListener, ItemListener
+ {
+ public void actionPerformed(ActionEvent ev)
+ {
+ fireActionPerformed(ev);
+ }
+
+ public void stateChanged(ChangeEvent ev)
+ {
+ fireStateChanged();
+ repaint();
+ }
+
+ public void itemStateChanged(ItemEvent ev)
+ {
+ fireItemStateChanged(ev);
+ }
+ }
+
+ /** The icon displayed by default. */
+ Icon default_icon;
+
+ /** The icon displayed when the button is pressed. */
+ Icon pressed_icon;
+
+ /** The icon displayed when the button is disabled. */
+ Icon disabledIcon;
+
+ /** The icon displayed when the button is selected. */
+ Icon selectedIcon;
+
+ /** The icon displayed when the button is selected but disabled. */
+ Icon disabledSelectedIcon;
+
+ /** The icon displayed when the button is rolled over. */
+ Icon rolloverIcon;
+
+ /** The icon displayed when the button is selected and rolled over. */
+ Icon rolloverSelectedIcon;
+
+ /** The icon currently displayed. */
+ Icon current_icon;
+
+ /** The text displayed in the button. */
+ String text;
+
+ /**
+ * The gap between icon and text, if both icon and text are
+ * non-null.
+ */
+ int iconTextGap;
+
+ /** The vertical alignment of the button's text and icon. */
+ int verticalAlignment;
+
+ /** The horizontal alignment of the button's text and icon. */
+ int horizontalAlignment;
+
+ /** The horizontal position of the button's text relative to its icon. */
+ int horizontalTextPosition;
+
+ /** The vertical position of the button's text relative to its icon. */
+ int verticalTextPosition;
+
+ /** Whether or not the button paints its border. */
+ boolean borderPainted;
+
+ /** Whether or not the button paints its focus state. */
+ boolean focusPainted;
+
+ /** Whether or not the button fills its content area. */
+ boolean contentAreaFilled;
+
+ /** Whether rollover is enabled. */
+ boolean rollOverEnabled;
+
+ /** The action taken when the button is clicked. */
+ Action action;
+
+ /** The button's current state. */
+ protected ButtonModel model;
+
+ /** The margin between the button's border and its label. */
+ Insets margin;
+
+ /**
+ * A hint to the look and feel class, suggesting which character in the
+ * button's label should be underlined when drawing the label.
+ */
+ int mnemonicIndex;
+
+ /**
+ * Listener the button uses to receive ActionEvents from its model.
+ */
+ protected ActionListener actionListener;
+
+ /**
+ * Listener the button uses to receive ItemEvents from its model.
+ */
+ protected ItemListener itemListener;
+
+ /**
+ * Listener the button uses to receive ChangeEvents from its model.
+ */
+ protected ChangeListener changeListener;
+
+ /**
+ * The event handler for ActionEvent, ItemEvent and ChangeEvent.
+ * This replaces the above three handlers and combines them
+ * into one for efficiency.
+ */
+ private EventHandler eventHandler;
+
+ /**
+ * The time in milliseconds in which clicks get coalesced into a single
+ * ActionEvent.
+ */
+ long multiClickThreshhold;
+
+ /**
+ * Listener the button uses to receive PropertyChangeEvents from its
+ * Action.
+ */
+ PropertyChangeListener actionPropertyChangeListener;
+
+ /** ChangeEvent that is fired to button's ChangeEventListeners */
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /**
+ * Indicates if the borderPainted property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientBorderPaintedSet = false;
+
+ /**
+ * Indicates if the rolloverEnabled property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRolloverEnabledSet = false;
+
+ /**
+ * Indicates if the iconTextGap property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientIconTextGapSet = false;
+
+ /**
+ * Indicates if the contentAreaFilled property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientContentAreaFilledSet = false;
+
+ /**
+ * Fired in a PropertyChangeEvent when the "borderPainted" property changes.
+ */
+ public static final String BORDER_PAINTED_CHANGED_PROPERTY = "borderPainted";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "contentAreaFilled" property
+ * changes.
+ */
+ public static final String CONTENT_AREA_FILLED_CHANGED_PROPERTY =
+ "contentAreaFilled";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "disabledIcon" property changes.
+ */
+ public static final String DISABLED_ICON_CHANGED_PROPERTY = "disabledIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "disabledSelectedIcon" property
+ * changes.
+ */
+ public static final String DISABLED_SELECTED_ICON_CHANGED_PROPERTY =
+ "disabledSelectedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "focusPainted" property changes.
+ */
+ public static final String FOCUS_PAINTED_CHANGED_PROPERTY = "focusPainted";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "horizontalAlignment" property
+ * changes.
+ */
+ public static final String HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY =
+ "horizontalAlignment";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "horizontalTextPosition" property
+ * changes.
+ */
+ public static final String HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY =
+ "horizontalTextPosition";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "icon" property changes. */
+ public static final String ICON_CHANGED_PROPERTY = "icon";
+
+ /** Fired in a PropertyChangeEvent when the "margin" property changes. */
+ public static final String MARGIN_CHANGED_PROPERTY = "margin";
+
+ /** Fired in a PropertyChangeEvent when the "mnemonic" property changes. */
+ public static final String MNEMONIC_CHANGED_PROPERTY = "mnemonic";
+
+ /** Fired in a PropertyChangeEvent when the "model" property changes. */
+ public static final String MODEL_CHANGED_PROPERTY = "model";
+
+ /** Fired in a PropertyChangeEvent when the "pressedIcon" property changes. */
+ public static final String PRESSED_ICON_CHANGED_PROPERTY = "pressedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverEnabled" property
+ * changes.
+ */
+ public static final String ROLLOVER_ENABLED_CHANGED_PROPERTY =
+ "rolloverEnabled";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverIcon" property changes.
+ */
+ public static final String ROLLOVER_ICON_CHANGED_PROPERTY = "rolloverIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "rolloverSelectedIcon" property
+ * changes.
+ */
+ public static final String ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY =
+ "rolloverSelectedIcon";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "selectedIcon" property changes.
+ */
+ public static final String SELECTED_ICON_CHANGED_PROPERTY = "selectedIcon";
+
+ /** Fired in a PropertyChangeEvent when the "text" property changes. */
+ public static final String TEXT_CHANGED_PROPERTY = "text";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "verticalAlignment" property
+ * changes.
+ */
+ public static final String VERTICAL_ALIGNMENT_CHANGED_PROPERTY =
+ "verticalAlignment";
+
+ /**
+ * Fired in a PropertyChangeEvent when the "verticalTextPosition" property
+ * changes.
+ */
+ public static final String VERTICAL_TEXT_POSITION_CHANGED_PROPERTY =
+ "verticalTextPosition";
+
+ /**
+ * A Java Accessibility extension of the AbstractButton.
+ */
+ protected abstract class AccessibleAbstractButton
+ extends AccessibleJComponent implements AccessibleAction, AccessibleValue,
+ AccessibleText
+ {
+ private static final long serialVersionUID = -5673062525319836790L;
+
+ protected AccessibleAbstractButton()
+ {
+ // Nothing to do here yet.
+ }
+
+ /**
+ * Returns the accessible state set of this object. In addition to the
+ * superclass's states, the AccessibleAbstractButton
+ * supports the following states: {@link AccessibleState#ARMED},
+ * {@link AccessibleState#FOCUSED}, {@link AccessibleState#PRESSED} and
+ * {@link AccessibleState#CHECKED}.
+ *
+ * @return the current state of this accessible object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+
+ if (getModel().isArmed())
+ state.add(AccessibleState.ARMED);
+ if (getModel().isPressed())
+ state.add(AccessibleState.PRESSED);
+ if (isSelected())
+ state.add(AccessibleState.CHECKED);
+
+ return state;
+ }
+
+ /**
+ * Returns the accessible name for the button.
+ */
+ public String getAccessibleName()
+ {
+ String result = super.getAccessibleName();
+ if (result == null)
+ result = text;
+ return result;
+ }
+
+ /**
+ * Returns the accessible icons of this object. If the AbstractButton's
+ * icon is an Accessible, and it's AccessibleContext is an AccessibleIcon,
+ * then this AccessibleIcon is returned, otherwise null.
+ *
+ * @return the accessible icons of this object, or null if
+ * there is no accessible icon
+ */
+ public AccessibleIcon[] getAccessibleIcon()
+ {
+ AccessibleIcon[] ret = null;
+ Icon icon = getIcon();
+ if (icon instanceof Accessible)
+ {
+ AccessibleContext ctx = ((Accessible) icon).getAccessibleContext();
+ if (ctx instanceof AccessibleIcon)
+ {
+ ret = new AccessibleIcon[]{ (AccessibleIcon) ctx };
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the accessible relations of this AccessibleAbstractButton.
+ * If the AbstractButton is part of a ButtonGroup, then all the buttons
+ * in this button group are added as targets in a MEMBER_OF relation,
+ * otherwise an empty relation set is returned (from super).
+ *
+ * @return the accessible relations of this AccessibleAbstractButton
+ */
+ public AccessibleRelationSet getAccessibleRelationSet()
+ {
+ AccessibleRelationSet relations = super.getAccessibleRelationSet();
+ ButtonModel model = getModel();
+ if (model instanceof DefaultButtonModel)
+ {
+ ButtonGroup group = ((DefaultButtonModel) model).getGroup();
+ if (group != null)
+ {
+ Object[] target = new Object[group.getButtonCount()];
+ Enumeration els = group.getElements();
+
+ for (int index = 0; els.hasMoreElements(); ++index)
+ {
+ target[index] = els.nextElement();
+ }
+
+ AccessibleRelation rel =
+ new AccessibleRelation(AccessibleRelation.MEMBER_OF);
+ rel.setTarget(target);
+ relations.add(rel);
+ }
+ }
+ return relations;
+ }
+
+ /**
+ * Returns the accessible action associated with this object. For buttons,
+ * this will be this.
+ *
+ * @return this
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible value of this AccessibleAbstractButton, which
+ * is always this.
+ *
+ * @return the accessible value of this AccessibleAbstractButton, which
+ * is always this
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of accessible actions that are supported by this
+ * object. Buttons support one action by default ('press button'), so this
+ * method always returns 1.
+ *
+ * @return 1, the number of supported accessible actions
+ */
+ public int getAccessibleActionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns a description for the action with the specified index or
+ * null if such action does not exist.
+ *
+ * @param actionIndex the zero based index to the actions
+ *
+ * @return a description for the action with the specified index or
+ * null if such action does not exist
+ */
+ public String getAccessibleActionDescription(int actionIndex)
+ {
+ String descr = null;
+ if (actionIndex == 0)
+ {
+ // FIXME: Supply localized descriptions in the UIDefaults.
+ descr = UIManager.getString("AbstractButton.clickText");
+ }
+ return descr;
+ }
+
+ /**
+ * Performs the acccessible action with the specified index on this object.
+ * Since buttons have only one action by default (which is to press the
+ * button), this method performs a 'press button' when the specified index
+ * is 0 and nothing otherwise.
+ *
+ * @param actionIndex a zero based index into the actions of this button
+ *
+ * @return true if the specified action has been performed
+ * successfully, false otherwise
+ */
+ public boolean doAccessibleAction(int actionIndex)
+ {
+ boolean retVal = false;
+ if (actionIndex == 0)
+ {
+ doClick();
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the current value of this object as a number. This
+ * implementation returns an Integer(1) if the button is
+ * selected, Integer(0) if the button is not selected.
+ *
+ * @return the current value of this object as a number
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ Integer retVal;
+ if (isSelected())
+ retVal = new Integer(1);
+ else
+ retVal = new Integer(0);
+ return retVal;
+ }
+
+ /**
+ * Sets the current accessible value as object. If the specified number
+ * is 0 the button will be deselected, otherwise the button will
+ * be selected.
+ *
+ * @param value 0 for deselected button, other for selected button
+ *
+ * @return true if the value has been set, false
+ * otherwise
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ boolean retVal = false;
+ if (value != null)
+ {
+ if (value.intValue() == 0)
+ setSelected(false);
+ else
+ setSelected(true);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the minimum accessible value for the AccessibleAbstractButton,
+ * which is 0.
+ *
+ * @return the minimimum accessible value for the AccessibleAbstractButton,
+ * which is 0
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(0);
+ }
+
+ /**
+ * Returns the maximum accessible value for the AccessibleAbstractButton,
+ * which is 1.
+ *
+ * @return the maximum accessible value for the AccessibleAbstractButton,
+ * which is 1
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(1);
+ }
+
+ /**
+ * Returns the accessible text for this AccessibleAbstractButton. This
+ * will be null if the button has a non-HTML label, otherwise
+ * this.
+ *
+ * @return the accessible text for this AccessibleAbstractButton
+ */
+ public AccessibleText getAccessibleText()
+ {
+ AccessibleText accessibleText = null;
+ if (getClientProperty(BasicHTML.propertyKey) != null)
+ accessibleText = this;
+
+ return accessibleText;
+ }
+
+ /**
+ * Returns the index of the label's character at the specified point,
+ * relative to the local bounds of the button. This only works for
+ * HTML labels.
+ *
+ * @param p the point, relative to the buttons local bounds
+ *
+ * @return the index of the label's character at the specified point
+ */
+ public int getIndexAtPoint(Point p)
+ {
+ int index = -1;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
+ index = view.viewToModel(p.x, p.y, shape, new Position.Bias[1]);
+ }
+ return index;
+ }
+
+ /**
+ * Returns the bounds of the character at the specified index of the
+ * button's label. This will only work for HTML labels.
+ *
+ * @param i the index of the character of the label
+ *
+ * @return the bounds of the character at the specified index of the
+ * button's label
+ */
+ public Rectangle getCharacterBounds(int i)
+ {
+ Rectangle rect = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle shape = new Rectangle(0, 0, getWidth(), getHeight());
+ try
+ {
+ Shape s = view.modelToView(i, shape, Position.Bias.Forward);
+ rect = s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ rect = null;
+ }
+ }
+ return rect;
+ }
+
+ /**
+ * Returns the number of characters in the button's label.
+ *
+ * @return the bounds of the character at the specified index of the
+ * button's label
+ */
+ public int getCharCount()
+ {
+ int charCount;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ charCount = view.getDocument().getLength();
+ }
+ else
+ {
+ charCount = getAccessibleName().length();
+ }
+ return charCount;
+ }
+
+ /**
+ * This always returns -1 since there is no caret in a button.
+ *
+ * @return -1 since there is no caret in a button
+ */
+ public int getCaretPosition()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the character, word or sentence at the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after index
+ */
+ public String getAtIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.lastIndexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.lastIndexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence after the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after index
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index + 1));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.indexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.indexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence before the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence before the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence before index
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index - 1));
+ break;
+ case AccessibleText.WORD:
+ endIndex = text.lastIndexOf(' ', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf(' ', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ endIndex = text.lastIndexOf('.', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf('.', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the text attribute for the character at the specified character
+ * index.
+ *
+ * @param i the character index
+ *
+ * @return the character attributes for the specified character or
+ * null if the character has no attributes
+ */
+ public AttributeSet getCharacterAttribute(int i)
+ {
+ AttributeSet atts = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Document doc = view.getDocument();
+ if (doc instanceof StyledDocument)
+ {
+ StyledDocument sDoc = (StyledDocument) doc;
+ Element charEl = sDoc.getCharacterElement(i);
+ if (charEl != null)
+ atts = charEl.getAttributes();
+ }
+ }
+ return atts;
+ }
+
+ /**
+ * This always returns -1 since
+ * button labels can't be selected.
+ *
+ * @return -1, button labels can't be selected
+ */
+ public int getSelectionStart()
+ {
+ return -1;
+ }
+
+ /**
+ * This always returns -1 since
+ * button labels can't be selected.
+ *
+ * @return -1, button labels can't be selected
+ */
+ public int getSelectionEnd()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the selected text. This always returns null since
+ * button labels can't be selected.
+ *
+ * @return null, button labels can't be selected
+ */
+ public String getSelectedText()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Creates a new AbstractButton object. Subclasses should call the following
+ * sequence in their constructor in order to initialize the button correctly:
+ *
+ * super();
+ * init(text, icon);
+ *
+ *
+ * The {@link #init(String, Icon)} method is not called automatically by this
+ * constructor.
+ *
+ * @see #init(String, Icon)
+ */
+ public AbstractButton()
+ {
+ horizontalAlignment = CENTER;
+ horizontalTextPosition = TRAILING;
+ verticalAlignment = CENTER;
+ verticalTextPosition = CENTER;
+ borderPainted = true;
+ contentAreaFilled = true;
+ focusPainted = true;
+ setFocusable(true);
+ setAlignmentX(CENTER_ALIGNMENT);
+ setAlignmentY(CENTER_ALIGNMENT);
+ setDisplayedMnemonicIndex(-1);
+ setOpaque(true);
+ text = "";
+ // testing on JRE1.5 shows that the iconTextGap default value is
+ // hard-coded here and the 'Button.iconTextGap' setting in the
+ // UI defaults is ignored, at least by the MetalLookAndFeel
+ iconTextGap = 4;
+ }
+
+ /**
+ * Get the model the button is currently using.
+ *
+ * @return The current model
+ */
+ public ButtonModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Set the model the button is currently using. This un-registers all
+ * listeners associated with the current model, and re-registers them
+ * with the new model.
+ *
+ * @param newModel The new model
+ */
+ public void setModel(ButtonModel newModel)
+ {
+ if (newModel == model)
+ return;
+
+ if (model != null)
+ {
+ model.removeActionListener(actionListener);
+ actionListener = null;
+ model.removeChangeListener(changeListener);
+ changeListener = null;
+ model.removeItemListener(itemListener);
+ itemListener = null;
+ }
+ ButtonModel old = model;
+ model = newModel;
+ if (model != null)
+ {
+ actionListener = createActionListener();
+ model.addActionListener(actionListener);
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ itemListener = createItemListener();
+ model.addItemListener(itemListener);
+ }
+ firePropertyChange(MODEL_CHANGED_PROPERTY, old, model);
+ revalidate();
+ repaint();
+ }
+
+ protected void init(String text, Icon icon)
+ {
+ // If text is null, we fall back to the empty
+ // string (which is set using AbstractButton's
+ // constructor).
+ // This way the behavior of the JDK is matched.
+ if(text != null)
+ setText(text);
+
+ if (icon != null)
+ default_icon = icon;
+
+ updateUI();
+ }
+
+ /**
+ *
Returns the action command string for this button's model.
+ *
+ *
If the action command was set to null, the button's
+ * text (label) is returned instead.
+ *
+ * @return The current action command string from the button's model
+ */
+ public String getActionCommand()
+ {
+ String ac = model.getActionCommand();
+ if (ac != null)
+ return ac;
+ else
+ return text;
+ }
+
+ /**
+ * Sets the action command string for this button's model.
+ *
+ * @param actionCommand The new action command string to set in the button's
+ * model.
+ */
+ public void setActionCommand(String actionCommand)
+ {
+ if (model != null)
+ model.setActionCommand(actionCommand);
+ }
+
+ /**
+ * Adds an ActionListener to the button's listener list. When the
+ * button's model is clicked it fires an ActionEvent, and these
+ * listeners will be called.
+ *
+ * @param l The new listener to add
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Removes an ActionListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+
+ /**
+ * Returns all added ActionListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Adds an ItemListener to the button's listener list. When the button's
+ * model changes state (between any of ARMED, ENABLED, PRESSED, ROLLOVER
+ * or SELECTED) it fires an ItemEvent, and these listeners will be
+ * called.
+ *
+ * @param l The new listener to add
+ */
+ public void addItemListener(ItemListener l)
+ {
+ listenerList.add(ItemListener.class, l);
+ }
+
+ /**
+ * Removes an ItemListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeItemListener(ItemListener l)
+ {
+ listenerList.remove(ItemListener.class, l);
+ }
+
+ /**
+ * Returns all added ItemListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) listenerList.getListeners(ItemListener.class);
+ }
+
+ /**
+ * Adds a ChangeListener to the button's listener list. When the button's
+ * model changes any of its (non-bound) properties, these listeners will be
+ * called.
+ *
+ * @param l The new listener to add
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * Removes a ChangeListener from the button's listener list.
+ *
+ * @param l The listener to remove
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * Returns all added ChangeListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Calls {@link ItemListener#itemStateChanged} on each ItemListener in
+ * the button's listener list.
+ *
+ * @param e The event signifying that the button's model changed state
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ e.setSource(this);
+ ItemListener[] listeners = getItemListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].itemStateChanged(e);
+ }
+
+ /**
+ * Calls {@link ActionListener#actionPerformed} on each {@link
+ * ActionListener} in the button's listener list.
+ *
+ * @param e The event signifying that the button's model was clicked
+ */
+ protected void fireActionPerformed(ActionEvent e)
+ {
+ // Dispatch a copy of the given ActionEvent in order to
+ // set the source and action command correctly.
+ ActionEvent ae = new ActionEvent(
+ this,
+ e.getID(),
+ getActionCommand(),
+ e.getWhen(),
+ e.getModifiers());
+
+ ActionListener[] listeners = getActionListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].actionPerformed(ae);
+ }
+
+ /**
+ * Calls {@link ChangeListener#stateChanged} on each {@link ChangeListener}
+ * in the button's listener list.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Get the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @return The button's current keyboard mnemonic
+ */
+ public int getMnemonic()
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ return mod.getMnemonic();
+ return -1;
+ }
+
+ /**
+ * Set the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @param mne A new mnemonic to use for the button
+ */
+ public void setMnemonic(char mne)
+ {
+ setMnemonic((int) mne);
+ }
+
+ /**
+ * Set the current keyboard mnemonic value. This value corresponds to a
+ * single key code (one of the {@link java.awt.event.KeyEvent} VK_*
+ * codes) and is used to activate the button when pressed in conjunction
+ * with the "mouseless modifier" of the button's look and feel class, and
+ * when focus is in one of the button's ancestors.
+ *
+ * @param mne A new mnemonic to use for the button
+ */
+ public void setMnemonic(int mne)
+ {
+ ButtonModel mod = getModel();
+ int old = -1;
+ if (mod != null)
+ old = mod.getMnemonic();
+
+ if (old != mne)
+ {
+ if (mod != null)
+ mod.setMnemonic(mne);
+
+ if (text != null && !text.equals(""))
+ {
+ // Since lower case char = upper case char for
+ // mnemonic, we will convert both text and mnemonic
+ // to upper case before checking if mnemonic character occurs
+ // in the menu item text.
+ int upperCaseMne = Character.toUpperCase((char) mne);
+ String upperCaseText = text.toUpperCase();
+ setDisplayedMnemonicIndex(upperCaseText.indexOf(upperCaseMne));
+ }
+
+ firePropertyChange(MNEMONIC_CHANGED_PROPERTY, old, mne);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Sets the button's mnemonic index. The mnemonic index is a hint to the
+ * look and feel class, suggesting which character in the button's label
+ * should be underlined when drawing the label. If the mnemonic index is
+ * -1, no mnemonic will be displayed.
+ *
+ * If no mnemonic index is set, the button will choose a mnemonic index
+ * by default, which will be the first occurrence of the mnemonic
+ * character in the button's text.
+ *
+ * @param index An offset into the "text" property of the button
+ * @throws IllegalArgumentException If index is not within the
+ * range of legal offsets for the "text" property of the button.
+ * @since 1.4
+ */
+
+ public void setDisplayedMnemonicIndex(int index)
+ {
+ if (index < -1 || (text != null && index >= text.length()))
+ throw new IllegalArgumentException();
+
+ mnemonicIndex = index;
+ }
+
+ /**
+ * Get the button's mnemonic index, which is an offset into the button's
+ * "text" property. The character specified by this offset should be
+ * underlined when the look and feel class draws this button.
+ *
+ * @return An index into the button's "text" property
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return mnemonicIndex;
+ }
+
+
+ /**
+ * Set the "rolloverEnabled" property. When rollover is enabled, and the
+ * look and feel supports it, the button will change its icon to
+ * rolloverIcon, when the mouse passes over it.
+ *
+ * @param r Whether or not to enable rollover icon changes
+ */
+ public void setRolloverEnabled(boolean r)
+ {
+ clientRolloverEnabledSet = true;
+ if (rollOverEnabled != r)
+ {
+ rollOverEnabled = r;
+ firePropertyChange(ROLLOVER_ENABLED_CHANGED_PROPERTY, !r, r);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns whether or not rollover icon changes are enabled on the
+ * button.
+ *
+ * @return The state of the "rolloverEnabled" property
+ */
+ public boolean isRolloverEnabled()
+ {
+ return rollOverEnabled;
+ }
+
+ /**
+ * Set the value of the button's "selected" property. Selection is only
+ * meaningful for toggle-type buttons (check boxes, radio buttons).
+ *
+ * @param s New value for the property
+ */
+ public void setSelected(boolean s)
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ mod.setSelected(s);
+ }
+
+ /**
+ * Get the value of the button's "selected" property. Selection is only
+ * meaningful for toggle-type buttons (check boxes, radio buttons).
+ *
+ * @return The value of the property
+ */
+ public boolean isSelected()
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ return mod.isSelected();
+ return false;
+ }
+
+ /**
+ * Enables or disables the button. A button will neither be selectable
+ * nor preform any actions unless it is enabled.
+ *
+ * @param b Whether or not to enable the button
+ */
+ public void setEnabled(boolean b)
+ {
+ // Do nothing if state does not change.
+ if (b == isEnabled())
+ return;
+ super.setEnabled(b);
+ setFocusable(b);
+ ButtonModel mod = getModel();
+ if (mod != null)
+ mod.setEnabled(b);
+ }
+
+ /**
+ * Set the horizontal alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: RIGHT, LEFT, CENTER,
+ * LEADING or TRAILING. The default is
+ * CENTER.
+ *
+ * @return The current horizontal alignment
+ *
+ * @see #setHorizontalAlignment(int)
+ */
+ public int getHorizontalAlignment()
+ {
+ return horizontalAlignment;
+ }
+
+ /**
+ * Set the horizontal alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: RIGHT, LEFT, CENTER,
+ * LEADING or TRAILING. The default is
+ * CENTER.
+ *
+ * @param a The new horizontal alignment
+ * @throws IllegalArgumentException If alignment is not one of the legal
+ * constants.
+ *
+ * @see #getHorizontalAlignment()
+ */
+ public void setHorizontalAlignment(int a)
+ {
+ if (horizontalAlignment == a)
+ return;
+ if (a != LEFT && a != CENTER && a != RIGHT && a != LEADING
+ && a != TRAILING)
+ throw new IllegalArgumentException("Invalid alignment.");
+ int old = horizontalAlignment;
+ horizontalAlignment = a;
+ firePropertyChange(HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the horizontal position of the button's text relative to its
+ * icon. The position is a numeric constant from {@link
+ * SwingConstants}. It must be one of: RIGHT,
+ * LEFT, CENTER, LEADING or
+ * TRAILING. The default is TRAILING.
+ *
+ * @return The current horizontal text position
+ */
+ public int getHorizontalTextPosition()
+ {
+ return horizontalTextPosition;
+ }
+
+ /**
+ * Set the horizontal position of the button's text relative to its
+ * icon. The position is a numeric constant from {@link
+ * SwingConstants}. It must be one of: RIGHT,
+ * LEFT, CENTER, LEADING or
+ * TRAILING. The default is TRAILING.
+ *
+ * @param t The new horizontal text position
+ * @throws IllegalArgumentException If position is not one of the legal
+ * constants.
+ */
+ public void setHorizontalTextPosition(int t)
+ {
+ if (horizontalTextPosition == t)
+ return;
+ if (t != LEFT && t != CENTER && t != RIGHT && t != LEADING
+ && t != TRAILING)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = horizontalTextPosition;
+ horizontalTextPosition = t;
+ firePropertyChange(HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the vertical alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: CENTER, TOP, or
+ * BOTTOM. The default is CENTER.
+ *
+ * @return The current vertical alignment
+ *
+ * @see #setVerticalAlignment(int)
+ */
+ public int getVerticalAlignment()
+ {
+ return verticalAlignment;
+ }
+
+ /**
+ * Set the vertical alignment of the button's text and icon. The
+ * alignment is a numeric constant from {@link SwingConstants}. It must
+ * be one of: CENTER, TOP, or
+ * BOTTOM. The default is CENTER.
+ *
+ * @param a The new vertical alignment
+ * @throws IllegalArgumentException If alignment is not one of the legal
+ * constants.
+ *
+ * @see #getVerticalAlignment()
+ */
+ public void setVerticalAlignment(int a)
+ {
+ if (verticalAlignment == a)
+ return;
+ if (a != TOP && a != CENTER && a != BOTTOM)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = verticalAlignment;
+ verticalAlignment = a;
+ firePropertyChange(VERTICAL_ALIGNMENT_CHANGED_PROPERTY, old, a);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the vertical position of the button's text relative to its
+ * icon. The alignment is a numeric constant from {@link
+ * SwingConstants}. It must be one of: CENTER,
+ * TOP, or BOTTOM. The default is
+ * CENTER.
+ *
+ * @return The current vertical position
+ */
+ public int getVerticalTextPosition()
+ {
+ return verticalTextPosition;
+ }
+
+ /**
+ * Set the vertical position of the button's text relative to its
+ * icon. The alignment is a numeric constant from {@link
+ * SwingConstants}. It must be one of: CENTER,
+ * TOP, or BOTTOM. The default is
+ * CENTER.
+ *
+ * @param t The new vertical position
+ * @throws IllegalArgumentException If position is not one of the legal
+ * constants.
+ */
+ public void setVerticalTextPosition(int t)
+ {
+ if (verticalTextPosition == t)
+ return;
+ if (t != TOP && t != CENTER && t != BOTTOM)
+ throw new IllegalArgumentException("Invalid alignment.");
+
+ int old = verticalTextPosition;
+ verticalTextPosition = t;
+ firePropertyChange(VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the "borderPainted" property. If set to
+ * false, the button's look and feel class should not paint
+ * a border for the button. The default is true.
+ *
+ * @return The current value of the property.
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Set the value of the "borderPainted" property. If set to
+ * false, the button's look and feel class should not paint
+ * a border for the button. The default is true.
+ *
+ * @param b The new value of the property.
+ */
+ public void setBorderPainted(boolean b)
+ {
+ clientBorderPaintedSet = true;
+ if (borderPainted == b)
+ return;
+ boolean old = borderPainted;
+ borderPainted = b;
+ firePropertyChange(BORDER_PAINTED_CHANGED_PROPERTY, old, b);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the value of the "action" property.
+ *
+ * @return The current value of the "action" property
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ *
Set the button's "action" property, subscribing the new action to the
+ * button, as an ActionListener, if it is not already subscribed. The old
+ * Action, if it exists, is unsubscribed, and the button is unsubscribed
+ * from the old Action if it was previously subscribed as a
+ * PropertyChangeListener.
+ *
+ *
This method also configures several of the button's properties from
+ * the Action, by calling {@link #configurePropertiesFromAction}, and
+ * subscribes the button to the Action as a PropertyChangeListener.
+ * Subsequent changes to the Action will thus reconfigure the button
+ * automatically.
+ *
+ * @param a The new value of the "action" property
+ */
+ public void setAction(Action a)
+ {
+ if (action != null)
+ {
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ removeActionListener(action);
+ if (actionPropertyChangeListener != null)
+ {
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ actionPropertyChangeListener = null;
+ }
+ }
+
+ Action old = action;
+ action = a;
+ configurePropertiesFromAction(action);
+ if (action != null)
+ {
+ actionPropertyChangeListener = createActionPropertyChangeListener(a);
+ action.addPropertyChangeListener(actionPropertyChangeListener);
+ addActionListener(action);
+ }
+ }
+
+ /**
+ * Return the button's default "icon" property.
+ *
+ * @return The current default icon
+ */
+ public Icon getIcon()
+ {
+ return default_icon;
+ }
+
+ /**
+ * Set the button's default "icon" property. This icon is used as a basis
+ * for the pressed and disabled icons, if none are explicitly set.
+ *
+ * @param i The new default icon
+ */
+ public void setIcon(Icon i)
+ {
+ if (default_icon == i)
+ return;
+
+ Icon old = default_icon;
+ default_icon = i;
+ firePropertyChange(ICON_CHANGED_PROPERTY, old, i);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "text" property. This property is synonymous with
+ * the "label" property.
+ *
+ * @return The current "text" property
+ */
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * Set the button's "label" property. This property is synonymous with the
+ * "text" property.
+ *
+ * @param label The new "label" property
+ *
+ * @deprecated use setText(text)
+ */
+ public void setLabel(String label)
+ {
+ setText(label);
+ }
+
+ /**
+ * Return the button's "label" property. This property is synonymous with
+ * the "text" property.
+ *
+ * @return The current "label" property
+ *
+ * @deprecated use getText()
+ */
+ public String getLabel()
+ {
+ return getText();
+ }
+
+ /**
+ * Set the button's "text" property. This property is synonymous with the
+ * "label" property.
+ *
+ * @param t The new "text" property
+ */
+ public void setText(String t)
+ {
+ if (text == t)
+ return;
+
+ String old = text;
+ text = t;
+ firePropertyChange(TEXT_CHANGED_PROPERTY, old, t);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #iconTextGap} property.
+ *
+ * @param i The new value of the property
+ *
+ * @since 1.4
+ */
+ public void setIconTextGap(int i)
+ {
+ clientIconTextGapSet = true;
+ if (iconTextGap == i)
+ return;
+
+ int old = iconTextGap;
+ iconTextGap = i;
+ firePropertyChange("iconTextGap", new Integer(old), new Integer(i));
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Get the value of the {@link #iconTextGap} property.
+ *
+ * @return The current value of the property
+ *
+ * @since 1.4
+ */
+ public int getIconTextGap()
+ {
+ return iconTextGap;
+ }
+
+ /**
+ * Return the button's "margin" property, which is an {@link Insets} object
+ * describing the distance between the button's border and its text and
+ * icon.
+ *
+ * @return The current "margin" property
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ /**
+ * Set the button's "margin" property, which is an {@link Insets} object
+ * describing the distance between the button's border and its text and
+ * icon.
+ *
+ * @param m The new "margin" property
+ */
+ public void setMargin(Insets m)
+ {
+ if (margin == m)
+ return;
+
+ Insets old = margin;
+ margin = m;
+ firePropertyChange(MARGIN_CHANGED_PROPERTY, old, m);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "pressedIcon" property. The look and feel class
+ * should paint this icon when the "pressed" property of the button's
+ * {@link ButtonModel} is true. This property may be
+ * null, in which case the default icon is used.
+ *
+ * @return The current "pressedIcon" property
+ */
+ public Icon getPressedIcon()
+ {
+ return pressed_icon;
+ }
+
+ /**
+ * Set the button's "pressedIcon" property. The look and feel class
+ * should paint this icon when the "pressed" property of the button's
+ * {@link ButtonModel} is true. This property may be
+ * null, in which case the default icon is used.
+ *
+ * @param pressedIcon The new "pressedIcon" property
+ */
+ public void setPressedIcon(Icon pressedIcon)
+ {
+ if (pressed_icon == pressedIcon)
+ return;
+
+ Icon old = pressed_icon;
+ pressed_icon = pressedIcon;
+ firePropertyChange(PRESSED_ICON_CHANGED_PROPERTY, old, pressed_icon);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "disabledIcon" property. The look and feel class
+ * should paint this icon when the "enabled" property of the button's
+ * {@link ButtonModel} is false. This property may be
+ * null, in which case an icon is constructed, based on the
+ * default icon.
+ *
+ * @return The current "disabledIcon" property
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && default_icon instanceof ImageIcon)
+ {
+ Image iconImage = ((ImageIcon) default_icon).getImage();
+ Image grayImage = GrayFilter.createDisabledImage(iconImage);
+ disabledIcon = new ImageIcon(grayImage);
+ }
+
+ return disabledIcon;
+ }
+
+ /**
+ * Set the button's "disabledIcon" property. The look and feel class should
+ * paint this icon when the "enabled" property of the button's {@link
+ * ButtonModel} is false. This property may be
+ * null, in which case an icon is constructed, based on the
+ * default icon.
+ *
+ * @param d The new "disabledIcon" property
+ */
+ public void setDisabledIcon(Icon d)
+ {
+ if (disabledIcon == d)
+ return;
+ Icon old = disabledIcon;
+ disabledIcon = d;
+ firePropertyChange(DISABLED_ICON_CHANGED_PROPERTY, old, d);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's "paintFocus" property. This property controls
+ * whether or not the look and feel class will paint a special indicator
+ * of focus state for the button. If it is false, the button still paints
+ * when focused, but no special decoration is painted to indicate the
+ * presence of focus.
+ *
+ * @return The current "paintFocus" property
+ */
+ public boolean isFocusPainted()
+ {
+ return focusPainted;
+ }
+
+ /**
+ * Set the button's "paintFocus" property. This property controls whether
+ * or not the look and feel class will paint a special indicator of focus
+ * state for the button. If it is false, the button still paints when
+ * focused, but no special decoration is painted to indicate the presence
+ * of focus.
+ *
+ * @param p The new "paintFocus" property
+ */
+ public void setFocusPainted(boolean p)
+ {
+ if (focusPainted == p)
+ return;
+
+ boolean old = focusPainted;
+ focusPainted = p;
+ firePropertyChange(FOCUS_PAINTED_CHANGED_PROPERTY, old, p);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Verifies that a particular key is one of the valid constants used for
+ * describing horizontal alignment and positioning. The valid constants
+ * are the following members of {@link SwingConstants}:
+ * RIGHT, LEFT, CENTER,
+ * LEADING or TRAILING.
+ *
+ * @param key The key to check
+ * @param exception A message to include in an IllegalArgumentException
+ *
+ * @return the value of key
+ *
+ * @throws IllegalArgumentException If key is not one of the valid constants
+ *
+ * @see #setHorizontalTextPosition(int)
+ * @see #setHorizontalAlignment(int)
+ */
+ protected int checkHorizontalKey(int key, String exception)
+ {
+ switch (key)
+ {
+ case SwingConstants.RIGHT:
+ case SwingConstants.LEFT:
+ case SwingConstants.CENTER:
+ case SwingConstants.LEADING:
+ case SwingConstants.TRAILING:
+ break;
+ default:
+ throw new IllegalArgumentException(exception);
+ }
+ return key;
+ }
+
+ /**
+ * Verifies that a particular key is one of the valid constants used for
+ * describing vertical alignment and positioning. The valid constants are
+ * the following members of {@link SwingConstants}: TOP,
+ * BOTTOM or CENTER.
+ *
+ * @param key The key to check
+ * @param exception A message to include in an IllegalArgumentException
+ *
+ * @return the value of key
+ *
+ * @throws IllegalArgumentException If key is not one of the valid constants
+ *
+ * @see #setVerticalTextPosition(int)
+ * @see #setVerticalAlignment(int)
+ */
+ protected int checkVerticalKey(int key, String exception)
+ {
+ switch (key)
+ {
+ case SwingConstants.TOP:
+ case SwingConstants.BOTTOM:
+ case SwingConstants.CENTER:
+ break;
+ default:
+ throw new IllegalArgumentException(exception);
+ }
+ return key;
+ }
+
+ /**
+ * Configure various properties of the button by reading properties
+ * of an {@link Action}. The mapping of properties is as follows:
+ *
+ *
+ *
+ *
Action keyed property
AbstractButton property
+ *
+ *
NAME
text
+ *
SMALL_ICON
icon
+ *
SHORT_DESCRIPTION
toolTipText
+ *
MNEMONIC_KEY
mnemonic
+ *
ACTION_COMMAND_KEY
actionCommand
+ *
+ *
+ *
+ *
In addition, this method always sets the button's "enabled" property to
+ * the value of the Action's "enabled" property.
+ *
+ *
If the provided Action is null, the text, icon, and
+ * toolTipText properties of the button are set to null, and
+ * the "enabled" property is set to true; the mnemonic and
+ * actionCommand properties are unchanged.
+ *
+ * @param a An Action to configure the button from
+ */
+ protected void configurePropertiesFromAction(Action a)
+ {
+ if (a == null)
+ {
+ setText(null);
+ setIcon(null);
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ else
+ {
+ setText((String) (a.getValue(Action.NAME)));
+ setIcon((Icon) (a.getValue(Action.SMALL_ICON)));
+ setEnabled(a.isEnabled());
+ setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
+ if (a.getValue(Action.MNEMONIC_KEY) != null)
+ setMnemonic(((Integer) (a.getValue(Action.MNEMONIC_KEY))).intValue());
+ String actionCommand = (String) (a.getValue(Action.ACTION_COMMAND_KEY));
+
+ // Set actionCommand to button's text by default if it is not specified
+ if (actionCommand != null)
+ setActionCommand((String) (a.getValue(Action.ACTION_COMMAND_KEY)));
+ else
+ setActionCommand(getText());
+ }
+ }
+
+ /**
+ *
A factory method which should return an {@link ActionListener} that
+ * propagates events from the button's {@link ButtonModel} to any of the
+ * button's ActionListeners. By default, this is an inner class which
+ * calls {@link AbstractButton#fireActionPerformed} with a modified copy
+ * of the incoming model {@link ActionEvent}.
+ *
+ *
The button calls this method during construction, stores the
+ * resulting ActionListener in its actionListener member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.
A factory method which should return a {@link PropertyChangeListener}
+ * that accepts changes to the specified {@link Action} and reconfigure
+ * the {@link AbstractButton}, by default using the {@link
+ * #configurePropertiesFromAction} method.
+ *
+ *
The button calls this method whenever a new Action is assigned to
+ * the button's "action" property, via {@link #setAction}, and stores the
+ * resulting PropertyChangeListener in its
+ * actionPropertyChangeListener member field. The button
+ * then subscribes the listener to the button's new action. If the
+ * button's action is changed subsequently, the listener is unsubscribed
+ * from the old action and subscribed to the new one.
+ *
+ * @param a The Action which will be listened to, and which should be
+ * the same as the source of any PropertyChangeEvents received by the
+ * new listener returned from this method.
+ *
+ * @return A new PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action a)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ if (e.getPropertyName().equals("enabled"))
+ setEnabled(act.isEnabled());
+ else if (e.getPropertyName().equals(Action.NAME))
+ setText((String) (act.getValue(Action.NAME)));
+ else if (e.getPropertyName().equals(Action.SMALL_ICON))
+ setIcon((Icon) (act.getValue(Action.SMALL_ICON)));
+ else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION))
+ setToolTipText((String) (act.getValue(Action.SHORT_DESCRIPTION)));
+ else if (e.getPropertyName().equals(Action.MNEMONIC_KEY))
+ if (act.getValue(Action.MNEMONIC_KEY) != null)
+ setMnemonic(((Integer) (act.getValue(Action.MNEMONIC_KEY)))
+ .intValue());
+ else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY))
+ setActionCommand((String) (act.getValue(Action.ACTION_COMMAND_KEY)));
+ }
+ };
+ }
+
+ /**
+ *
Factory method which creates a {@link ChangeListener}, used to
+ * subscribe to ChangeEvents from the button's model. Subclasses of
+ * AbstractButton may wish to override the listener used to subscribe to
+ * such ChangeEvents. By default, the listener just propagates the
+ * {@link ChangeEvent} to the button's ChangeListeners, via the {@link
+ * AbstractButton#fireStateChanged} method.
+ *
+ *
The button calls this method during construction, stores the
+ * resulting ChangeListener in its changeListener member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.
Factory method which creates a {@link ItemListener}, used to
+ * subscribe to ItemEvents from the button's model. Subclasses of
+ * AbstractButton may wish to override the listener used to subscribe to
+ * such ItemEvents. By default, the listener just propagates the
+ * {@link ItemEvent} to the button's ItemListeners, via the {@link
+ * AbstractButton#fireItemStateChanged} method.
+ *
+ *
The button calls this method during construction, stores the
+ * resulting ItemListener in its changeListener member
+ * field, and subscribes it to the button's model. If the button's model
+ * is changed, this listener is unsubscribed from the old model and
+ * subscribed to the new one.
+ *
+ *
Note that ItemEvents are only generated from the button's model
+ * when the model's selected property changes. If you want to
+ * subscribe to other properties of the model, you must subscribe to
+ * ChangeEvents.
+ *
+ * @return The new ItemListener
+ */
+ protected ItemListener createItemListener()
+ {
+ return getEventHandler();
+ }
+
+ /**
+ * Programmatically perform a "click" on the button: arming, pressing,
+ * waiting, un-pressing, and disarming the model.
+ */
+ public void doClick()
+ {
+ doClick(100);
+ }
+
+ /**
+ * Programmatically perform a "click" on the button: arming, pressing,
+ * waiting, un-pressing, and disarming the model.
+ *
+ * @param pressTime The number of milliseconds to wait in the pressed state
+ */
+ public void doClick(int pressTime)
+ {
+ ButtonModel mod = getModel();
+ if (mod != null)
+ {
+ mod.setArmed(true);
+ mod.setPressed(true);
+ try
+ {
+ java.lang.Thread.sleep(pressTime);
+ }
+ catch (java.lang.InterruptedException e)
+ {
+ // probably harmless
+ }
+ mod.setPressed(false);
+ mod.setArmed(false);
+ }
+ }
+
+ /**
+ * Return the button's disabled selected icon. The look and feel class
+ * should paint this icon when the "enabled" property of the button's model
+ * is false and its "selected" property is
+ * true. This icon can be null, in which case
+ * it is synthesized from the button's selected icon.
+ *
+ * @return The current disabled selected icon
+ */
+ public Icon getDisabledSelectedIcon()
+ {
+ return disabledSelectedIcon;
+ }
+
+ /**
+ * Set the button's disabled selected icon. The look and feel class
+ * should paint this icon when the "enabled" property of the button's model
+ * is false and its "selected" property is
+ * true. This icon can be null, in which case
+ * it is synthesized from the button's selected icon.
+ *
+ * @param icon The new disabled selected icon
+ */
+ public void setDisabledSelectedIcon(Icon icon)
+ {
+ if (disabledSelectedIcon == icon)
+ return;
+
+ Icon old = disabledSelectedIcon;
+ disabledSelectedIcon = icon;
+ firePropertyChange(DISABLED_SELECTED_ICON_CHANGED_PROPERTY, old, icon);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's rollover icon. The look and feel class should
+ * paint this icon when the "rolloverEnabled" property of the button is
+ * true and the mouse rolls over the button.
+ *
+ * @return The current rollover icon
+ */
+ public Icon getRolloverIcon()
+ {
+ return rolloverIcon;
+ }
+
+ /**
+ * Set the button's rollover icon and sets the rolloverEnabled
+ * property to true. The look and feel class should
+ * paint this icon when the "rolloverEnabled" property of the button is
+ * true and the mouse rolls over the button.
+ *
+ * @param r The new rollover icon
+ */
+ public void setRolloverIcon(Icon r)
+ {
+ if (rolloverIcon == r)
+ return;
+
+ Icon old = rolloverIcon;
+ rolloverIcon = r;
+ firePropertyChange(ROLLOVER_ICON_CHANGED_PROPERTY, old, rolloverIcon);
+ setRolloverEnabled(true);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's rollover selected icon. The look and feel class
+ * should paint this icon when the "rolloverEnabled" property of the button
+ * is true, the "selected" property of the button's model is
+ * true, and the mouse rolls over the button.
+ *
+ * @return The current rollover selected icon
+ */
+ public Icon getRolloverSelectedIcon()
+ {
+ return rolloverSelectedIcon;
+ }
+
+ /**
+ * Set the button's rollover selected icon and sets the
+ * rolloverEnabled property to true. The look and
+ * feel class should paint this icon when the "rolloverEnabled" property of
+ * the button is true, the "selected" property of the button's
+ * model is true, and the mouse rolls over the button.
+ *
+ * @param r The new rollover selected icon.
+ */
+ public void setRolloverSelectedIcon(Icon r)
+ {
+ if (rolloverSelectedIcon == r)
+ return;
+
+ Icon old = rolloverSelectedIcon;
+ rolloverSelectedIcon = r;
+ firePropertyChange(ROLLOVER_SELECTED_ICON_CHANGED_PROPERTY, old, r);
+ setRolloverEnabled(true);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Return the button's selected icon. The look and feel class should
+ * paint this icon when the "selected" property of the button's model is
+ * true, and either the "rolloverEnabled" property of the
+ * button is false or the mouse is not currently rolled
+ * over the button.
+ *
+ * @return The current selected icon
+ */
+ public Icon getSelectedIcon()
+ {
+ return selectedIcon;
+ }
+
+ /**
+ * Set the button's selected icon. The look and feel class should
+ * paint this icon when the "selected" property of the button's model is
+ * true, and either the "rolloverEnabled" property of the
+ * button is false or the mouse is not currently rolled
+ * over the button.
+ *
+ * @param s The new selected icon
+ */
+ public void setSelectedIcon(Icon s)
+ {
+ if (selectedIcon == s)
+ return;
+
+ Icon old = selectedIcon;
+ selectedIcon = s;
+ firePropertyChange(SELECTED_ICON_CHANGED_PROPERTY, old, s);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns an single-element array containing the "text" property of the
+ * button if the "selected" property of the button's model is
+ * true, otherwise returns null.
+ *
+ * @return The button's "selected object" array
+ */
+ public Object[] getSelectedObjects()
+ {
+ if (isSelected())
+ {
+ Object[] objs = new Object[1];
+ objs[0] = getText();
+ return objs;
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Called when image data becomes available for one of the button's icons.
+ *
+ * @param img The image being updated
+ * @param infoflags One of the constant codes in {@link ImageObserver} used
+ * to describe updated portions of an image.
+ * @param x X coordinate of the region being updated
+ * @param y Y coordinate of the region being updated
+ * @param w Width of the region beign updated
+ * @param h Height of the region being updated
+ *
+ * @return true if img is equal to the button's current icon,
+ * otherwise false
+ */
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
+ int h)
+ {
+ return current_icon == img;
+ }
+
+ /**
+ * Returns the value of the button's "contentAreaFilled" property. This
+ * property indicates whether the area surrounding the text and icon of
+ * the button should be filled by the look and feel class. If this
+ * property is false, the look and feel class should leave
+ * the content area transparent.
+ *
+ * @return The current value of the "contentAreaFilled" property
+ */
+ public boolean isContentAreaFilled()
+ {
+ return contentAreaFilled;
+ }
+
+ /**
+ * Sets the value of the button's "contentAreaFilled" property. This
+ * property indicates whether the area surrounding the text and icon of
+ * the button should be filled by the look and feel class. If this
+ * property is false, the look and feel class should leave
+ * the content area transparent.
+ *
+ * @param b The new value of the "contentAreaFilled" property
+ */
+ public void setContentAreaFilled(boolean b)
+ {
+ clientContentAreaFilledSet = true;
+ if (contentAreaFilled == b)
+ return;
+
+ // The JDK sets the opaque property to the value of the contentAreaFilled
+ // property, so should we do.
+ setOpaque(b);
+ boolean old = contentAreaFilled;
+ contentAreaFilled = b;
+ firePropertyChange(CONTENT_AREA_FILLED_CHANGED_PROPERTY, old, b);
+ }
+
+ /**
+ * Paints the button's border, if the button's "borderPainted" property is
+ * true, by out calling to the button's look and feel class.
+ *
+ * @param g The graphics context used to paint the border
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (isBorderPainted())
+ super.paintBorder(g);
+ }
+
+ /**
+ * Returns a string, used only for debugging, which identifies or somehow
+ * represents this button. The exact value is implementation-defined.
+ *
+ * @return A string representation of the button
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",defaultIcon=");
+ if (getIcon() != null)
+ sb.append(getIcon());
+ sb.append(",disabledIcon=");
+ if (getDisabledIcon() != null)
+ sb.append(getDisabledIcon());
+ sb.append(",disabledSelectedIcon=");
+ if (getDisabledSelectedIcon() != null)
+ sb.append(getDisabledSelectedIcon());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(getMargin());
+ sb.append(",paintBorder=").append(isBorderPainted());
+ sb.append(",paintFocus=").append(isFocusPainted());
+ sb.append(",pressedIcon=");
+ if (getPressedIcon() != null)
+ sb.append(getPressedIcon());
+ sb.append(",rolloverEnabled=").append(isRolloverEnabled());
+ sb.append(",rolloverIcon=");
+ if (getRolloverIcon() != null)
+ sb.append(getRolloverIcon());
+ sb.append(",rolloverSelected=");
+ if (getRolloverSelectedIcon() != null)
+ sb.append(getRolloverSelectedIcon());
+ sb.append(",selectedIcon=");
+ if (getSelectedIcon() != null)
+ sb.append(getSelectedIcon());
+ sb.append(",text=");
+ if (getText() != null)
+ sb.append(getText());
+ return sb.toString();
+ }
+
+ /**
+ * Set the "UI" property of the button, which is a look and feel class
+ * responsible for handling the button's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(ButtonUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Set the "UI" property of the button, which is a look and feel class
+ * responsible for handling the button's input events and painting it.
+ *
+ * @return The current "UI" property
+ */
+ public ButtonUI getUI()
+ {
+ return (ButtonUI) ui;
+ }
+
+ /**
+ * Set the "UI" property to a class constructed, via the {@link
+ * UIManager}, from the current look and feel. This should be overridden
+ * for each subclass of AbstractButton, to retrieve a suitable {@link
+ * ButtonUI} look and feel class.
+ */
+ public void updateUI()
+ {
+ // TODO: What to do here?
+ }
+
+ /**
+ * Returns the current time in milliseconds in which clicks gets coalesced
+ * into a single ActionEvent.
+ *
+ * @return the time in milliseconds
+ *
+ * @since 1.4
+ */
+ public long getMultiClickThreshhold()
+ {
+ return multiClickThreshhold;
+ }
+
+ /**
+ * Sets the time in milliseconds in which clicks gets coalesced into a single
+ * ActionEvent.
+ *
+ * @param threshhold the time in milliseconds
+ *
+ * @since 1.4
+ */
+ public void setMultiClickThreshhold(long threshhold)
+ {
+ if (threshhold < 0)
+ throw new IllegalArgumentException();
+
+ multiClickThreshhold = threshhold;
+ }
+
+ /**
+ * Adds the specified component to this AbstractButton. This overrides the
+ * default in order to install an {@link OverlayLayout} layout manager
+ * before adding the component. The layout manager is only installed if
+ * no other layout manager has been installed before.
+ *
+ * @param comp the component to be added
+ * @param constraints constraints for the layout manager
+ * @param index the index at which the component is added
+ *
+ * @since 1.5
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // We use a client property here, so that no extra memory is used in
+ // the common case with no layout manager.
+ if (getClientProperty("AbstractButton.customLayoutSet") == null)
+ setLayout(new OverlayLayout(this));
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * Sets a layout manager on this AbstractButton. This is overridden in order
+ * to detect if the application sets a custom layout manager. If no custom
+ * layout manager is set, {@link #addImpl(Component, Object, int)} installs
+ * an OverlayLayout before adding a component.
+ *
+ * @param layout the layout manager to install
+ *
+ * @since 1.5
+ */
+ public void setLayout(LayoutManager layout)
+ {
+ // We use a client property here, so that no extra memory is used in
+ // the common case with no layout manager.
+ putClientProperty("AbstractButton.customLayoutSet", Boolean.TRUE);
+ super.setLayout(layout);
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("borderPainted"))
+ {
+ if (! clientBorderPaintedSet)
+ {
+ setBorderPainted(((Boolean) value).booleanValue());
+ clientBorderPaintedSet = false;
+ }
+ }
+ else if (propertyName.equals("rolloverEnabled"))
+ {
+ if (! clientRolloverEnabledSet)
+ {
+ setRolloverEnabled(((Boolean) value).booleanValue());
+ clientRolloverEnabledSet = false;
+ }
+ }
+ else if (propertyName.equals("iconTextGap"))
+ {
+ if (! clientIconTextGapSet)
+ {
+ setIconTextGap(((Integer) value).intValue());
+ clientIconTextGapSet = false;
+ }
+ }
+ else if (propertyName.equals("contentAreaFilled"))
+ {
+ if (! clientContentAreaFilledSet)
+ {
+ setContentAreaFilled(((Boolean) value).booleanValue());
+ clientContentAreaFilledSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+
+ /**
+ * Returns the combined event handler. The instance is created if
+ * necessary.
+ *
+ * @return the combined event handler
+ */
+ EventHandler getEventHandler()
+ {
+ if (eventHandler == null)
+ eventHandler = new EventHandler();
+ return eventHandler;
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractCellEditor.java b/libjava/classpath/javax/swing/AbstractCellEditor.java
new file mode 100644
index 000000000..eade00c07
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractCellEditor.java
@@ -0,0 +1,195 @@
+/* AbstractCellEditor.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.EventObject;
+
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+
+/**
+ * An abstract superclass for table and tree cell editors. This provides some
+ * common shared functionality.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractCellEditor
+ implements CellEditor, Serializable
+{
+ private static final long serialVersionUID = -1048006551406220959L;
+
+ /**
+ * Our Swing event listeners.
+ */
+ protected EventListenerList listenerList;
+
+ /**
+ * The cached ChangeEvent.
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * Creates a new instance of AbstractCellEditor.
+ */
+ public AbstractCellEditor()
+ {
+ listenerList = new EventListenerList();
+ changeEvent = new ChangeEvent(this);
+ }
+
+ /**
+ * Returns true if the cell is editable using
+ * event, false
+ * if it's not. The default behaviour is to return true.
+ *
+ * @param event an event
+ *
+ * @return true if the cell is editable using
+ * event, false if it's not
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ return true;
+ }
+
+ /**
+ * Returns true if the editing cell should be selected,
+ * false otherwise. This is usually returning true,
+ * but in some special cases it might be useful not to switch cell selection
+ * when editing one cell.
+ *
+ * @param event an event
+ *
+ * @return true if the editing cell should be selected,
+ * false otherwise
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ return true;
+ }
+
+ /**
+ * Stop editing the cell and accept any partial value that has been entered
+ * into the cell.
+ *
+ * @return true if editing has been stopped successfully,
+ * falseotherwise
+ */
+ public boolean stopCellEditing()
+ {
+ fireEditingStopped();
+ return true;
+ }
+
+ /**
+ * Stop editing the cell and do not accept any partial value that has
+ * been entered into the cell.
+ */
+ public void cancelCellEditing()
+ {
+ fireEditingCanceled();
+ }
+
+ /**
+ * Adds a CellEditorListener to the list of CellEditorListeners of this
+ * CellEditor.
+ *
+ * @param listener the CellEditorListener to add
+ */
+ public void addCellEditorListener(CellEditorListener listener)
+ {
+ listenerList.add(CellEditorListener.class, listener);
+ }
+
+ /**
+ * Removes the specified CellEditorListener from the list of the
+ * CellEditorListeners of this CellEditor.
+ *
+ * @param listener the CellEditorListener to remove
+ */
+ public void removeCellEditorListener(CellEditorListener listener)
+ {
+ listenerList.remove(CellEditorListener.class, listener);
+ }
+
+ /**
+ * Returns the list of CellEditorListeners that have been registered
+ * in this CellEditor.
+ *
+ * @return the list of CellEditorListeners that have been registered
+ * in this CellEditor
+ *
+ * @since 1.4
+ */
+ public CellEditorListener[] getCellEditorListeners()
+ {
+ return (CellEditorListener[]) listenerList.getListeners(
+ CellEditorListener.class);
+ }
+
+ /**
+ * Notifies all registered listeners that the editing of the cell has has been
+ * stopped.
+ */
+ protected void fireEditingStopped()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ {
+ listeners[index].editingStopped(changeEvent);
+ }
+ }
+
+ /**
+ * Notifies all registered listeners that the editing of the cell has
+ * has been canceled.
+ */
+ protected void fireEditingCanceled()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ {
+ listeners[index].editingCanceled(changeEvent);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractListModel.java b/libjava/classpath/javax/swing/AbstractListModel.java
new file mode 100644
index 000000000..72aefbcd5
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractListModel.java
@@ -0,0 +1,181 @@
+/* AbstractListModel.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * Provides standard implementations of some methods in {@link ListModel}.
+ *
+ * @author Ronald Veldema
+ * @author Andrew Selkirk
+ */
+public abstract class AbstractListModel implements ListModel, Serializable
+{
+ private static final long serialVersionUID = -3285184064379168730L;
+
+ /** List of ListDataListeners called for each change to the list. */
+ protected EventListenerList listenerList;
+
+ /**
+ * Creates a new model instance - initialises the event listener list.
+ */
+ public AbstractListModel()
+ {
+ listenerList = new EventListenerList();
+ }
+
+ /**
+ * Add a listener object to this model. The listener will be called
+ * any time the set of elements in the model is changed.
+ *
+ * @param listener The listener to add
+ */
+ public void addListDataListener(ListDataListener listener)
+ {
+ listenerList.add(ListDataListener.class, listener);
+ }
+
+ /**
+ * Add a listener object to this model. The listener will no longer be
+ * called when the set of elements in the model is changed.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeListDataListener(ListDataListener listener)
+ {
+ listenerList.remove(ListDataListener.class, listener);
+ }
+
+ /**
+ * Call {@link ListDataListener#contentsChanged} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#CONTENTS_CHANGED} and represents a
+ * change to the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically this
+ * @param startIndex The index of the first element which changed
+ * @param endIndex The index of the last element which changed
+ */
+ protected void fireContentsChanged(Object source, int startIndex,
+ int endIndex)
+ {
+ ListDataEvent event = new ListDataEvent(source, ListDataEvent.CONTENTS_CHANGED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].contentsChanged(event);
+ }
+
+ /**
+ * Call {@link ListDataListener#intervalAdded} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#INTERVAL_ADDED} and represents an
+ * addition of the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically this
+ * @param startIndex The index of the first new element
+ * @param endIndex The index of the last new element
+ */
+ protected void fireIntervalAdded(Object source, int startIndex, int endIndex)
+ {
+ ListDataEvent event =
+ new ListDataEvent(source, ListDataEvent.INTERVAL_ADDED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].intervalAdded(event);
+ }
+
+ /**
+ * Call {@link ListDataListener#intervalRemoved} on each element of the
+ * {@link #listenerList} which is a {@link ListDataListener}. The event
+ * fired has type {@link ListDataEvent#INTERVAL_REMOVED} and represents a
+ * removal of the data elements in the range [startIndex, endIndex]
+ * inclusive.
+ *
+ * @param source The source of the change, typically this
+ * @param startIndex The index of the first element removed
+ * @param endIndex The index of the last element removed
+ */
+ protected void fireIntervalRemoved(Object source, int startIndex,
+ int endIndex)
+ {
+ ListDataEvent event =
+ new ListDataEvent(source, ListDataEvent.INTERVAL_REMOVED,
+ startIndex, endIndex);
+ ListDataListener[] listeners = getListDataListeners();
+
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].intervalRemoved(event);
+ }
+
+ /**
+ * Return the subset of {@link EventListener} objects found in this
+ * object's {@link #listenerList} which are elements of the specified
+ * type.
+ *
+ * @param listenerType The type of listeners to select
+ *
+ * @return The set of listeners of the specified type
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * A synonym for getListeners(ListDataListener.class).
+ *
+ * @return The set of ListDataListeners found in the {@link #listenerList}
+ */
+ public ListDataListener[] getListDataListeners()
+ {
+ return (ListDataListener[]) getListeners(ListDataListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/AbstractSpinnerModel.java b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
new file mode 100644
index 000000000..984e52af2
--- /dev/null
+++ b/libjava/classpath/javax/swing/AbstractSpinnerModel.java
@@ -0,0 +1,123 @@
+/* AbstractSpinnerModel.java --
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * Provides standard implementations for some of the methods in
+ * {@link SpinnerModel}.
+ *
+ * @since 1.4
+ *
+ * @author Ka-Hing Cheung
+ */
+public abstract class AbstractSpinnerModel implements SpinnerModel
+{
+ private ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /** Stores the listeners registered with the model. */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Creates an AbstractSpinnerModel.
+ */
+ public AbstractSpinnerModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Registers a ChangeListener with the model so that it will
+ * receive {@link ChangeEvent} notifications when the model changes.
+ *
+ * @param listener the listener to add (null is ignored).
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Gets all the listeners that are of a particular type.
+ *
+ * @param c the type of listener
+ * @return the listeners that are of the specific type
+ */
+ public T[] getListeners(Class c)
+ {
+ return listenerList.getListeners(c);
+ }
+
+ /**
+ * Gets all the ChangeListeners.
+ *
+ * @return all the ChangeListeners
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Remove a particular listener.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Fires a ChangeEvent to all the ChangeListeners
+ * added to this model
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(changeEvent);
+ }
+}
diff --git a/libjava/classpath/javax/swing/Action.java b/libjava/classpath/javax/swing/Action.java
new file mode 100644
index 000000000..fa1925f45
--- /dev/null
+++ b/libjava/classpath/javax/swing/Action.java
@@ -0,0 +1,153 @@
+/* Action.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeListener;
+
+/**
+ * Provides a convenient central point of control for some task
+ * that can be triggered by more than one control in a Swing user interface
+ * (for example, a menu item and a toolbar button).
+ *
+ * @see AbstractButton#setAction(Action)
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Andrew Selkirk
+ */
+public interface Action extends ActionListener {
+
+ /**
+ * A key to access the default property for the action (this is not used).
+ */
+ String DEFAULT = "Default";
+
+ /**
+ * A key to access the long description for the action.
+ */
+ String LONG_DESCRIPTION = "LongDescription";
+
+ /**
+ * A key to access the name for the action.
+ */
+ String NAME = "Name";
+
+ /**
+ * A key to access the short description for the action (the short
+ * description is typically used as the tool tip text).
+ */
+ String SHORT_DESCRIPTION = "ShortDescription";
+
+ /**
+ * A key to access the icon for the action.
+ */
+ String SMALL_ICON = "SmallIcon";
+
+ /**
+ * A key to access the {@link KeyStroke} used as the accelerator for the
+ * action.
+ */
+ String ACCELERATOR_KEY = "AcceleratorKey";
+
+ /**
+ * A key to access the action command string for the action.
+ */
+ String ACTION_COMMAND_KEY = "ActionCommandKey";
+
+ /**
+ * A key to access the mnemonic for the action.
+ */
+ String MNEMONIC_KEY = "MnemonicKey";
+
+ /**
+ * Returns the value associated with the specified key.
+ *
+ * @param key the key (not null).
+ *
+ * @return The value associated with the specified key, or
+ * null if the key is not found.
+ */
+ Object getValue(String key);
+
+ /**
+ * Sets the value associated with the specified key and sends a
+ * {@link java.beans.PropertyChangeEvent} to all registered listeners.
+ * The standard keys are defined in this interface: {@link #NAME},
+ * {@link #SHORT_DESCRIPTION}, {@link #LONG_DESCRIPTION},
+ * {@link #SMALL_ICON}, {@link #ACTION_COMMAND_KEY},
+ * {@link #ACCELERATOR_KEY} and {@link #MNEMONIC_KEY}. Any existing value
+ * associated with the key will be overwritten.
+ *
+ * @param key the key (not null).
+ * @param value the value (null permitted).
+ */
+ void putValue(String key, Object value);
+
+ /**
+ * Returns the flag that indicates whether or not this action is enabled.
+ *
+ * @return The flag.
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets the flag that indicates whether or not this action is enabled. If
+ * the value changes, a {@link java.beans.PropertyChangeEvent} is sent to
+ * all registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ void setEnabled(boolean b);
+
+ /**
+ * Registers a listener to receive notification whenever one of the
+ * action's properties is modified.
+ *
+ * @param listener the listener.
+ */
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * changes to the action's properties.
+ *
+ * @param listener the listener.
+ */
+ void removePropertyChangeListener(PropertyChangeListener listener);
+
+} // Action
diff --git a/libjava/classpath/javax/swing/ActionMap.java b/libjava/classpath/javax/swing/ActionMap.java
new file mode 100644
index 000000000..0d6706c3b
--- /dev/null
+++ b/libjava/classpath/javax/swing/ActionMap.java
@@ -0,0 +1,195 @@
+/* ActionMap.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * Maps arbitrary keys (usually Strings) to {@link Action} instances. This
+ * is used in combination with {@link InputMap}s.
+ *
+ * If a component receives an input event, this is looked up in
+ * the component's InputMap. The result is an object which
+ * serves as a key to the components ActionMap. Finally
+ * the Action that is stored is executed.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ */
+public class ActionMap
+ implements Serializable
+{
+ private static final long serialVersionUID = -6277518704513986346L;
+
+ /**
+ * actionMap
+ */
+ private Map actionMap = new HashMap();
+
+ /**
+ * parent
+ */
+ private ActionMap parent;
+
+ /**
+ * Creates a new ActionMap instance.
+ */
+ public ActionMap()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns an action associated with an object.
+ *
+ * @param key the key of the enty
+ *
+ * @return the action associated with key, may be null
+ */
+ public Action get(Object key)
+ {
+ Object result = actionMap.get(key);
+
+ if (result == null && parent != null)
+ result = parent.get(key);
+
+ return (Action) result;
+ }
+
+ /**
+ * Puts a new Action into the ActionMap.
+ * If action is null an existing entry will be removed.
+ *
+ * @param key the key for the entry
+ * @param action the action.
+ */
+ public void put(Object key, Action action)
+ {
+ if (action == null)
+ actionMap.remove(key);
+ else
+ actionMap.put(key, action);
+ }
+
+ /**
+ * Remove an entry from the ActionMap.
+ *
+ * @param key the key of the entry to remove
+ */
+ public void remove(Object key)
+ {
+ actionMap.remove(key);
+ }
+
+ /**
+ * Returns the parent of this ActionMap.
+ *
+ * @return the parent, may be null.
+ */
+ public ActionMap getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Sets a parent for this ActionMap.
+ *
+ * @param parentMap the new parent
+ */
+ public void setParent(ActionMap parentMap)
+ {
+ if (parentMap != this)
+ parent = parentMap;
+ }
+
+ /**
+ * Returns the number of entries in this ActionMap.
+ *
+ * @return the number of entries
+ */
+ public int size()
+ {
+ return actionMap.size();
+ }
+
+ /**
+ * Clears the ActionMap.
+ */
+ public void clear()
+ {
+ actionMap.clear();
+ }
+
+ /**
+ * Returns all keys of entries in this ActionMap.
+ *
+ * @return an array of keys
+ */
+ public Object[] keys()
+ {
+ if (size() != 0)
+ return actionMap.keySet().toArray();
+ return null;
+ }
+
+ /**
+ * Returns all keys of entries in this ActionMap
+ * and all its parents.
+ *
+ * @return an array of keys
+ */
+ public Object[] allKeys()
+ {
+ Set set = new HashSet();
+
+ if (parent != null)
+ set.addAll(Arrays.asList(parent.allKeys()));
+
+ set.addAll(actionMap.keySet());
+ if (set.size() != 0)
+ return set.toArray();
+ return null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/BorderFactory.java b/libjava/classpath/javax/swing/BorderFactory.java
new file mode 100644
index 000000000..f5c4cbbb6
--- /dev/null
+++ b/libjava/classpath/javax/swing/BorderFactory.java
@@ -0,0 +1,456 @@
+/* BorderFactory.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import javax.swing.border.BevelBorder;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.EmptyBorder;
+import javax.swing.border.EtchedBorder;
+import javax.swing.border.LineBorder;
+import javax.swing.border.MatteBorder;
+import javax.swing.border.TitledBorder;
+
+/**
+ * A factory for commonly used borders.
+ *
+ * @author original author unknown
+ */
+public class BorderFactory
+{
+ private BorderFactory()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Creates a line border withe the specified color.
+ *
+ * @param color A color to use for the line.
+ *
+ * @return The Border object
+ */
+ public static Border createLineBorder(Color color)
+ {
+ return createLineBorder(color, 1);
+ }
+
+ /**
+ * Creates a line border withe the specified color and width. The width
+ * applies to all 4 sides of the border. To specify widths individually for
+ * the top, bottom, left, and right, use
+ * createMatteBorder(int,int,int,int,Color).
+ *
+ * @param color A color to use for the line.
+ * @param thickness An int specifying the width in pixels.
+ *
+ * @return The Border object
+ */
+ public static Border createLineBorder(Color color, int thickness)
+ {
+ return new LineBorder(color, thickness);
+ }
+
+ /**
+ * Created a border with a raised beveled edge, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a raised border, highlights are on top and
+ * shadows are underneath.)
+ *
+ * @return The Border object
+ */
+ public static Border createRaisedBevelBorder()
+ {
+ return new BevelBorder(BevelBorder.RAISED);
+ }
+
+ /**
+ * Created a border with a lowered beveled edge, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a lowered border, shadows are on top and
+ * highlights are underneath.)
+ *
+ * @return The Border object
+ */
+ public static Border createLoweredBevelBorder()
+ {
+ return new BevelBorder(BevelBorder.LOWERED);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using brighter shades of
+ * the component's current background color for highlighting, and darker
+ * shading for shadows. (In a lowered border, shadows are on top and
+ * highlights are underneath.).
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type)
+ {
+ return new BevelBorder(type);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using the specified
+ * highlighting and shadowing. The outer edge of the highlighted area uses
+ * a brighter shade of the highlight color. The inner edge of the shadow
+ * area uses a brighter shade of the shadaw color.
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ * @param highlight A Color object for highlights
+ * @param shadow A Color object for shadows
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type, Color highlight, Color shadow)
+ {
+ return new BevelBorder(type, highlight, shadow);
+ }
+
+ /**
+ * Create a beveled border of the specified type, using the specified colors
+ * for the inner and outer highlight and shadow areas.
+ *
+ * @param type An int specifying either BevelBorder.LOWERED or
+ * BevelBorder.RAISED
+ * @param highlightOuter A Color object for the outer edge of the
+ * highlight area
+ * @param highlightInner A Color object for the inner edge of the
+ * highlight area
+ * @param shadowOuter A Color object for the outer edge of the shadow area
+ * @param shadowInner A Color object for the inner edge of the shadow area
+ *
+ * @return The Border object
+ */
+ public static Border createBevelBorder(int type, Color highlightOuter,
+ Color highlightInner,
+ Color shadowOuter, Color shadowInner)
+ {
+ return new BevelBorder(type, highlightOuter, highlightInner, shadowOuter,
+ shadowInner);
+ }
+
+ /**
+ * Create a border with an "etched" look using the component's current
+ * background color for highlighting and shading.
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder()
+ {
+ return new EtchedBorder();
+ }
+
+ /**
+ * Create a border with an "etched" look using the component's current
+ * background color for highlighting and shading.
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(int etchType)
+ {
+ return new EtchedBorder(etchType);
+ }
+
+ /**
+ * Create a border with an "etched" look using the specified highlighting and
+ * shading colors.
+ *
+ * @param highlight A Color object for the border highlights
+ * @param shadow A Color object for the border shadows
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(Color highlight, Color shadow)
+ {
+ return new EtchedBorder(highlight, shadow);
+ }
+
+ /**
+ * Create a border with an "etched" look using the specified highlighting and
+ * shading colors.
+ *
+ * @param highlight A Color object for the border highlights
+ * @param shadow A Color object for the border shadows
+ *
+ * @return The Border object
+ */
+ public static Border createEtchedBorder(int etchType, Color highlight,
+ Color shadow)
+ {
+ return new EtchedBorder(etchType, highlight, shadow);
+ }
+
+ /**
+ * Create a new title border specifying the text of the title, using the
+ * default border (etched), using the default text position (sitting on the
+ * top line) and default justification (left) and using the default font and
+ * text color determined by the current look and feel.
+ *
+ * @param title A String containing the text of the title
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(String title)
+ {
+ return new TitledBorder(title);
+ }
+
+ /**
+ * Create a new title border with an empty title specifying the border
+ * object, using the default text position (sitting on the top line) and
+ * default justification (left) and using the default font, text color,
+ * and border determined by the current look and feel. (The Motif and Windows
+ * look and feels use an etched border; The Java look and feel use a
+ * gray border.)
+ *
+ * @param border The Border object to add the title to
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border)
+ {
+ return new TitledBorder(border);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title, using
+ * the default positioning (sitting on the top line) and default
+ * justification (left) and using the default font and text color determined
+ * by the current look and feel.
+ *
+ * @param border The Border object to add the title to
+ * @param title A String containing the text of the title
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title)
+ {
+ return new TitledBorder(border, title);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning, using the default font and text color determined by
+ * the current look and feel.
+ *
+ * @param border The Border object to add the title to
+ * @param title A String containing the text of the title
+ * @param titleJustification An int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition An int specifying the vertical position of the text
+ * in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION
+ * (top).
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning and font, using the default text color determined by
+ * the current look and feel.
+ *
+ * @param border - the Border object to add the title to
+ * @param title - a String containing the text of the title
+ * @param titleJustification - an int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition - an int specifying the vertical position of the
+ * text in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION (top).
+ * @param titleFont - a Font object specifying the title font
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition,
+ Font titleFont)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition,
+ titleFont);
+ }
+
+ /**
+ * Add a title to an existing border, specifying the text of the title along
+ * with its positioning, font, and color.
+ *
+ * @param border - the Border object to add the title to
+ * @param title - a String containing the text of the title
+ * @param titleJustification - an int specifying the left/right position of
+ * the title -- one of TitledBorder.LEFT, TitledBorder.CENTER, or
+ * TitledBorder.RIGHT, TitledBorder.DEFAULT_JUSTIFICATION (left).
+ * @param titlePosition - an int specifying the vertical position of the text
+ * in relation to the border -- one of: TitledBorder.ABOVE_TOP,
+ * TitledBorder.TOP (sitting on the top line), TitledBorder.BELOW_TOP,
+ * TitledBorder.ABOVE_BOTTOM, TitledBorder.BOTTOM (sitting on the bottom
+ * line), TitledBorder.BELOW_BOTTOM, or TitledBorder.DEFAULT_POSITION (top).
+ * @param titleFont - a Font object specifying the title font
+ * @param titleColor - a Color object specifying the title color
+ *
+ * @return The TitledBorder object
+ */
+ public static TitledBorder createTitledBorder(Border border, String title,
+ int titleJustification,
+ int titlePosition,
+ Font titleFont, Color titleColor)
+ {
+ return new TitledBorder(border, title, titleJustification, titlePosition,
+ titleFont, titleColor);
+ }
+
+ /**
+ * Creates an empty border that takes up no space. (The width of the top,
+ * bottom, left, and right sides are all zero.)
+ *
+ * @return The Border object
+ */
+ public static Border createEmptyBorder()
+ {
+ return new EmptyBorder(0, 0, 0, 0);
+ }
+
+ /**
+ * Creates an empty border that takes up no space but which does no drawing,
+ * specifying the width of the top, left, bottom, and right sides.
+ *
+ * @param top An int specifying the width of the top in pixels
+ * @param left An int specifying the width of the left side in pixels
+ * @param bottom An int specifying the width of the right side in pixels
+ * @param right An int specifying the width of the bottom in pixels
+ *
+ * @return The Border object
+ */
+ public static Border createEmptyBorder(int top, int left, int bottom,
+ int right)
+ {
+ return new EmptyBorder(top, left, bottom, right);
+ }
+
+ /**
+ * Create a compound border with a null inside edge and a null outside edge.
+ *
+ * @return The CompoundBorder object
+ */
+ public static CompoundBorder createCompoundBorder()
+ {
+ return new CompoundBorder();
+ }
+
+ /**
+ * Create a compound border specifying the border objects to use for the
+ * outside and inside edges.
+ *
+ * @param outsideBorder A Border object for the outer edge of the
+ * compound border
+ * @param insideBorder A Border object for the inner edge of the
+ * compound border
+ *
+ * @return The CompoundBorder object
+ */
+ public static CompoundBorder createCompoundBorder(Border outsideBorder,
+ Border insideBorder)
+ {
+ return new CompoundBorder(outsideBorder, insideBorder);
+ }
+
+ /**
+ * Create a matte-look border using a solid color. (The difference between
+ * this border and a line border is that you can specify the individual border
+ * dimensions.)
+ *
+ * @param top
+ * An int specifying the width of the top in pixels
+ * @param left
+ * An int specifying the width of the left side in pixels
+ * @param bottom
+ * An int specifying the width of the right side in pixels
+ * @param right
+ * An int specifying the width of the bottom in pixels
+ * @param color
+ * A Color to use for the border
+ * @return The MatteBorder object
+ */
+ public static MatteBorder createMatteBorder(int top, int left, int bottom,
+ int right, Color color)
+ {
+ return new MatteBorder(top, left, bottom, right, color);
+ }
+
+ /**
+ * Create a matte-look border that consists of multiple tiles of a specified
+ * icon. Multiple copies of the icon are placed side-by-side to fill up the
+ * border area.
+ *
+ * Note:
+ * If the icon doesn't load, the border area is painted gray.
+ *
+ * @param top An int specifying the width of the top in pixels
+ * @param left An int specifying the width of the left side in pixels
+ * @param bottom An int specifying the width of the right side in pixels
+ * @param right An int specifying the width of the bottom in pixels
+ * @param tileIcon The Icon object used for the border tiles
+ *
+ * @return The MatteBorder object
+ */
+ public static MatteBorder createMatteBorder(int top, int left, int bottom,
+ int right, Icon tileIcon)
+ {
+ return new MatteBorder(top, left, bottom, right, tileIcon);
+ }
+}
diff --git a/libjava/classpath/javax/swing/BoundedRangeModel.java b/libjava/classpath/javax/swing/BoundedRangeModel.java
new file mode 100644
index 000000000..87fec5f0f
--- /dev/null
+++ b/libjava/classpath/javax/swing/BoundedRangeModel.java
@@ -0,0 +1,193 @@
+/* BoundedRangeModel.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * The data model that represents a range that is constrained to fit
+ * within specified bounds. The range is defined as value
+ * to value + extent, where both value and
+ * extent are integers, and extent >= 0. The bounds
+ * are defined by integers minimum and maximum.
+ *
+ * This type of model is used in components that display a range of values,
+ * like {@link JProgressBar} and {@link JSlider}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface BoundedRangeModel
+{
+ /**
+ * Returns the current value for the model.
+ *
+ * @return The current value for the model.
+ *
+ * @see #setValue(int)
+ */
+ int getValue();
+
+ /**
+ * Sets the value for the model and sends a {@link ChangeEvent} to
+ * all registered listeners. The new value must satisfy the constraint
+ * min <= value <= value + extent <= max.
+ *
+ * @param value the value
+ *
+ * @see #getValue()
+ */
+ void setValue(int value);
+
+ /**
+ * Returns the lower bound for the model. The start of the model's range
+ * (see {@link #getValue()}) cannot be less than this lower bound.
+ *
+ * @return The lower bound for the model.
+ *
+ * @see #setMinimum(int)
+ * @see #getMaximum()
+ */
+ int getMinimum();
+
+ /**
+ * Sets the lower bound for the model and sends a {@link ChangeEvent} to all
+ * registered listeners. The new minimum must be less than or equal to the
+ * start value of the model's range (as returned by {@link #getValue()}).
+ *
+ * @param minimum the minimum value
+ *
+ * @see #getMinimum()
+ */
+ void setMinimum(int minimum);
+
+ /**
+ * Returns the upper bound for the model. This sets an upper limit for the
+ * end value of the model's range ({@link #getValue()} +
+ * {@link #getExtent()}).
+ *
+ * @return The upper bound for the model.
+ *
+ * @see #setMaximum(int)
+ * @see #getMinimum()
+ */
+ int getMaximum();
+
+ /**
+ * Sets the upper bound for the model and sends a {@link ChangeEvent} to all
+ * registered listeners. The new maximum must be greater than or equal to the
+ * end value of the model's range (as returned by {@link #getValue()} +
+ * {@link #getExtent()}).
+ *
+ * @param maximum the maximum value
+ *
+ * @see #getMaximum()
+ */
+ void setMaximum(int maximum);
+
+ /**
+ * Returns the value of the valueIsAdjusting property.
+ *
+ * @return true if value is adjusting,
+ * otherwise false
+ *
+ * @see #setValueIsAdjusting(boolean)
+ */
+ boolean getValueIsAdjusting();
+
+ /**
+ * Sets the valueIsAdjusting property.
+ *
+ * @param adjusting true if adjusting,
+ * false otherwise
+ *
+ * @see #getValueIsAdjusting()
+ */
+ void setValueIsAdjusting(boolean adjusting);
+
+ /**
+ * Returns the current extent.
+ *
+ * @return the extent
+ *
+ * @see #setExtent(int)
+ */
+ int getExtent();
+
+ /**
+ * Sets the extent, which is the length of the model's range, and sends a
+ * {@link ChangeEvent} to all registered listeners.
+ *
+ * @param extent the extent
+ *
+ * @see #getExtent()
+ */
+ void setExtent(int extent);
+
+ /**
+ * Sets all the properties for the model in a single call.
+ *
+ * @param value the value
+ * @param extent the extent
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param adjusting a flag that indicates the model is being adjusted
+ * continuously.
+ */
+ void setRangeProperties(int value, int extent, int minimum, int maximum,
+ boolean adjusting);
+
+ /**
+ * Adds a ChangeListener to this object.
+ *
+ * @param listener the listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Removes a ChangeListener from this object.
+ *
+ * @param listener the listener to remove
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ void removeChangeListener(ChangeListener listener);
+}
diff --git a/libjava/classpath/javax/swing/Box.java b/libjava/classpath/javax/swing/Box.java
new file mode 100644
index 000000000..ce9cb8f20
--- /dev/null
+++ b/libjava/classpath/javax/swing/Box.java
@@ -0,0 +1,290 @@
+/* Box.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTError;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.LayoutManager;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A component that uses a {@link BoxLayout} as Layout Manager.
+ *
+ * In addition to that, this class provides a set of static methods for
+ * creating some filler components ('struts' and 'glue') for use in
+ * containers that are laid out using BoxLayout.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class Box extends JComponent implements Accessible
+{
+ private static final long serialVersionUID = 1525417495883046342L;
+
+ /**
+ * Provides accessibility support for Boxes.
+ */
+ protected class AccessibleBox extends Container.AccessibleAWTContainer
+ {
+ private static final long serialVersionUID = -7775079816389931944L;
+
+ protected AccessibleBox()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return null;
+ }
+ }
+
+ /**
+ * A component that servers as a filler in BoxLayout controlled containers.
+ */
+ public static class Filler extends JComponent implements Accessible
+ {
+ private static final long serialVersionUID = -1204263191910183998L;
+
+ /**
+ * Provides accessibility support for Box.Filler.
+ */
+ protected class AccessibleBoxFiller
+ extends Component.AccessibleAWTComponent
+ {
+ private static final long serialVersionUID = 164963348357479321L;
+
+ protected AccessibleBoxFiller()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return null;
+ }
+ }
+
+ private transient Dimension min, pref, max;
+
+ /**
+ * Creates a new instance of Filler.
+ *
+ * @param min the minimum size of the filler.
+ * @param pref the preferred size of the filler.
+ * @param max the maximum size of the filler.
+ */
+ public Filler(Dimension min, Dimension pref, Dimension max)
+ {
+ changeShape(min, pref, max);
+ }
+
+ /**
+ * Changes the dimensions of this Filler.
+ *
+ * @param min the new minimum size of the filler.
+ * @param pref the new preferred size of the filler.
+ * @param max the new maximum size of the filler.
+ */
+ public void changeShape(Dimension min, Dimension pref, Dimension max)
+ {
+ this.min = min;
+ this.pref = pref;
+ this.max = max;
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleBoxFiller();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the maximum size of this Filler.
+ *
+ * @return the maximum size of this Filler.
+ */
+ public Dimension getMaximumSize()
+ {
+ return max;
+ }
+
+ /**
+ * Returns the minimum size of this Filler.
+ *
+ * @return the minimum size of this Filler.
+ */
+ public Dimension getMinimumSize()
+ {
+ return min;
+ }
+
+ /**
+ * Returns the preferred size of this Filler.
+ *
+ * @return the preferred size of this Filler.
+ */
+ public Dimension getPreferredSize()
+ {
+ return pref;
+ }
+ }
+
+ /**
+ * Creates a new Box component, that lays out its children according
+ * to the axis parameter.
+ *
+ * @param axis the orientation of the BoxLayout.
+ *
+ * @see BoxLayout#X_AXIS
+ * @see BoxLayout#Y_AXIS
+ * @see BoxLayout#LINE_AXIS
+ * @see BoxLayout#PAGE_AXIS
+ */
+ public Box(int axis)
+ {
+ super.setLayout(new BoxLayout(this, axis));
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in both X and Y directions.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createGlue()
+ {
+ Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0),
+ new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
+ return glue;
+ }
+
+ public static Box createHorizontalBox()
+ {
+ return new Box(BoxLayout.X_AXIS);
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in the X direction.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createHorizontalGlue()
+ {
+ Filler glue = new Filler(new Dimension(0, 0), new Dimension(0, 0),
+ new Dimension(Short.MAX_VALUE, 0));
+ return glue;
+ }
+
+ /**
+ * Creates a filler component which acts as strut between components.
+ * It will fill exactly the specified horizontal size.
+ *
+ * @param width the width of this strut in pixels.
+ *
+ * @return a strut-like filler component.
+ */
+ public static Component createHorizontalStrut(int width)
+ {
+ Filler strut = new Filler(new Dimension(width, 0),
+ new Dimension(width, 0),
+ new Dimension(width, Integer.MAX_VALUE));
+ return strut;
+ }
+
+ public static Component createRigidArea(Dimension d)
+ {
+ return new Filler(d, d, d);
+ }
+
+ public static Box createVerticalBox()
+ {
+ return new Box(BoxLayout.Y_AXIS);
+ }
+
+ /**
+ * Creates a filler component which acts as glue between components.
+ * It does not take space unless some extra space is available. If extra
+ * space is available, this component can expand in the Y direction.
+ *
+ * @return a glue-like filler component.
+ */
+ public static Component createVerticalGlue()
+ {
+ return createGlue();
+ }
+
+ /**
+ * Creates a filler component which acts as strut between components.
+ * It will fill exactly the specified vertical size.
+ *
+ * @param height the height of this strut in pixels.
+ *
+ * @return a strut-like filler component.
+ */
+ public static Component createVerticalStrut(int height)
+ {
+ Filler strut = new Filler(new Dimension(0, height),
+ new Dimension(0, height),
+ new Dimension(Integer.MAX_VALUE, height));
+ return strut;
+ }
+
+ public void setLayout(LayoutManager l)
+ {
+ throw new AWTError("Not allowed to set layout managers for boxes.");
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleBox();
+ return accessibleContext;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/BoxLayout.java b/libjava/classpath/javax/swing/BoxLayout.java
new file mode 100644
index 000000000..55c489eb9
--- /dev/null
+++ b/libjava/classpath/javax/swing/BoxLayout.java
@@ -0,0 +1,451 @@
+/* BoxLayout.java -- A layout for swing components.
+ Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTError;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.io.Serializable;
+
+/**
+ * A layout that stacks the children of a container in a Box, either
+ * horizontally or vertically.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class BoxLayout implements LayoutManager2, Serializable
+{
+
+ /**
+ * Specifies that components are laid out left to right.
+ */
+ public static final int X_AXIS = 0;
+
+ /**
+ * Specifies that components are laid out top to bottom.
+ */
+ public static final int Y_AXIS = 1;
+
+ /**
+ * Specifies that components are laid out in the direction of a line of text.
+ */
+ public static final int LINE_AXIS = 2;
+
+ /**
+ * Sepcifies that components are laid out in the direction of the line flow.
+ */
+ public static final int PAGE_AXIS = 3;
+
+ /*
+ * Needed for serialization.
+ */
+ private static final long serialVersionUID = -2474455742719112368L;
+
+ /*
+ * The container given to the constructor.
+ */
+ private Container container;
+
+ /**
+ * Current type of component layouting. Defaults to X_AXIS.
+ */
+ private int way = X_AXIS;
+
+ /**
+ * The size requirements of the containers children for the X direction.
+ */
+ private SizeRequirements[] xChildren;
+
+ /**
+ * The size requirements of the containers children for the Y direction.
+ */
+ private SizeRequirements[] yChildren;
+
+ /**
+ * The size requirements of the container to be laid out for the X direction.
+ */
+ private SizeRequirements xTotal;
+
+ /**
+ * The size requirements of the container to be laid out for the Y direction.
+ */
+ private SizeRequirements yTotal;
+
+ /**
+ * The offsets of the child components in the X direction.
+ */
+ private int[] offsetsX;
+
+ /**
+ * The offsets of the child components in the Y direction.
+ */
+ private int[] offsetsY;
+
+ /**
+ * The spans of the child components in the X direction.
+ */
+ private int[] spansX;
+
+ /**
+ * The spans of the child components in the Y direction.
+ */
+ private int[] spansY;
+
+ /**
+ * Constructs a BoxLayout object.
+ *
+ * @param container The container that needs to be laid out.
+ * @param way The orientation of the components.
+ *
+ * @exception AWTError If way has an invalid value.
+ */
+ public BoxLayout(Container container, int way)
+ {
+ if (way != X_AXIS && way != Y_AXIS && way != LINE_AXIS && way != PAGE_AXIS)
+ throw new AWTError("Invalid axis");
+
+ int width = 0;
+ int height = 0;
+ this.container = container;
+ this.way = way;
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout.
+ *
+ * @param name The name of the component to add.
+ * @param component the component to add to the layout.
+ */
+ public void addLayoutComponent(String name, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Removes a component from the layout. Not used in BoxLayout.
+ *
+ * @param component The component to remove from the layout.
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // Nothing to do here.
+ }
+
+ private boolean isHorizontalIn(Container parent)
+ {
+ ComponentOrientation orientation = parent.getComponentOrientation();
+ return this.way == X_AXIS
+ || (this.way == LINE_AXIS
+ && orientation.isHorizontal())
+ || (this.way == PAGE_AXIS
+ && (!orientation.isHorizontal()));
+ }
+
+
+
+ /**
+ * Returns the preferred size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ return new Dimension(xTotal.preferred + i.left + i.right,
+ yTotal.preferred + i.top + i.bottom);
+ }
+ }
+
+ /**
+ * Returns the minimum size of the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ return new Dimension(xTotal.minimum + i.left + i.right,
+ yTotal.minimum + i.top + i.bottom);
+ }
+ }
+
+ /**
+ * Lays out the specified container using this layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void layoutContainer(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkLayout();
+ Component[] children = container.getComponents();
+ Insets in = container.getInsets();
+ for (int i = 0; i < children.length; i++)
+ children[i].setBounds(offsetsX[i] + in.left, offsetsY[i] + in.top,
+ spansX[i], spansY[i]);
+ }
+ }
+
+ /**
+ * Adds a component to the layout. Not used in BoxLayout
+ *
+ * @param child The component to add to the layout.
+ * @param constraints The constraints for the component in the layout.
+ */
+ public void addLayoutComponent(Component child, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the alignment along the X axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentX(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ return xTotal.alignment;
+ }
+ }
+
+ /**
+ * Returns the alignment along the Y axis for the container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The alignment.
+ */
+ public float getLayoutAlignmentY(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ return yTotal.alignment;
+ }
+ }
+
+ /**
+ * Invalidates the layout.
+ *
+ * @param parent The container that needs to be laid out.
+ */
+ public void invalidateLayout(Container parent)
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ synchronized (container.getTreeLock())
+ {
+ xChildren = null;
+ yChildren = null;
+ xTotal = null;
+ yTotal = null;
+ offsetsX = null;
+ offsetsY = null;
+ spansX = null;
+ spansY = null;
+ }
+ }
+
+ /**
+ * Returns the maximum size of the layout gived the components
+ * in the given container.
+ *
+ * @param parent The container that needs to be laid out.
+ *
+ * @return The dimension of the layout.
+ */
+ public Dimension maximumLayoutSize(Container parent)
+ {
+ synchronized (container.getTreeLock())
+ {
+ if (container != parent)
+ throw new AWTError("BoxLayout can't be shared");
+
+ checkTotalRequirements();
+ Insets i = container.getInsets();
+ int xDim = xTotal.maximum + i.left + i.right;
+ int yDim = yTotal.maximum + i.top + i.bottom;
+
+ // Check for overflow
+ if (xDim < xTotal.maximum)
+ xDim = Integer.MAX_VALUE;
+ if (yDim < yTotal.maximum)
+ yDim = Integer.MAX_VALUE;
+ return new Dimension(xDim, yDim);
+ }
+ }
+
+ /**
+ * Makes sure that the xTotal and yTotal fields are set up correctly. A call
+ * to {@link #invalidateLayout} sets these fields to null and they have to be
+ * recomputed.
+ */
+ private void checkTotalRequirements()
+ {
+ if (xTotal == null || yTotal == null)
+ {
+ checkRequirements();
+ if (isHorizontalIn(container))
+ {
+ xTotal = SizeRequirements.getTiledSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
+ }
+ else
+ {
+ xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getTiledSizeRequirements(yChildren);
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the xChildren and yChildren fields are correctly set up.
+ * A call to {@link #invalidateLayout(Container)} sets these fields to null,
+ * so they have to be set up again.
+ */
+ private void checkRequirements()
+ {
+ if (xChildren == null || yChildren == null)
+ {
+ Component[] children = container.getComponents();
+ xChildren = new SizeRequirements[children.length];
+ yChildren = new SizeRequirements[children.length];
+ for (int i = 0; i < children.length; i++)
+ {
+ if (! children[i].isVisible())
+ {
+ xChildren[i] = new SizeRequirements();
+ yChildren[i] = new SizeRequirements();
+ }
+ else
+ {
+ xChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().width,
+ children[i].getPreferredSize().width,
+ children[i].getMaximumSize().width,
+ children[i].getAlignmentX());
+ yChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().height,
+ children[i].getPreferredSize().height,
+ children[i].getMaximumSize().height,
+ children[i].getAlignmentY());
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set
+ * up correctly. A call to {@link #invalidateLayout} sets these fields
+ * to null and they have to be recomputed.
+ */
+ private void checkLayout()
+ {
+ if (offsetsX == null || offsetsY == null || spansX == null
+ || spansY == null)
+ {
+ checkRequirements();
+ checkTotalRequirements();
+ int len = container.getComponents().length;
+ offsetsX = new int[len];
+ offsetsY = new int[len];
+ spansX = new int[len];
+ spansY = new int[len];
+
+ Insets in = container.getInsets();
+ int width = container.getWidth() - in.left - in.right;
+ int height = container.getHeight() - in.top - in.bottom;
+
+ if (isHorizontalIn(container))
+ {
+ SizeRequirements.calculateTiledPositions(width,
+ xTotal, xChildren,
+ offsetsX, spansX);
+ SizeRequirements.calculateAlignedPositions(height,
+ yTotal, yChildren,
+ offsetsY, spansY);
+ }
+ else
+ {
+ SizeRequirements.calculateAlignedPositions(width,
+ xTotal, xChildren,
+ offsetsX, spansX);
+ SizeRequirements.calculateTiledPositions(height,
+ yTotal, yChildren,
+ offsetsY, spansY);
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/ButtonGroup.java b/libjava/classpath/javax/swing/ButtonGroup.java
new file mode 100644
index 000000000..d4168bb99
--- /dev/null
+++ b/libjava/classpath/javax/swing/ButtonGroup.java
@@ -0,0 +1,223 @@
+/* ButtonGroup.java --
+ Copyright (C) 2002, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Vector;
+
+
+/**
+ * Logically groups a set of buttons, so that only one of the buttons in
+ * a ButtonGroup can be selected at the same time. If one
+ * button in a ButtonGroup is selected, all other buttons
+ * are automatically deselected.
+ *
+ * While ButtonGroup can be used for all buttons that are derived
+ * from {@link AbstractButton}, it is normally only used for
+ * {@link JRadioButton}s, {@link JRadioButtonMenuItem}s and
+ * {@link JToggleButton}s.
+ *
+ * You could use it for {@link JCheckBox}es, but for the sake of usability
+ * this is strongly discouraged because the common expectation of checkboxes
+ * is that the user is allowed to make multiple selections.
+ *
+ * It makes no sense to put {@link JButton}s or {@link JMenuItem}s in
+ * a ButtonGroup because they don't implement the
+ * selected semantics.
+ *
+ * @author original author unknown
+ */
+public class ButtonGroup implements Serializable
+{
+ private static final long serialVersionUID = 4259076101881721375L;
+
+ /** Stores references to the buttons added to this button group. */
+ protected Vector buttons = new Vector();
+
+ /** The currently selected button model. */
+ ButtonModel sel;
+
+ /**
+ * Creates a new button group.
+ */
+ public ButtonGroup()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a button to this group. If the button is in the selected state, then:
+ *
+ *
if the group has no current selection, the new button becomes the
+ * selected button for the group;
+ *
if the group already has a selected button, the new button is set to
+ * "not selected".
+ *
+ *
+ * @param b the button to add (null is ignored).
+ */
+ public void add(AbstractButton b)
+ {
+ if (b == null)
+ return;
+ b.getModel().setGroup(this);
+ if (b.isSelected())
+ {
+ if (sel == null)
+ sel = b.getModel();
+ else
+ b.setSelected(false);
+ }
+ buttons.addElement(b);
+ }
+
+ /**
+ * Removes the specified button from this group. If the button is the
+ * selected button, the current selection is set to null.
+ * The group for the removed button's model is set to null.
+ *
+ * @param b the button to remove (null is ignored).
+ */
+ public void remove(AbstractButton b)
+ {
+ if (b == null)
+ return;
+ b.getModel().setGroup(null);
+ if (b.getModel() == sel)
+ sel = null;
+ buttons.removeElement(b);
+ }
+
+ /**
+ * Returns the currently added buttons.
+ *
+ * @return Enumeration over all added buttons
+ */
+ public Enumeration getElements()
+ {
+ return buttons.elements();
+ }
+
+ /**
+ * Returns the currently selected button model.
+ *
+ * @return the currently selected button model, null if none was selected
+ * yet
+ */
+ public ButtonModel getSelection()
+ {
+ return sel;
+ }
+
+ /**
+ * Returns the button that has the specified model, or null if
+ * there is no such button in the group.
+ *
+ * @param m the button model.
+ *
+ * @return The button that has the specified model, or null.
+ */
+ AbstractButton findButton(ButtonModel m)
+ {
+ for (int i = 0; i < buttons.size(); i++)
+ {
+ AbstractButton a = (AbstractButton) buttons.get(i);
+ if (a.getModel() == m)
+ return a;
+ }
+ return null;
+ }
+
+ /**
+ * Sets the currently selected button model. Only one button of a group can
+ * be selected at a time.
+ *
+ * @param m the model to select
+ * @param b true if this button is to be selected, false otherwise
+ */
+ public void setSelected(ButtonModel m, boolean b)
+ {
+ if ((sel != m || b) && (! b || sel == m))
+ return;
+
+ if (b && sel != m)
+ {
+ ButtonModel old = sel;
+ sel = m;
+
+ if (old != null)
+ old.setSelected(false);
+
+ if (m != null)
+ sel.setSelected(true);
+
+ AbstractButton button = findButton(old);
+ if (button != null)
+ button.repaint();
+ }
+ else if (!b && sel == m)
+ m.setSelected(true);
+ }
+
+ /**
+ * Checks if the given ButtonModel is selected in this button
+ * group.
+ *
+ * @param m the button model (null permitted).
+ *
+ * @return true if m is the selected button model
+ * in this group, and false otherwise.
+ */
+ public boolean isSelected(ButtonModel m)
+ {
+ return m == sel;
+ }
+
+ /**
+ * Return the number of buttons in this button group.
+ *
+ * @return the number of buttons
+ *
+ * @since 1.3
+ */
+ public int getButtonCount()
+ {
+ return buttons.size();
+ }
+}
diff --git a/libjava/classpath/javax/swing/ButtonModel.java b/libjava/classpath/javax/swing/ButtonModel.java
new file mode 100644
index 000000000..d48eb1e65
--- /dev/null
+++ b/libjava/classpath/javax/swing/ButtonModel.java
@@ -0,0 +1,301 @@
+/* ButtonModel.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.ItemSelectable;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemListener;
+
+import javax.swing.event.ChangeListener;
+
+/**
+ * The data model that is used in all kinds of buttons.
+ */
+public interface ButtonModel extends ItemSelectable
+{
+
+ /**
+ * Returns true if the button is armed, false
+ * otherwise.
+ *
+ * A button is armed, when the user has pressed the mouse over it, but has
+ * not yet released the mouse.
+ *
+ * @return true if the button is armed, false
+ * otherwise
+ *
+ * @see #setArmed(boolean)
+ */
+ boolean isArmed();
+
+ /**
+ * Sets the armed flag of the button.
+ *
+ * A button is armed, when the user has pressed the mouse over it, but has
+ * not yet released the mouse.
+ *
+ * @param b true if the button is armed, false
+ * otherwise
+ *
+ * @see #isArmed()
+ */
+ void setArmed(boolean b);
+
+ /**
+ * Returns true if the button is enabled, false
+ * otherwise.
+ *
+ * When a button is disabled, it is usually grayed out and the user cannot
+ * change its state.
+ *
+ * @return true if the button is enabled, false
+ * otherwise
+ *
+ * @see #setEnabled(boolean)
+ */
+ boolean isEnabled();
+
+ /**
+ * Sets the enabled flag of the button.
+ *
+ * When a button is disabled, it is usually grayed out and the user cannot
+ * change its state.
+ *
+ * @param b true if the button is enabled, false
+ * otherwise
+ *
+ * @see #isEnabled()
+ */
+ void setEnabled(boolean b);
+
+ /**
+ * Sets the pressed flag of the button.
+ *
+ * The button usually gets pressed when the user clicks on a button, it will
+ * be un-pressed when the user releases the mouse.
+ *
+ * @param b true if the button is pressed, false
+ * otherwise
+ *
+ * @see #isPressed()
+ */
+ void setPressed(boolean b);
+
+ /**
+ * Returns true if the button is pressed, false
+ * otherwise.
+ *
+ * The button usually gets pressed when the user clicks on a button, it will
+ * be un-pressed when the user releases the mouse.
+ *
+ * @return true if the button is pressed, false
+ * otherwise
+ *
+ * @see #setPressed(boolean)
+ */
+ boolean isPressed();
+
+ /**
+ * Removes an {@link ActionListener} from the list of registered listeners.
+ *
+ * @param l the action listener to remove
+ *
+ * @see #addActionListener(ActionListener)
+ */
+ void removeActionListener(ActionListener l);
+
+ /**
+ * Adds an {@link ActionListener} to the list of registered listeners.
+ *
+ * An ActionEvent is usually fired when the user clicks on a
+ * button.
+ *
+ * @param l the action listener to add
+ *
+ * @see #removeActionListener(ActionListener)
+ */
+ void addActionListener(ActionListener l);
+
+ /**
+ * Adds an {@link ItemListener} to the list of registered listeners.
+ *
+ * An ItemEvent is usually fired when a button's selected
+ * state changes. This applies only to buttons that support the selected
+ * flag.
+ *
+ * @param l the item listener to add
+ *
+ * @see #removeItemListener(ItemListener)
+ */
+ void addItemListener(ItemListener l);
+
+ /**
+ * Adds an {@link ItemListener} to the list of registered listeners.
+ *
+ * @param l the item listener to add
+ *
+ * @see #removeItemListener(ItemListener)
+ */
+ void removeItemListener(ItemListener l);
+
+ /**
+ * Adds an {@link ChangeListener} to the list of registered listeners.
+ *
+ * A ChangeEvent is fired when any one of the button's flags
+ * changes.
+ *
+ * @param l the change listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void addChangeListener(ChangeListener l);
+
+ /**
+ * Adds an {@link ChangeListener} to the list of registered listeners.
+ *
+ * @param l the change listener to add
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ void removeChangeListener(ChangeListener l);
+
+ /**
+ * Sets the rollover flag of the button.
+ *
+ * A button is rollover-ed, when the user has moved the mouse over it, but has
+ * not yet pressed the mouse.
+ *
+ * @param b true if the button is rollover, false
+ * otherwise
+ *
+ * @see #isRollover()
+ */
+ void setRollover(boolean b);
+
+ /**
+ * Returns true if the button is rollover-ed, false
+ * otherwise.
+ *
+ * A button is rollover-ed, when the user has moved the mouse over it, but has
+ * not yet pressed the mouse.
+ *
+ * @return true if the button is rollover, false
+ * otherwise
+ *
+ * @see #setRollover(boolean)
+ */
+ boolean isRollover();
+
+ /**
+ * Returns the keyboard mnemonic for the button. This specifies a shortcut
+ * or accelerator key that can be used to activate the button.
+ *
+ * @return the keyboard mnemonic for the button
+ *
+ * @see #setMnemonic(int)
+ */
+ int getMnemonic();
+
+ /**
+ * Sets the keyboard mnemonic for the button. This specifies a shortcut
+ * or accelerator key that can be used to activate the button.
+ *
+ * @param key the keyboard mnemonic for the button
+ *
+ * @see #getMnemonic()
+ */
+ void setMnemonic(int key);
+
+ /**
+ * Sets the action command for the button. This will be used in
+ * ActionEvents fired by the button.
+ *
+ * @param s the action command to set
+ *
+ * @see #getActionCommand()
+ */
+ void setActionCommand(String s);
+
+ /**
+ * Returns the action command of the button.
+ *
+ * @return the action command of the button
+ *
+ * @see #setActionCommand(String)
+ */
+ String getActionCommand();
+
+ /**
+ * Sets the button group for the button. Some kinds of button (e.g. radio
+ * buttons) allow only one button within a button group selected at any one
+ * time.
+ *
+ * @param group the button group to set
+ */
+ void setGroup(ButtonGroup group);
+
+ /**
+ * Sets the selected flag of the button.
+ *
+ * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons)
+ * can be in one of two states: selected or unselected. The selected state
+ * is usually toggled by clicking on the button.
+ *
+ * @param b true if the button is selected, false
+ * otherwise
+ *
+ * @see #isSelected()
+ */
+ void setSelected(boolean b);
+
+ /**
+ * Returns true if the button is selected, false
+ * otherwise.
+ *
+ * Some kinds of buttons (e.g. toggle buttons, check boxes, radio buttons)
+ * can be in one of two states: selected or unselected. The selected state
+ * is usually toggled by clicking on the button.
+ *
+ * @return true if the button is selected, false
+ * otherwise
+ *
+ * @see #setSelected(boolean)
+ */
+ boolean isSelected();
+}
diff --git a/libjava/classpath/javax/swing/CellEditor.java b/libjava/classpath/javax/swing/CellEditor.java
new file mode 100644
index 000000000..134c316e4
--- /dev/null
+++ b/libjava/classpath/javax/swing/CellEditor.java
@@ -0,0 +1,108 @@
+/* CellEditor.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.util.EventObject;
+
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+
+/**
+ * Provides edit capabilities for components that display cells like
+ * {@link JTable}, {@link JList} and {@link JTree}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface CellEditor
+{
+ /**
+ * Returns the current value for the CellEditor.
+ *
+ * @return The value.
+ */
+ Object getCellEditorValue();
+
+ /**
+ * Returns true if the specified event makes the editor
+ * editable, and false otherwise.
+ *
+ * @param event the event.
+ *
+ * @return A boolean.
+ */
+ boolean isCellEditable(EventObject event);
+
+ /**
+ * shouldSelectCell
+ * @param event TODO
+ * @return boolean
+ */
+ boolean shouldSelectCell(EventObject event);
+
+ /**
+ * Signals to the CellEditor that it should stop editing,
+ * accepting the current cell value, and returns true if the
+ * editor actually stops editing, and false otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean stopCellEditing();
+
+ /**
+ * Signals to the CellEditor that it should cancel editing.
+ */
+ void cancelCellEditing();
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from the
+ * CellEditor.
+ *
+ * @param listener the listener.
+ */
+ void addCellEditorListener(CellEditorListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from the CellEditor.
+ *
+ * @param listener the listener.
+ */
+ void removeCellEditorListener(CellEditorListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/CellRendererPane.java b/libjava/classpath/javax/swing/CellRendererPane.java
new file mode 100644
index 000000000..0140b4c2c
--- /dev/null
+++ b/libjava/classpath/javax/swing/CellRendererPane.java
@@ -0,0 +1,251 @@
+/* CellRendererPane.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * Paints the cells of JList, JTable and JTree.
+ * It intercepts the usual paint tree, so that we don't walk up and
+ * repaint everything.
+ *
+ * @author Andrew Selkirk
+ */
+public class CellRendererPane extends Container implements Accessible
+{
+ private static final long serialVersionUID = -7642183829532984273L;
+
+ /**
+ * Provides accessibility support for CellRendererPanes.
+ */
+ protected class AccessibleCellRendererPane extends AccessibleAWTContainer
+ {
+ private static final long serialVersionUID = -8981090083147391074L;
+
+ /**
+ * Constructor AccessibleCellRendererPane
+ */
+ protected AccessibleCellRendererPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PANEL;
+ }
+ }
+
+ /**
+ * accessibleContext
+ */
+ protected AccessibleContext accessibleContext = null;
+
+ /**
+ * Constructs a new CellRendererPane.
+ */
+ public CellRendererPane()
+ {
+ setVisible(false);
+ }
+
+ /**
+ * Should not be called.
+ *
+ * @param graphics not used here
+ */
+ public void update(Graphics graphics)
+ {
+ //Nothing to do here.
+ }
+
+ /**
+ * Despite normal behaviour this does not cause the container
+ * to be invalidated. This prevents propagating up the paint tree.
+ */
+ public void invalidate()
+ {
+ // Overridden to do nothing.
+ }
+
+ /**
+ * Should not be called.
+ *
+ * @param graphics not used here
+ */
+ public void paint(Graphics graphics)
+ {
+ // Overridden to do nothing.
+ }
+
+ /**
+ * Overridden to check if a component is already a child of this Container.
+ * If it's already a child, nothing is done. Otherwise we pass this to
+ * super.addImpl().
+ *
+ * @param c the component to add
+ * @param constraints not used here
+ * @param index not used here
+ */
+ protected void addImpl(Component c, Object constraints, int index)
+ {
+ if (!isAncestorOf(c))
+ {
+ super.addImpl(c, constraints, index);
+ }
+ }
+
+ /**
+ * Paints the specified component c on the {@link Graphics}
+ * context graphics. The Graphics context is tranlated to
+ * (x,y) and the components bounds are set to (w,h). If
+ * shouldValidate
+ * is set to true, then the component is validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the parent of the component
+ * @param x the X coordinate of the upper left corner where c should
+ be painted
+ * @param y the Y coordinate of the upper left corner where c should
+ be painted
+ * @param w the width of the components drawing area
+ * @param h the height of the components drawing area
+ * @param shouldValidate if c should be validated before
+ * painting
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, int x, int y, int w, int h,
+ boolean shouldValidate)
+ {
+ // reparent c
+ addImpl(c, null, 0);
+
+ Rectangle oldClip = graphics.getClipBounds();
+ boolean translated = false;
+ try
+ {
+ // translate to (x,y)
+ graphics.translate(x, y);
+ translated = true;
+ graphics.clipRect(0, 0, w, h);
+ // set bounds of c
+ c.setBounds(0, 0, w, h);
+
+ // validate if necessary
+ if (shouldValidate)
+ {
+ c.validate();
+ }
+
+ // paint component
+ c.paint(graphics);
+ }
+ finally
+ {
+ // untranslate g
+ if (translated)
+ graphics.translate(-x, -y);
+ graphics.setClip(oldClip);
+ }
+ }
+
+ /**
+ * Paints the specified component c on the {@link Graphics}
+ * context graphics. The Graphics context is tranlated to (x,y)
+ * and the components bounds are set to (w,h). The component is not
+ * validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the parent of the component
+ * @param x the X coordinate of the upper left corner where c should
+ be painted
+ * @param y the Y coordinate of the upper left corner where c should
+ be painted
+ * @param w the width of the components drawing area
+ * @param h the height of the components drawing area
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, int x, int y, int w, int h)
+ {
+ paintComponent(graphics, c, p, x, y, w, h, false);
+ }
+
+ /**
+ * Paints the specified component c on the {@link Graphics}
+ * context g. The Graphics context is tranlated to (r.x,r.y) and
+ * the components bounds are set to (r.width,r.height).
+ * The component is not
+ * validated before painting.
+ *
+ * @param graphics the graphics context to paint on
+ * @param c the component to be painted
+ * @param p the component on which we paint
+ * @param r the bounding rectangle of c
+ */
+ public void paintComponent(Graphics graphics, Component c,
+ Container p, Rectangle r)
+ {
+ paintComponent(graphics, c, p, r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * getAccessibleContext TODO
+ * @return AccessibleContext
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleCellRendererPane();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ComboBoxEditor.java b/libjava/classpath/javax/swing/ComboBoxEditor.java
new file mode 100644
index 000000000..8e914e4b9
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComboBoxEditor.java
@@ -0,0 +1,96 @@
+/* ComboBoxEditor.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.event.ActionListener;
+
+/**
+ * Provides edit capabilities for {@link JComboBox}es.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ */
+public interface ComboBoxEditor
+{
+ /**
+ * This method returns component that will be used by the combo box to
+ * display/edit currently selected item in the combo box.
+ *
+ * @return Component that will be used by the combo box to display/edit
+ * currently selected item
+ */
+ Component getEditorComponent();
+
+ /**
+ * Sets item that should be editted when any editting operation is performed
+ * by the user. The value is always equal to the currently selected value
+ * in the combo box. Thus, whenever a different value is selected from the
+ * combo box list then this method should be called to change editting item
+ * to the new selected item.
+ *
+ * @param item item that is currently selected in the combo box
+ */
+ void setItem(Object item);
+
+ /**
+ * This method returns item that is currently editable.
+ *
+ * @return Item in the combo box that is currently editable
+ */
+ Object getItem();
+
+ /**
+ * selectAll
+ */
+ void selectAll();
+
+ /**
+ * This method adds specified ActionListener to this ComboBoxEditor.
+ *
+ * @param listener
+ */
+ void addActionListener(ActionListener listener);
+
+ /**
+ * This method removes given ActionListener from this ComboBoxEditor.
+ *
+ * @param listener TODO
+ */
+ void removeActionListener(ActionListener listener);
+} // ComboBoxEditor
diff --git a/libjava/classpath/javax/swing/ComboBoxModel.java b/libjava/classpath/javax/swing/ComboBoxModel.java
new file mode 100644
index 000000000..ce252faed
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComboBoxModel.java
@@ -0,0 +1,69 @@
+/* ComboBoxModel.java --
+ Copyright (C) 2002, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+
+/**
+ * The data model for a {@link JComboBox}. This model keeps track of elements
+ * contained in the JComboBox as well as the current
+ * combo box selection. Whenever the selection in the JComboBox
+ * changes, the ComboBoxModel should fire a {@link ListDataEvent}
+ * to the model's {@link ListDataListener}s.
+ *
+ * @author Andrew Selkirk
+ */
+public interface ComboBoxModel extends ListModel
+{
+ /**
+ * Sets the selected item in the combo box. Classes implementing this
+ * interface should fire a {@link ListDataEvent} to all registered
+ * {@link ListDataListener}s to indicate that the selection has changed.
+ *
+ * @param item the selected item (null permitted).
+ */
+ void setSelectedItem(Object item);
+
+ /**
+ * Returns the currently selected item in the combo box.
+ *
+ * @return The selected item (possibly null).
+ */
+ Object getSelectedItem();
+}
diff --git a/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java b/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java
new file mode 100644
index 000000000..40c2010c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/CompatibilityFocusTraversalPolicy.java
@@ -0,0 +1,164 @@
+/* CompatibilityFocusTraversalPolicy.java -- Provides compatibility to old
+ focus API
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.FocusTraversalPolicy;
+import java.util.HashMap;
+
+/**
+ * Provides compatibility to the older focus API in
+ * {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class CompatibilityFocusTraversalPolicy
+ extends FocusTraversalPolicy
+{
+
+ /**
+ * The focus traversal policy that has been installed on the focus cycle
+ * root before, and to which we fall back.
+ */
+ private FocusTraversalPolicy fallback;
+
+ /**
+ * Maps components to their next focused components.
+ */
+ private HashMap forward;
+
+ /**
+ * Maps components to their previous focused components.
+ */
+ private HashMap backward;
+
+ /**
+ * Creates a new CompatibilityFocusTraversalPolicy with the specified
+ * policy as fallback.
+ *
+ * @param p the fallback policy
+ */
+ CompatibilityFocusTraversalPolicy(FocusTraversalPolicy p)
+ {
+ fallback = p;
+ forward = new HashMap();
+ backward = new HashMap();
+ }
+
+ public Component getComponentAfter(Container root, Component current)
+ {
+ Component next = (Component) forward.get(current);
+ if (next == null && fallback != null)
+ next = fallback.getComponentAfter(root, current);
+ return next;
+ }
+
+ public Component getComponentBefore(Container root, Component current)
+ {
+ Component previous = (Component) backward.get(current);
+ if (previous == null && fallback != null)
+ previous = fallback.getComponentAfter(root, current);
+ return previous;
+ }
+
+ public Component getFirstComponent(Container root)
+ {
+ Component first = null;
+ if (fallback != null)
+ first = fallback.getFirstComponent(root);
+ return first;
+ }
+
+ public Component getLastComponent(Container root)
+ {
+ Component last = null;
+ if (fallback != null)
+ last = fallback.getLastComponent(root);
+ return last;
+ }
+
+ public Component getDefaultComponent(Container root)
+ {
+ Component def = null;
+ if (fallback != null)
+ def = fallback.getDefaultComponent(root);
+ return def;
+ }
+
+ /**
+ * Sets a next focused component for a specified component. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void setNextFocusableComponent(Component current, Component next)
+ {
+ forward.put(current, next);
+ backward.put(next, current);
+ }
+
+ /**
+ * Sets a next focused component for a specified component. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void addNextFocusableComponent(Component current, Component next)
+ {
+ forward.put(current, next);
+ backward.put(next, current);
+ }
+
+ /**
+ * Removes a focused component mapping. This is called
+ * by {@link JComponent#setNextFocusableComponent(Component)}.
+ *
+ * @param current the current component
+ * @param next the next focused component
+ */
+ void removeNextFocusableComponent(Component current, Component next)
+ {
+ forward.remove(current);
+ backward.remove(next);
+ }
+}
diff --git a/libjava/classpath/javax/swing/ComponentInputMap.java b/libjava/classpath/javax/swing/ComponentInputMap.java
new file mode 100644
index 000000000..dc4d0bfd4
--- /dev/null
+++ b/libjava/classpath/javax/swing/ComponentInputMap.java
@@ -0,0 +1,141 @@
+/* ComponentInputMap.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+
+/**
+ * An {@link InputMap} that is associated with a particular {@link JComponent}.
+ * The component is notified when its ComponentInputMap changes.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ */
+public class ComponentInputMap extends InputMap
+{
+ /**
+ * The component to notify.
+ */
+ private JComponent component;
+
+ /**
+ * Creates ComponentInputMap object that notifies the given
+ * component about changes to it.
+ *
+ * @param comp the component to notify
+ *
+ * @exception IllegalArgumentException if comp is null
+ */
+ public ComponentInputMap(JComponent comp)
+ {
+ if (comp == null)
+ throw new IllegalArgumentException();
+
+ this.component = comp;
+ }
+
+ /**
+ * Puts a new entry into the InputMap.
+ * If actionMapKey is null an existing entry will be removed.
+ *
+ * @param keystroke the keystroke for the entry
+ * @param value the action.
+ */
+ public void put(KeyStroke keystroke, Object value)
+ {
+ super.put(keystroke, value);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Clears the InputMap.
+ */
+ public void clear()
+ {
+ super.clear();
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Remove an entry from the InputMap.
+ *
+ * @param keystroke the key of the entry to remove
+ */
+ public void remove(KeyStroke keystroke)
+ {
+ super.remove(keystroke);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Sets a parent for this ComponentInputMap.
+ *
+ * @param parentMap the new parent
+ *
+ * @exception IllegalArgumentException if parentMap is not a
+ * ComponentInputMap or not associated with the same component
+ */
+ public void setParent(InputMap parentMap)
+ {
+ if (parentMap != null && !(parentMap instanceof ComponentInputMap))
+ throw new IllegalArgumentException("ComponentInputMaps can only have " +
+ "ComponentInputMaps for parents");
+
+ if (parentMap != null &&
+ ((ComponentInputMap) parentMap).getComponent() != component)
+ throw new
+ IllegalArgumentException("ComponentInputMaps' parents must " +
+ "be associated with the same JComponents");
+
+ super.setParent(parentMap);
+ if (component != null)
+ component.updateComponentInputMap(this);
+ }
+
+ /**
+ * Returns the component to notify about changes.
+ *
+ * @return a JComponent object
+ */
+ public JComponent getComponent()
+ {
+ return component;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DebugGraphics.java b/libjava/classpath/javax/swing/DebugGraphics.java
new file mode 100644
index 000000000..d96de4a22
--- /dev/null
+++ b/libjava/classpath/javax/swing/DebugGraphics.java
@@ -0,0 +1,1125 @@
+/* DebugGraphics.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.image.ImageObserver;
+import java.io.PrintStream;
+import java.text.AttributedCharacterIterator;
+
+
+/**
+ * An extension of {@link Graphics} that can be used for debugging
+ * custom Swing widgets. DebugGraphics has the ability to
+ * draw slowly and can log drawing actions.
+ *
+ * @author Andrew Selkirk
+ */
+public class DebugGraphics extends Graphics
+{
+ /**
+ * LOG_OPTION
+ */
+ public static final int LOG_OPTION = 1;
+
+ /**
+ * FLASH_OPTION
+ */
+ public static final int FLASH_OPTION = 2;
+
+ /**
+ * BUFFERED_OPTION
+ */
+ public static final int BUFFERED_OPTION = 4;
+
+ /**
+ * NONE_OPTION
+ */
+ public static final int NONE_OPTION = -1;
+
+ static Color debugFlashColor = Color.RED;
+ static int debugFlashCount = 10;
+ static int debugFlashTime = 1000;
+ static PrintStream debugLogStream = System.out;
+
+ /**
+ * Counts the created DebugGraphics objects. This is used by the
+ * logging facility.
+ */
+ static int counter = 0;
+
+ /**
+ * graphics
+ */
+ Graphics graphics;
+
+ /**
+ * buffer
+ */
+ Image buffer;
+
+ /**
+ * debugOptions
+ */
+ int debugOptions;
+
+ /**
+ * graphicsID
+ */
+ int graphicsID;
+
+ /**
+ * xOffset
+ */
+ int xOffset;
+
+ /**
+ * yOffset
+ */
+ int yOffset;
+
+ /**
+ * Creates a DebugGraphics object.
+ */
+ public DebugGraphics()
+ {
+ counter++;
+ }
+
+ /**
+ * Creates a DebugGraphics object.
+ *
+ * @param graphics The Graphics object to wrap
+ * @param component TODO
+ */
+ public DebugGraphics(Graphics graphics, JComponent component)
+ {
+ this(graphics);
+ // FIXME: What shall we do with component ?
+ }
+
+ /**
+ * Creates a DebugGraphics object.
+ *
+ * @param graphics The Graphics object to wrap
+ */
+ public DebugGraphics(Graphics graphics)
+ {
+ this();
+ this.graphics = graphics;
+ }
+
+ /**
+ * Sets the color to draw stuff with.
+ *
+ * @param color The color
+ */
+ public void setColor(Color color)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting color: " + color);
+
+ graphics.setColor(color);
+ }
+
+ /**
+ * Creates a overrides Graphics.create to create a
+ * DebugGraphics object.
+ *
+ * @return a new DebugGraphics object.
+ */
+ public Graphics create()
+ {
+ DebugGraphics copy = new DebugGraphics(graphics.create());
+ copy.debugOptions = debugOptions;
+ return copy;
+ }
+
+ /**
+ * Creates a overrides Graphics.create to create a
+ * DebugGraphics object.
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ *
+ * @return a new DebugGraphics object.
+ */
+ public Graphics create(int x, int y, int width, int height)
+ {
+ DebugGraphics copy = new DebugGraphics(graphics.create(x, y, width,
+ height));
+ copy.debugOptions = debugOptions;
+ return copy;
+ }
+
+ /**
+ * flashColor
+ *
+ * @return Color
+ */
+ public static Color flashColor()
+ {
+ return debugFlashColor;
+ }
+
+ /**
+ * setFlashColor
+ *
+ * @param color the color to use for flashing
+ */
+ public static void setFlashColor(Color color)
+ {
+ debugFlashColor = color;
+ }
+
+ /**
+ * flashTime
+ *
+ * @return The time in milliseconds
+ */
+ public static int flashTime()
+ {
+ return debugFlashTime;
+ }
+
+ /**
+ * setFlashTime
+ *
+ * @param time The time in milliseconds
+ */
+ public static void setFlashTime(int time)
+ {
+ debugFlashTime = time;
+ }
+
+ /**
+ * flashCount
+ *
+ * @return The number of flashes
+ */
+ public static int flashCount()
+ {
+ return debugFlashCount;
+ }
+
+ /**
+ * setFlashCount
+ *
+ * @param count The number of flashes
+ */
+ public static void setFlashCount(int count)
+ {
+ debugFlashCount = count;
+ }
+
+ /**
+ * logStream
+ *
+ * @return The PrintStream to write logging messages to
+ */
+ public static PrintStream logStream()
+ {
+ return debugLogStream;
+ }
+
+ /**
+ * setLogStream
+ *
+ * @param stream The currently set PrintStream.
+ */
+ public static void setLogStream(PrintStream stream)
+ {
+ debugLogStream = stream;
+ }
+
+ /**
+ * getFont
+ *
+ * @return The font
+ */
+ public Font getFont()
+ {
+ return graphics.getFont();
+ }
+
+ /**
+ * setFont
+ *
+ * @param font The font to use for drawing text
+ */
+ public void setFont(Font font)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting font: " + font);
+
+ graphics.setFont(font);
+ }
+
+ /**
+ * Returns the color used for drawing.
+ *
+ * @return The color.
+ */
+ public Color getColor()
+ {
+ return graphics.getColor();
+ }
+
+ /**
+ * Returns the font metrics of the current font.
+ *
+ * @return a FontMetrics object
+ */
+ public FontMetrics getFontMetrics()
+ {
+ return graphics.getFontMetrics();
+ }
+
+ /**
+ * Returns the font metrics for a given font.
+ *
+ * @param font the font to get the metrics for
+ *
+ * @return a FontMetrics object
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return graphics.getFontMetrics(font);
+ }
+
+ /**
+ * translate
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void translate(int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Translating by: " + new Point(x, y));
+
+ graphics.translate(x, y);
+ }
+
+ /**
+ * setPaintMode
+ */
+ public void setPaintMode()
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting paint mode");
+
+ graphics.setPaintMode();
+ }
+
+ /**
+ * setXORMode
+ *
+ * @param color the color
+ */
+ public void setXORMode(Color color)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting XOR mode: " + color);
+
+ graphics.setXORMode(color);
+ }
+
+ /**
+ * getClipBounds
+ *
+ * @return Rectangle
+ */
+ public Rectangle getClipBounds()
+ {
+ return graphics.getClipBounds();
+ }
+
+ /**
+ * Intersects the current clip region with the given region.
+ *
+ * @param x The x-position of the region
+ * @param y The y-position of the region
+ * @param width The width of the region
+ * @param height The height of the region
+ */
+ public void clipRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().print(prefix() + " Setting clipRect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.clipRect(x, y, width, height);
+
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(" Netting clipRect: " + graphics.getClipBounds());
+ }
+
+ /**
+ * Sets the clipping region.
+ *
+ * @param x The x-position of the region
+ * @param y The y-position of the region
+ * @param width The width of the region
+ * @param height The height of the region
+ */
+ public void setClip(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Setting new clipRect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.setClip(x, y, width, height);
+ }
+
+ /**
+ * Returns the current clipping region.
+ *
+ * @return Shape
+ */
+ public Shape getClip()
+ {
+ return graphics.getClip();
+ }
+
+ /**
+ * Sets the current clipping region
+ *
+ * @param shape The clippin region
+ */
+ public void setClip(Shape shape)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Setting new clipRect: " + shape);
+
+ graphics.setClip(shape);
+ }
+
+ private void sleep(int milliseconds)
+ {
+ try
+ {
+ Thread.sleep(milliseconds);
+ }
+ catch (InterruptedException e)
+ {
+ // Ignore this.
+ }
+ }
+
+ /**
+ * Draws a rectangle.
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void drawRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.drawRect(x, y, width, height);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.drawRect(x, y, width, height);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.drawRect(x, y, width, height);
+ }
+
+ /**
+ * Draws a filled rectangle.
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void fillRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.fillRect(x, y, width, height);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.fillRect(x, y, width, height);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.fillRect(x, y, width, height);
+ }
+
+ /**
+ * clearRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ */
+ public void clearRect(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Clearing rect: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.clearRect(x, y, width, height);
+ }
+
+ /**
+ * drawRoundRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param arcWidth TODO
+ * @param arcHeight TODO
+ */
+ public void drawRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing round rect: "
+ + new Rectangle(x, y, width, height)
+ + " arcWidth: " + arcWidth
+ + " arcHeight: " + arcHeight);
+ }
+
+ graphics.drawRoundRect(x, y, width, height, arcWidth, arcHeight);
+ }
+
+ /**
+ * fillRoundRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param arcWidth TODO
+ * @param arcHeight TODO
+ */
+ public void fillRoundRect(int x, int y, int width, int height,
+ int arcWidth, int arcHeight)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling round rect: "
+ + new Rectangle(x, y, width, height)
+ + " arcWidth: " + arcWidth
+ + " arcHeight: " + arcHeight);
+ }
+
+ graphics.fillRoundRect(x, y, width, height, arcWidth, arcHeight);
+ }
+
+ /**
+ * drawLine
+ *
+ * @param x1 The x-position of the start
+ * @param y1 The y-position of the start
+ * @param x2 The x-position of the end
+ * @param y2 The y-position of the end
+ */
+ public void drawLine(int x1, int y1, int x2, int y2)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing line: from (" + x1 + ", "
+ + y1 + ") to (" + x2 + ", " + y2 + ")");
+ }
+
+ graphics.drawLine(x1, y1, x2, y2);
+ }
+
+ /**
+ * draw3DRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param raised TODO
+ */
+ public void draw3DRect(int x, int y, int width, int height, boolean raised)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing 3D rect: "
+ + new Rectangle(x, y, width, height)
+ + "Raised bezel: " + raised);
+ }
+
+ graphics.draw3DRect(x, y, width, height, raised);
+ }
+
+ /**
+ * fill3DRect
+ *
+ * @param x The x-position of the rectangle
+ * @param y The y-position of the rectangle
+ * @param width The width of the rectangle
+ * @param height The height of the rectangle
+ * @param raised TODO
+ */
+ public void fill3DRect(int x, int y, int width, int height, boolean raised)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling 3D rect: "
+ + new Rectangle(x, y, width, height)
+ + "Raised bezel: " + raised);
+ }
+
+ graphics.fill3DRect(x, y, width, height, raised);
+ }
+
+ /**
+ * drawOval
+ *
+ * @param x the x coordinate
+ * @param y the y coordiante
+ * @param width the width
+ * @param height the height
+ */
+ public void drawOval(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing oval: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.drawOval(x, y, width, height);
+ }
+
+ /**
+ * fillOval
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ */
+ public void fillOval(int x, int y, int width, int height)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling oval: "
+ + new Rectangle(x, y, width, height));
+ }
+
+ graphics.fillOval(x, y, width, height);
+ }
+
+ /**
+ * drawArc
+ *
+ * @param x the x coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @param startAngle TODO
+ * @param arcAngle TODO
+ */
+ public void drawArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing arc: "
+ + new Rectangle(x, y, width, height)
+ + " startAngle: " + startAngle
+ + " arcAngle: " + arcAngle);
+ }
+
+ graphics.drawArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ /**
+ * fillArc
+ *
+ * @param x the coordinate
+ * @param y the y coordinate
+ * @param width the width
+ * @param height the height
+ * @param startAngle TODO
+ * @param arcAngle TODO
+ */
+ public void fillArc(int x, int y, int width, int height,
+ int startAngle, int arcAngle)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Filling arc: "
+ + new Rectangle(x, y, width, height)
+ + " startAngle: " + startAngle
+ + " arcAngle: " + arcAngle);
+ }
+
+ graphics.fillArc(x, y, width, height, startAngle, arcAngle);
+ }
+
+ /**
+ * drawPolyline
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void drawPolyline(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polyline: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.drawPolyline(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * drawPolygon
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void drawPolygon(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.drawPolygon(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * fillPolygon
+ *
+ * @param xpoints TODO
+ * @param ypoints TODO
+ * @param npoints TODO
+ */
+ public void fillPolygon(int[] xpoints, int[] ypoints, int npoints)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing polygon: nPoints: " + npoints
+ + " X's: " + xpoints + " Y's: " + ypoints);
+ }
+
+ graphics.fillPolygon(xpoints, ypoints, npoints);
+ }
+
+ /**
+ * drawString
+ *
+ * @param string the string
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawString(String string, int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing string: \"" + string
+ + "\" at: " + new Point(x, y));
+ }
+
+ graphics.drawString(string, x, y);
+ }
+
+ /**
+ * drawString
+ *
+ * @param iterator TODO
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawString(AttributedCharacterIterator iterator,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing string: \"" + iterator
+ + "\" at: " + new Point(x, y));
+ }
+
+ graphics.drawString(iterator, x, y);
+ }
+
+ /**
+ * drawBytes
+ *
+ * @param data TODO
+ * @param offset TODO
+ * @param length TODO
+ * @param x the x coordinate
+ * @param y the y coordinate
+ */
+ public void drawBytes(byte[] data, int offset, int length,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Drawing bytes at: " + new Point(x, y));
+
+ graphics.drawBytes(data, offset, length, x, y);
+ }
+
+ /**
+ * drawChars
+ *
+ * @param data array of characters to draw
+ * @param offset offset in array
+ * @param length number of characters in array to draw
+ * @param x x-position
+ * @param y y-position
+ */
+ public void drawChars(char[] data, int offset, int length,
+ int x, int y)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ logStream().println(prefix() + " Drawing chars at: " + new Point(x, y));
+
+ if ((debugOptions & FLASH_OPTION) != 0)
+ {
+ Color color = graphics.getColor();
+ for (int index = 0; index < (debugFlashCount - 1); ++index)
+ {
+ graphics.setColor(color);
+ graphics.drawChars(data, offset, length, x, y);
+ sleep(debugFlashTime);
+ graphics.setColor(debugFlashColor);
+ graphics.drawChars(data, offset, length, x, y);
+ sleep(debugFlashTime);
+ }
+ graphics.setColor(color);
+ }
+
+ graphics.drawChars(data, offset, length, x, y);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param observer The image observer
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y,
+ ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image + " at: "
+ + new Point(x, y));
+ }
+
+ return graphics.drawImage(image, x, y, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param width The width of the area to draw the image
+ * @param height The height of the area to draw the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y, int width,
+ int height, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Rectangle(x, y, width, height));
+ }
+
+ return graphics.drawImage(image, x, y, width, height, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Point(x, y)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, x, y, background, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param x The x position
+ * @param y The y position
+ * @param width The width of the area to draw the image
+ * @param height The height of the area to draw the image
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int x, int y, int width, int height,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " at: " + new Rectangle(x, y, width, height)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, x, y, width, height, background, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param dx1 TODO
+ * @param dy1 TODO
+ * @param dx2 TODO
+ * @param dy2 TODO
+ * @param sx1 TODO
+ * @param sy1 TODO
+ * @param sx2 TODO
+ * @param sy2 TODO
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int dx1, int dy1,
+ int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
+ ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " destination: " + new Rectangle(dx1, dy1, dx2, dy2)
+ + " source: " + new Rectangle(sx1, sy1, sx2, sy2));
+ }
+
+ return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, observer);
+ }
+
+ /**
+ * drawImage
+ *
+ * @param image The image to draw
+ * @param dx1 TODO
+ * @param dy1 TODO
+ * @param dx2 TODO
+ * @param dy2 TODO
+ * @param sx1 TODO
+ * @param sy1 TODO
+ * @param sx2 TODO
+ * @param sy2 TODO
+ * @param background The color for the background in the opaque regions
+ * of the image
+ * @param observer The image observer
+ *
+ * @return boolean
+ */
+ public boolean drawImage(Image image, int dx1, int dy1,
+ int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
+ Color background, ImageObserver observer)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Drawing image: " + image
+ + " destination: " + new Rectangle(dx1, dy1, dx2, dy2)
+ + " source: " + new Rectangle(sx1, sy1, sx2, sy2)
+ + ", bgcolor: " + background);
+ }
+
+ return graphics.drawImage(image, dx1, dy1, dx2, dy2, sx1, sy1, sx2, sy2, background, observer);
+ }
+
+ /**
+ * copyArea
+ *
+ * @param x The x position of the source area
+ * @param y The y position of the source area
+ * @param width The width of the area
+ * @param height The height of the area
+ * @param destx The x position of the destination area
+ * @param desty The y posiiton of the destination area
+ */
+ public void copyArea(int x, int y, int width, int height,
+ int destx, int desty)
+ {
+ if ((debugOptions & LOG_OPTION) != 0)
+ {
+ logStream().println(prefix() + " Copying area from: "
+ + new Rectangle(x, y, width, height)
+ + " to: " + new Point(destx, desty));
+ }
+
+ graphics.copyArea(x, y, width, height, destx, desty);
+ }
+
+ /**
+ * Releases all system resources that this Graphics is using.
+ */
+ public void dispose()
+ {
+ graphics.dispose();
+ graphics = null;
+ }
+
+ /**
+ * isDrawingBuffer
+ *
+ * @return boolean
+ */
+ public boolean isDrawingBuffer()
+ {
+ return false; // TODO
+ }
+
+ /**
+ * setDebugOptions
+ *
+ * @param options the debug options
+ */
+ public void setDebugOptions(int options)
+ {
+ debugOptions = options;
+ if ((debugOptions & LOG_OPTION) != 0)
+ if (options == NONE_OPTION)
+ logStream().println(prefix() + "Disabling debug");
+ else
+ logStream().println(prefix() + "Enabling debug");
+ }
+
+ /**
+ * getDebugOptions
+ *
+ * @return the debug options
+ */
+ public int getDebugOptions()
+ {
+ return debugOptions;
+ }
+
+ /**
+ * Creates and returns the prefix that should be prepended to all logging
+ * messages. The prefix is made up like this:
+ *
+ * Graphics(-1) where counter is an integer number
+ * saying how many DebugGraphics objects have been created so far. The second
+ * number always seem to be 1 on Sun's JDK, this has to be investigated a
+ * little more.
+ *
+ * @return the prefix that should be prepended to all logging
+ * messages
+ */
+ private String prefix()
+ {
+ return "Graphics(" + counter + "-1)";
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
new file mode 100644
index 000000000..c1d0fe74a
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultBoundedRangeModel.java
@@ -0,0 +1,475 @@
+/* DefaultBoundedRangeModel.java -- Default implementation
+ of BoundedRangeModel.
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of BoundedRangeModel.
+ *
+ * @author Andrew Selkirk (aselkirk@sympatico.ca)
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class DefaultBoundedRangeModel
+ implements BoundedRangeModel, Serializable
+{
+ /**
+ * The identifier of this class in object serialization. Verified
+ * using the serialver tool of Sun J2SE 1.4.1_01.
+ */
+ private static final long serialVersionUID = 5034068491295259790L;
+
+ /**
+ * An event that is sent to all registered {@link ChangeListener}s
+ * when the state of this range model has changed.
+ *
+ *
The event object is created on demand, the first time it
+ * is actually needed.
+ *
+ * @see #fireStateChanged()
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * The list of the currently registered EventListeners.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * The current value of the range model, which is always between
+ * {@link #minimum} and ({@link #maximum} - {@link #extent}). In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * value is displayed as the position of the thumb.
+ */
+ private int value;
+
+ /**
+ * The current extent of the range model, which is a number greater
+ * than or equal to zero. In a scroll bar visualization of a {@link
+ * BoundedRangeModel}, the extent is displayed as the
+ * size of the thumb.
+ */
+ private int extent;
+
+ /**
+ * The current minimum value of the range model, which is always
+ * less than or equal to {@link #maximum}.
+ */
+ private int minimum;
+
+ /**
+ * The current maximum value of the range model, which is always
+ * greater than or equal to {@link #minimum}.
+ */
+ private int maximum;
+
+ /**
+ * A property that indicates whether the value of this {@link
+ * BoundedRangeModel} is going to change in the immediate future.
+ */
+ private boolean isAdjusting;
+
+ /**
+ * Constructs a DefaultBoundedRangeModel with default
+ * values for the properties. The properties value,
+ * extent and minimum will be initialized
+ * to zero; maximum will be set to 100; the property
+ * valueIsAdjusting will be false.
+ */
+ public DefaultBoundedRangeModel()
+ {
+ // The fields value, extent, minimum have the default value 0, and
+ // isAdjusting is already false. These fields no not need to be
+ // set explicitly.
+ maximum = 100;
+ }
+
+ /**
+ * Constructs a DefaultBoundedRangeModel with the
+ * specified values for some properties.
+ *
+ * @param value the initial value of the range model, which must be
+ * a number between minimum and (maximum -
+ * extent). In a scroll bar visualization of a {@link
+ * BoundedRangeModel}, the value is displayed as the
+ * position of the thumb.
+ * @param extent the initial extent of the range model, which is a
+ * number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * extent is displayed as the size of the thumb.
+ * @param minimum the initial minimal value of the range model.
+ * @param maximum the initial maximal value of the range model.
+ *
+ * @throws IllegalArgumentException if the following condition is
+ * not satisfied: minimum <= value <= value + extent <=
+ * maximum.
+ */
+ public DefaultBoundedRangeModel(int value, int extent, int minimum,
+ int maximum)
+ {
+ if (!(minimum <= value && extent >= 0 && (value + extent) <= maximum))
+ throw new IllegalArgumentException();
+
+ this.value = value;
+ this.extent = extent;
+ this.minimum = minimum;
+ this.maximum = maximum;
+
+ // The isAdjusting field already has a false value by default.
+ }
+
+ /**
+ * Returns a string with all relevant properties of this range
+ * model.
+ *
+ * @return a string representing the object
+ */
+ public String toString()
+ {
+ return getClass().getName()
+ + "[value=" + value
+ + ", extent=" + extent
+ + ", min=" + minimum
+ + ", max=" + maximum
+ + ", adj=" + isAdjusting
+ + ']';
+ }
+
+ /**
+ * Returns the current value of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * value is displayed as the position of the thumb.
+ *
+ * @return the value
+ */
+ public int getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Changes the current value of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * value is displayed as the position of the thumb;
+ * changing the value of a scroll bar's model
+ * thus moves the thumb to a different position.
+ *
+ * @param value the value
+ */
+ public void setValue(int value)
+ {
+ value = Math.max(minimum, value);
+ if (value + extent > maximum)
+ value = maximum - extent;
+
+ if (value != this.value)
+ {
+ this.value = value;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current extent of this bounded range model, which is
+ * a number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * extent is displayed as the size of the thumb.
+ *
+ * @return the extent
+ */
+ public int getExtent()
+ {
+ return extent;
+ }
+
+ /**
+ * Changes the current extent of this bounded range model. In a
+ * scroll bar visualization of a {@link BoundedRangeModel}, the
+ * extent is displayed as the size of the thumb.
+ *
+ * @param extent the new extent of the range model, which is a
+ * number greater than or equal to zero.
+ */
+ public void setExtent(int extent)
+ {
+ extent = Math.max(extent, 0);
+ if (value + extent > maximum)
+ extent = maximum - value;
+
+ if (extent != this.extent)
+ {
+ this.extent = extent;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current minimal value of this bounded range model.
+ */
+ public int getMinimum()
+ {
+ return minimum;
+ }
+
+ /**
+ * Changes the current minimal value of this bounded range model.
+ *
+ * @param minimum the new minimal value.
+ */
+ public void setMinimum(int minimum)
+ {
+ int value, maximum;
+
+ maximum = Math.max(minimum, this.maximum);
+ value = Math.max(minimum, this.value);
+
+ setRangeProperties(value, extent, minimum, maximum, isAdjusting);
+ }
+
+ /**
+ * Returns the current maximal value of this bounded range model.
+ *
+ * @return the maximum
+ */
+ public int getMaximum()
+ {
+ return maximum;
+ }
+
+ /**
+ * Changes the current maximal value of this bounded range model.
+ *
+ * @param maximum the new maximal value.
+ */
+ public void setMaximum(int maximum)
+ {
+ int value, extent, minimum;
+
+ minimum = Math.min(this.minimum, maximum);
+ extent = Math.min(this.extent, maximum - minimum);
+ value = Math.min(this.value, maximum - extent);
+
+ setRangeProperties(value, extent, minimum, maximum, isAdjusting);
+ }
+
+ /**
+ * Returns whether or not the value of this bounded range model is
+ * going to change in the immediate future. Scroll bars set this
+ * property to true while the thumb is being dragged
+ * around; when the mouse is relased, they set the property to
+ * false and post a final {@link ChangeEvent}.
+ *
+ * @return true if the value will change soon again;
+ * false if the value will probably not change soon.
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return isAdjusting;
+ }
+
+ /**
+ * Specifies whether or not the value of this bounded range model is
+ * going to change in the immediate future. Scroll bars set this
+ * property to true while the thumb is being dragged
+ * around; when the mouse is relased, they set the property to
+ * false.
+ *
+ * @param isAdjusting true if the value will change
+ * soon again; false if the value will probably not
+ * change soon.
+ */
+ public void setValueIsAdjusting(boolean isAdjusting)
+ {
+ if (isAdjusting == this.isAdjusting)
+ return;
+
+ this.isAdjusting = isAdjusting;
+ fireStateChanged();
+ }
+
+ /**
+ * Sets all properties.
+ *
+ * @param value the new value of the range model. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * value is displayed as the position of the thumb.
+ * @param extent the new extent of the range model, which is a
+ * number greater than or equal to zero. In a scroll bar
+ * visualization of a {@link BoundedRangeModel}, the
+ * extent is displayed as the size of the thumb.
+ * @param minimum the new minimal value of the range model.
+ * @param maximum the new maximal value of the range model.
+ * @param isAdjusting whether or not the value of this bounded range
+ * model is going to change in the immediate future. Scroll bars set
+ * this property to true while the thumb is being
+ * dragged around; when the mouse is relased, they set the property
+ * to false.
+ */
+ public void setRangeProperties(int value, int extent, int minimum,
+ int maximum, boolean isAdjusting)
+ {
+ minimum = Math.min(Math.min(minimum, maximum), value);
+ maximum = Math.max(value, maximum);
+ if (extent + value > maximum)
+ extent = maximum - value;
+ extent = Math.max(0, extent);
+
+ if ((value == this.value)
+ && (extent == this.extent)
+ && (minimum == this.minimum)
+ && (maximum == this.maximum)
+ && (isAdjusting == this.isAdjusting))
+ return;
+
+ this.value = value;
+ this.extent = extent;
+ this.minimum = minimum;
+ this.maximum = maximum;
+ this.isAdjusting = isAdjusting;
+
+ fireStateChanged();
+ }
+
+ /**
+ * Subscribes a ChangeListener to state changes.
+ *
+ * @param listener the listener to be subscribed.
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Cancels the subscription of a ChangeListener.
+ *
+ * @param listener the listener to be unsubscribed.
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to any registered {@link
+ * ChangeListener}s.
+ *
+ * @see #addChangeListener(ChangeListener)
+ * @see #removeChangeListener(ChangeListener)
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+
+ for (int i = listeners.length - 1; i >= 0; --i)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Retrieves the current listeners of the specified class.
+ *
+ * @param listenerType the class of listeners; usually {@link
+ * ChangeListener}.class.
+ *
+ * @return an array with the currently subscribed listeners, or
+ * an empty array if there are currently no listeners.
+ *
+ * @since 1.3
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns all ChangeListeners that are currently
+ * subscribed for changes to this
+ * DefaultBoundedRangeModel.
+ *
+ * @return an array with the currently subscribed listeners, or
+ * an empty array if there are currently no listeners.
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Provides serialization support.
+ *
+ * @param stream the output stream (null not permitted).
+ *
+ * @throws IOException if there is an I/O error.
+ */
+ private void writeObject(ObjectOutputStream stream)
+ throws IOException
+ {
+ stream.defaultWriteObject();
+ }
+
+ /**
+ * Provides serialization support.
+ *
+ * @param stream the input stream (null not permitted).
+ *
+ * @throws IOException if there is an I/O error.
+ * @throws ClassNotFoundException if there is a classpath problem.
+ */
+ private void readObject(ObjectInputStream stream)
+ throws ClassNotFoundException, IOException
+ {
+ stream.defaultReadObject();
+ listenerList = new EventListenerList();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/DefaultButtonModel.java b/libjava/classpath/javax/swing/DefaultButtonModel.java
new file mode 100644
index 000000000..d29a23ed3
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultButtonModel.java
@@ -0,0 +1,578 @@
+/* DefaultButtonModel.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.ItemSelectable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of {@link ButtonModel}.
+ * The purpose of this class is to model the dynamic state of an abstract
+ * button. The concrete button type holding this state may be a a "toggle"
+ * button (checkbox, radio button) or a "push" button (menu button, button).
+ * If the model is disabled, only the "selected" property can be changed. An
+ * attempt to change the "armed", "rollover" or "pressed" properties while
+ * the model is disabled will be blocked. Any successful (non-blocked) change
+ * to the model's properties will trigger the firing of a ChangeEvent. Any
+ * change to the "selected" property will trigger the firing of an ItemEvent
+ * in addition to ChangeEvent. This is true whether the model is enabled or
+ * not. One other state change is special: the transition from "enabled,
+ * armed and pressed" to "enabled, armed and not-pressed". This is considered
+ * the "trailing edge" of a successful mouse click, and therefore fires an
+ * ActionEvent in addition to a ChangeEvent. In all other respects this class
+ * is just a container of boolean flags.
+ *
+ * @author Graydon Hoare (graydon_at_redhat.com)
+ */
+public class DefaultButtonModel implements ButtonModel, Serializable
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -5342609566534980231L;
+
+ /**
+ * Indicates that the button is partially committed to being
+ * pressed, but not entirely. This usually happens when a user has pressed
+ * but not yet released the mouse button.
+ */
+ public static final int ARMED = 1;
+
+ /**
+ * State constant indicating that the button is enabled. Buttons cannot be
+ * pressed or selected unless they are enabled.
+ */
+ public static final int ENABLED = 8;
+
+ /**
+ * State constant indicating that the user is holding down the button. When
+ * this transitions from true to false, an ActionEvent may be fired,
+ * depending on the value of the "armed" property.
+ */
+ public static final int PRESSED = 4;
+
+ /**
+ * State constant indicating that the mouse is currently positioned over the
+ * button.
+ */
+ public static final int ROLLOVER = 16;
+
+ /**
+ * State constant indicating that the button is selected. This constant is
+ * only meaningful for toggle-type buttons (radio buttons, checkboxes).
+ */
+ public static final int SELECTED = 2;
+
+ /**
+ * Represents the "state properties" (armed, enabled, pressed, rollover and
+ * selected) by a bitwise combination of integer constants.
+ */
+ protected int stateMask = ENABLED;
+
+ /**
+ * List of ItemListeners, ChangeListeners, and ActionListeners registered on
+ * this model.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /** The single ChangeEvent this model (re)uses to call its ChangeListeners. */
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /**
+ * The group this model belongs to. Only one button in a group may be
+ * selected at any given time.
+ */
+ protected ButtonGroup group;
+
+ /**
+ * The key code (one of {@link java.awt.event.KeyEvent} VK_) used to press
+ * this button via a keyboard interface.
+ */
+ protected int mnemonic = KeyEvent.VK_UNDEFINED;
+
+ /**
+ * The string used as the "command" property of any ActionEvent this model
+ * sends.
+ */
+ protected String actionCommand;
+
+ /**
+ * Creates a new DefaultButtonModel object.
+ */
+ public DefaultButtonModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Return null. Use {@link AbstractButton} if you wish to
+ * interface with a button via an {@link ItemSelectable} interface.
+ *
+ * @return null
+ */
+ public Object[] getSelectedObjects()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a specified class of listeners.
+ *
+ * @param listenerType the type of listener to return
+ *
+ * @return array of listeners
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Add an ActionListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Remove an ActionListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+
+ /**
+ * Returns all registered ActionListener objects.
+ *
+ * @return array of ActionListener objects
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Add an ItemListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addItemListener(ItemListener l)
+ {
+ listenerList.add(ItemListener.class, l);
+ }
+
+ /**
+ * Remove an ItemListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeItemListener(ItemListener l)
+ {
+ listenerList.remove(ItemListener.class, l);
+ }
+
+ /**
+ * Returns all registered ItemListener objects.
+ *
+ * @return array of ItemListener objects
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) listenerList.getListeners(ItemListener.class);
+ }
+
+ /**
+ * Add a ChangeListener to the model. Usually only called to subscribe an
+ * AbstractButton's listener to the model.
+ *
+ * @param l The listener to add
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * Remove a ChangeListener to the model. Usually only called to unsubscribe
+ * an AbstractButton's listener to the model.
+ *
+ * @param l The listener to remove
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * Returns all registered ChangeListener objects.
+ *
+ * @return array of ChangeListener objects
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Inform each ItemListener in the {@link #listenerList} that an ItemEvent
+ * has occurred. This happens in response to any change to the {@link
+ * #stateMask} field.
+ *
+ * @param e The ItemEvent to fire
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ ItemListener[] ll = getItemListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].itemStateChanged(e);
+ }
+
+ /**
+ * Inform each ActionListener in the {@link #listenerList} that an
+ * ActionEvent has occurred. This happens in response to the any change to
+ * the {@link #stateMask} field which makes the enabled, armed and pressed
+ * properties all simultaneously true.
+ *
+ * @param e The ActionEvent to fire
+ */
+ protected void fireActionPerformed(ActionEvent e)
+ {
+ ActionListener[] ll = getActionListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].actionPerformed(e);
+ }
+
+ /**
+ * Inform each ChangeListener in the {@link #listenerList} that a ChangeEvent
+ * has occurred. This happens in response to the any change to a property
+ * of the model.
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] ll = getChangeListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Get the value of the model's "armed" property.
+ *
+ * @return The current "armed" property
+ */
+ public boolean isArmed()
+ {
+ return (stateMask & ARMED) == ARMED;
+ }
+
+ /**
+ * Set the value of the model's "armed" property.
+ *
+ * @param a The new "armed" property
+ */
+ public void setArmed(boolean a)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((a && isArmed()) || (!a && !isArmed()))
+ return;
+
+ // cannot change ARMED state unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (a)
+ stateMask = stateMask | ARMED;
+ else
+ stateMask = stateMask & (~ARMED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Get the value of the model's "enabled" property.
+ *
+ * @return The current "enabled" property.
+ */
+ public boolean isEnabled()
+ {
+ return (stateMask & ENABLED) == ENABLED;
+ }
+
+ /**
+ * Set the value of the model's "enabled" property.
+ *
+ * @param e The new "enabled" property
+ */
+ public void setEnabled(boolean e)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((e && isEnabled()) || (!e && !isEnabled()))
+ return;
+
+ // make the change
+ if (e)
+ stateMask = stateMask | ENABLED;
+ else
+ stateMask = stateMask & (~ENABLED) & (~ARMED) & (~PRESSED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Set the value of the model's "pressed" property.
+ *
+ * @param p The new "pressed" property
+ */
+ public void setPressed(boolean p)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((p && isPressed()) || (!p && !isPressed()))
+ return;
+
+ // cannot changed PRESSED state unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (p)
+ stateMask = stateMask | PRESSED;
+ else
+ stateMask = stateMask & (~PRESSED);
+
+ // if button is armed and was released, fire action event
+ if (!p && isArmed())
+ fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Get the value of the model's "pressed" property.
+ *
+ * @return The current "pressed" property
+ */
+ public boolean isPressed()
+ {
+ return (stateMask & PRESSED) == PRESSED;
+ }
+
+ /**
+ * Set the value of the model's "rollover" property.
+ *
+ * @param r The new "rollover" property
+ */
+ public void setRollover(boolean r)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if (r == isRollover())
+ return;
+
+ // cannot set ROLLOVER property unless button is enabled
+ if (!isEnabled())
+ return;
+
+ // make the change
+ if (r)
+ stateMask = stateMask | ROLLOVER;
+ else
+ stateMask = stateMask & (~ROLLOVER);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+ }
+
+ /**
+ * Set the value of the model's "selected" property.
+ *
+ * @param s The new "selected" property
+ */
+ public void setSelected(boolean s)
+ {
+ // if this call does not represent a CHANGE in state, then return
+ if ((s && isSelected()) || (!s && !isSelected()))
+ return;
+
+ // make the change
+ if (s)
+ stateMask = stateMask | SELECTED;
+ else
+ stateMask = stateMask & (~SELECTED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+
+ // fire ItemStateChanged events
+ if (s)
+ {
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ this, ItemEvent.SELECTED));
+ if (group != null)
+ group.setSelected(this, true);
+ }
+ else
+ {
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ this, ItemEvent.DESELECTED));
+ if (group != null)
+ group.setSelected(this, false);
+ }
+ }
+
+ /**
+ * Get the value of the model's "selected" property.
+ *
+ * @return The current "selected" property
+ */
+ public boolean isSelected()
+ {
+ return (stateMask & SELECTED) == SELECTED;
+ }
+
+ /**
+ * Get the value of the model's "rollover" property.
+ *
+ * @return The current "rollover" property
+ */
+ public boolean isRollover()
+ {
+ return (stateMask & ROLLOVER) == ROLLOVER;
+ }
+
+ /**
+ * Get the value of the model's "mnemonic" property.
+ *
+ * @return The current "mnemonic" property
+ */
+ public int getMnemonic()
+ {
+ return mnemonic;
+ }
+
+ /**
+ * Set the value of the model's "mnemonic" property.
+ *
+ * @param key The new "mnemonic" property
+ */
+ public void setMnemonic(int key)
+ {
+ if (mnemonic != key)
+ {
+ mnemonic = key;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Set the value of the model's "actionCommand" property. This property is
+ * used as the "command" property of the {@link ActionEvent} fired from the
+ * model.
+ *
+ * @param s The new "actionCommand" property.
+ */
+ public void setActionCommand(String s)
+ {
+ if (actionCommand != s)
+ {
+ actionCommand = s;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current value of the model's "actionCommand" property.
+ *
+ * @return The current "actionCommand" property
+ */
+ public String getActionCommand()
+ {
+ return actionCommand;
+ }
+
+ /**
+ * Set the value of the model's "group" property. The model is said to be a
+ * member of the {@link ButtonGroup} held in its "group" property, and only
+ * one model in a given group can have their "selected" property be
+ * true at a time.
+ *
+ * @param g The new "group" property (null permitted).
+ *
+ * @see #getGroup()
+ */
+ public void setGroup(ButtonGroup g)
+ {
+ group = g;
+ }
+
+ /**
+ * Returns the current value of the model's "group" property.
+ *
+ * @return The value of the "group" property
+ *
+ * @see #setGroup(ButtonGroup)
+ */
+ public ButtonGroup getGroup()
+ {
+ return group;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultCellEditor.java b/libjava/classpath/javax/swing/DefaultCellEditor.java
new file mode 100644
index 000000000..46f637071
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultCellEditor.java
@@ -0,0 +1,570 @@
+/* DefaultCellEditor.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.MouseEvent;
+import java.io.Serializable;
+import java.util.EventObject;
+
+import javax.swing.JTable;
+import javax.swing.JTextField;
+import javax.swing.event.CellEditorListener;
+import javax.swing.table.TableCellEditor;
+import javax.swing.tree.TreeCellEditor;
+
+/**
+ * The default implementation of {@link TableCellEditor} and
+ * {@link TreeCellEditor}. It provides editor components for
+ * some standard object types.
+ *
+ * @author Andrew Selkirk
+ * @author Audrius Meskauskas
+ */
+public class DefaultCellEditor
+ extends AbstractCellEditor
+ implements TableCellEditor, TreeCellEditor
+{
+ private static final long serialVersionUID = 3564035141373880027L;
+
+ /**
+ * This changeable module access the editor component in the component
+ * specific way. For instance, to set the value for JTextField, we need to
+ * call setText(String), and for JCheckBox we need to call
+ * setSelected(boolean). Each default editor has the component specific
+ * derivative of this class. These derivatives are private inner classes of
+ * the DefaultCellEditor.
+ *
+ * The editor delegate is also set for the editor component as the action
+ * listener. It listens for the events that indicate that editing has stopped.
+ */
+ protected class EditorDelegate
+ implements ActionListener, ItemListener, Serializable
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = -1420007406015481933L;
+
+ /**
+ * The object value (updated when getting and setting the value).
+ */
+ protected Object value;
+
+ /**
+ * Constructor EditorDelegate
+ */
+ protected EditorDelegate()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Set the value for the editor component. This method is normally
+ * overridden to set the value in the way, specific for the text
+ * component, check box or combo box.
+ *
+ * @param aValue the value to set (String, Boolean or Number).
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ }
+
+ /**
+ * Get the value for the editor component. This method is normally
+ * overridden to obtain the value in the way, specific for the text
+ * component, check box or combo box.
+ *
+ * @return value the value of the component (String, Boolean or Number).
+ */
+ public Object getCellEditorValue()
+ {
+ return value;
+ }
+
+ /**
+ * The default method returns true for the {@link MouseEvent} and false
+ * for any other events.
+ *
+ * @param event the event to check
+ *
+ * @return true if the passed event is the mouse event and false otherwise.
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ if (event == null || !(event instanceof MouseEvent) ||
+ (((MouseEvent) event).getClickCount() >= getClickCountToStart()))
+ return true;
+ return false;
+ } // isCellEditable()
+
+ /**
+ * Returns true to indicate that the editing cell can be selected.
+ *
+ * The default method returns true without action but may be overridden
+ * in derived classes for more specific behavior.
+ *
+ * @param event unused in default method
+ *
+ * @return true always
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ // return true to indicate that the editing cell may be selected
+ return true;
+ }
+
+ /**
+ * Finish the cell editing session. This method notifies the registered
+ * cell editor listeners (including the table) that the editing has been
+ * stopped.
+ *
+ * @return boolean
+ */
+ public boolean stopCellEditing()
+ {
+ fireEditingStopped();
+ return true;
+ } // stopCellEditing()
+
+ /**
+ * Cancel the cell editing session. This method notifies the registered
+ * cell editor listeners (including the table) that the editing has been
+ * canceled.
+ */
+ public void cancelCellEditing()
+ {
+ fireEditingCanceled();
+ } // cancelCellEditing()
+
+ /**
+ * Start editing session and returns true to indicate the editing has begun.
+ * The default method returns true without action but may be overridden
+ * in derived classes for more specific behavior.
+ *
+ * @param event the event.
+ *
+ * @return true, always
+ */
+ public boolean startCellEditing(EventObject event)
+ {
+ // return true to indicate that editing has begun
+ return true;
+ } // startCellEditing()
+
+ /**
+ * This event is fired by the editor component (for instance, by pressing
+ * ENTER in the {@link JTextField}. The default method delegates call to
+ * the {@link #stopCellEditing}, finishing the editing session.
+ *
+ * @param event unused in default method
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ stopCellEditing();
+ } // actionPerformed()
+
+ /**
+ * This event is fired by the editor component.The default method delegates
+ * call to the {@link #stopCellEditing}, finishing the editing session.
+ *
+ * @param event unused in default method
+ */
+ public void itemStateChanged(ItemEvent event)
+ {
+ stopCellEditing();
+ } // itemStateChanged()
+
+ /**
+ * Notify the registered listeners (including the table) that the editing
+ * has been completed.
+ */
+ void fireEditingStopped()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingStopped(changeEvent);
+
+ }
+
+ /**
+ * Notify the registered listeners (including the table) that the editing
+ * has been canceled.
+ */
+ void fireEditingCanceled()
+ {
+ CellEditorListener[] listeners = getCellEditorListeners();
+ for (int index = 0; index < listeners.length; index++)
+ listeners[index].editingCanceled(changeEvent);
+ }
+ } // EditorDelegate
+
+ /**
+ * Provides getter and setter methods to work with the text component.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JTextFieldDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param aValue the value to set (toString() will be called).
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ JTextField f = (JTextField) editorComponent;
+ if (value == null)
+ f.setText("");
+ else
+ f.setText(value.toString());
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (String)
+ */
+ public Object getCellEditorValue()
+ {
+ JTextField f = (JTextField) editorComponent;
+ return value = f.getText();
+ }
+ }
+
+ /**
+ * Provides getter and setter methods to work with the combo box.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JComboBoxDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param aValue the value to set.
+ */
+ public void setValue(Object aValue)
+ {
+ value = aValue;
+ JComboBox c = (JComboBox) editorComponent;
+ if (value != null)
+ c.setSelectedItem(value);
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (as String)
+ */
+ public Object getCellEditorValue()
+ {
+ JComboBox c = (JComboBox) editorComponent;
+ return value = c.getSelectedItem();
+ }
+
+ /**
+ * Returns true to indicate that the editing cell can be selected. If the
+ * check box is not editable, expands it. If it is editable, brings
+ * focus to the editor field.
+ *
+ * @param event unused in default method
+ *
+ * @return true always
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ JComboBox c = (JComboBox) editorComponent;
+ if (!c.isEditable)
+ c.showPopup();
+ return true;
+ }
+ }
+
+ /**
+ * Provides getter and setter methods to work with the check box.
+ *
+ * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
+ */
+ private class JCheckBoxDelegate extends EditorDelegate
+ {
+ /**
+ * Use the serial version UID for interoperability.
+ */
+ private static final long serialVersionUID = 1;
+
+ /**
+ * Set the value for the editor component.
+ *
+ * @param value the value to set (must be Boolean).
+ */
+ public void setValue(Object value)
+ {
+ JCheckBox c = (JCheckBox) editorComponent;
+
+ if (value == null)
+ c.setSelected(false);
+ else
+ c.setSelected( ((Boolean) value).booleanValue());
+ }
+
+ /**
+ * Get the value for the editor component.
+ *
+ * @return value the value of the component (must be CharSequence)
+ */
+ public Object getCellEditorValue()
+ {
+ JCheckBox c = (JCheckBox) editorComponent;
+ value = c.isSelected() ? Boolean.TRUE : Boolean.FALSE;
+ return value;
+ }
+ }
+
+ /**
+ * The Swing JComponent, performing the editing session.
+ */
+ protected JComponent editorComponent;
+
+ /**
+ * The editor delegate, responsible for listening the {@link #editorComponent}
+ * events and getting/setting its value.
+ */
+ protected EditorDelegate delegate;
+
+ /**
+ * The number of the mouse clicks, required to start the editing session.
+ */
+ protected int clickCountToStart;
+
+ /**
+ * Create the DefaultCellEditor that uses the text field as its editor
+ * component (appropriate for the text content)
+ *
+ * @param textfield the text field as will be used as the editor component
+ */
+ public DefaultCellEditor(JTextField textfield)
+ {
+ editorComponent = textfield;
+ clickCountToStart = 2;
+ delegate = new JTextFieldDelegate();
+ textfield.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Constructor DefaultCellEditor that uses the checkbox (appropriate
+ * for boolean values)
+ *
+ * @param checkbox the checkbox that will be used with this editor.
+ */
+ public DefaultCellEditor(JCheckBox checkbox)
+ {
+ editorComponent = checkbox;
+ clickCountToStart = 1;
+ delegate = new JCheckBoxDelegate();
+ checkbox.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Constructor DefaultCellEditor that uses the combo box.
+ *
+ * @param combobox the combo box that will be used with this editor.
+ */
+ public DefaultCellEditor(JComboBox combobox)
+ {
+ editorComponent = combobox;
+ clickCountToStart = 1;
+ delegate = new JComboBoxDelegate();
+ combobox.addActionListener(delegate);
+ } // DefaultCellEditor()
+
+ /**
+ * Get the component that performs the editing sessions. It is the same
+ * component that was passed in constructor.
+ *
+ * @return the component, performing the editing sessions.
+ */
+ public Component getComponent()
+ {
+ return editorComponent;
+ } // getComponent()
+
+ /**
+ * Get the number of mouse clicks, required to start the editing session.
+ *
+ * @return int the number of mouse clicks, required to start the session
+ */
+ public int getClickCountToStart()
+ {
+ return clickCountToStart;
+ } // getClickCountToStart()
+
+ /**
+ * Set the number of mouse clicks, required to start the editing session.
+ *
+ * @param count the number of clicks, required to start the session
+ */
+ public void setClickCountToStart(int count)
+ {
+ clickCountToStart = count;
+ } // setClickCountToStart()
+
+ /**
+ * Get the value, currently being displayed by the editor component. The
+ * call is forwarded to the {@link #delegate}.
+ *
+ * @return Object the value (class depends on the editor component)
+ */
+ public Object getCellEditorValue()
+ {
+ return delegate.getCellEditorValue();
+ } // getCellEditorValue()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @param event forwarded to the delegate.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean isCellEditable(EventObject event)
+ {
+ return delegate.isCellEditable(event);
+ } // isCellEditable()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @param event forwarded to the delegate.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean shouldSelectCell(EventObject event)
+ {
+ return delegate.shouldSelectCell(event);
+ } // shouldSelectCell()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ *
+ * @return boolean returned by delegate
+ */
+ public boolean stopCellEditing()
+ {
+ return delegate.stopCellEditing();
+ } // stopCellEditing()
+
+ /**
+ * Forwards call to the {@link #delegate}.
+ */
+ public void cancelCellEditing()
+ {
+ delegate.cancelCellEditing();
+ } // cancelCellEditing()
+
+ /**
+ * Sets an initial value for the editor.
+ * This will cause the editor to stopEditing and lose any partially
+ * edited value if the editor is editing when this method is called.
+ * Returns the component that should be added to the client's Component
+ * hierarchy. Once installed in the client's hierarchy this component will
+ * then be able to draw and receive user input.
+ *
+ * @param tree - the JTree that is asking the editor to edit; this
+ * parameter can be null
+ * @param value - the value of the cell to be edited
+ * @param isSelected - true is the cell is to be renderer with selection
+ * highlighting
+ * @param expanded - true if the node is expanded
+ * @param leaf - true if the node is a leaf node
+ * @param row - the row index of the node being edited
+ *
+ * @return Component the component for editing
+ */
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean isSelected,
+ boolean expanded, boolean leaf,
+ int row)
+ {
+ delegate.setValue(value);
+ return editorComponent;
+ } // getTreeCellEditorComponent()
+
+ /**
+ * Get the cell editor component that will perform the editing session. If
+ * returned once, the same component is also returned on the repetetive calls
+ * again (reused).
+ *
+ * @param table the table where the editing is performed
+ * @param value the current value of the table. It is set as the initial
+ * component value.
+ * @param isSelected if true, the cell is currently selected
+ * @param row the row of the cell being edited
+ * @param column the column of the cell being edited
+ *
+ * @return Component the component that will perform the editing session
+ */
+ public Component getTableCellEditorComponent(JTable table, Object value,
+ boolean isSelected, int row,
+ int column)
+ {
+ // NOTE: as specified by Sun, we don't call new() everytime, we return
+ // editorComponent on each call to getTableCellEditorComponent or
+ // getTreeCellEditorComponent.
+ delegate.setValue(value);
+ return editorComponent;
+ } // getTableCellEditorComponent()
+
+}
diff --git a/libjava/classpath/javax/swing/DefaultComboBoxModel.java b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
new file mode 100644
index 000000000..a90b89d93
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultComboBoxModel.java
@@ -0,0 +1,286 @@
+/* DefaultComboBoxModel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Vector;
+
+import javax.swing.event.ListDataEvent;
+
+
+/**
+ * A model that stores a list of elements and a selected item (which may be
+ * null). Changes to the model are signalled to listeners using
+ * {@link ListDataEvent}. This model is designed for use by the
+ * {@link JComboBox} component.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class DefaultComboBoxModel extends AbstractListModel
+ implements MutableComboBoxModel, Serializable
+{
+ private static final long serialVersionUID = 6698657703676921904L;
+
+ /**
+ * Storage for the elements in the model's list.
+ */
+ private Vector list;
+
+ /**
+ * The selected item (null indicates no selection).
+ */
+ private Object selectedItem = null;
+
+ /**
+ * Creates a new model, initially empty.
+ */
+ public DefaultComboBoxModel()
+ {
+ list = new Vector();
+ }
+
+ /**
+ * Creates a new model and initializes its item list to the values in the
+ * given array. The selected item is set to the first item in the array, or
+ * null if the array length is zero.
+ *
+ * @param items an array containing items for the model (null
+ * not permitted).
+ *
+ * @throws NullPointerException if items is null.
+ */
+ public DefaultComboBoxModel(Object[] items)
+ {
+ list = new Vector(Arrays.asList(items));
+ if (list.size() > 0)
+ selectedItem = list.get(0);
+ }
+
+ /**
+ * Creates a new model and initializes its item list to the values in the
+ * given vector. The selected item is set to the first item in the vector,
+ * or null if the vector length is zero.
+ *
+ * @param vector a vector containing items for the model (null
+ * not permitted).
+ *
+ * @throws NullPointerException if vector is null.
+ */
+ public DefaultComboBoxModel(Vector> vector)
+ {
+ this.list = vector;
+ if (getSize() > 0)
+ selectedItem = vector.get(0);
+ }
+
+ /**
+ * Adds an element to the model's item list and sends a {@link ListDataEvent}
+ * to all registered listeners. If the new element is the first item added
+ * to the list, and the selected item is null, the new element
+ * is set as the selected item.
+ *
+ * @param object item to add to the model's item list.
+ */
+ public void addElement(Object object)
+ {
+ list.addElement(object);
+ int index = list.size() - 1;
+ fireIntervalAdded(this, index, index);
+ if (list.size() == 1 && selectedItem == null)
+ setSelectedItem(object);
+ }
+
+ /**
+ * Removes the element at the specified index from the model's item list
+ * and sends a {@link ListDataEvent} to all registered listeners. If the
+ * element removed was the selected item, then the preceding element becomes
+ * the new selected item (or the next element, if there is no preceding
+ * element).
+ *
+ * @param index the index of the item to remove.
+ *
+ * @throws ArrayIndexOutOfBoundsException if index is out of
+ * bounds.
+ */
+ public void removeElementAt(int index)
+ {
+ int selected = getIndexOf(selectedItem);
+ if (selected == index) // choose a new selected item
+ {
+ if (selected > 0)
+ setSelectedItem(getElementAt(selected - 1));
+ else
+ setSelectedItem(getElementAt(selected + 1));
+ }
+ list.removeElementAt(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ /**
+ * Adds an element at the specified index in the model's item list
+ * and sends a {@link ListDataEvent} to all registered listeners.
+ *
+ * @param object element to insert
+ * @param index index specifing position in the list where given element
+ * should be inserted.
+ *
+ * @throws ArrayIndexOutOfBoundsException if index is out of
+ * bounds.
+ *
+ * @see #addElement(Object)
+ */
+ public void insertElementAt(Object object, int index)
+ {
+ list.insertElementAt(object, index);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Removes an element from the model's item list and sends a
+ * {@link ListDataEvent} to all registered listeners. If the item to be
+ * removed is the current selected item, a new selected item will be set.
+ * If the element is not found in the model's item list, this method does
+ * nothing.
+ *
+ * @param object the element to remove.
+ */
+ public void removeElement(Object object)
+ {
+ int index = getIndexOf(object);
+ if (index != -1)
+ removeElementAt(index);
+ }
+
+ /**
+ * Removes all the items from the model's item list, resets and selected item
+ * to null, and sends a {@link ListDataEvent} to all registered
+ * listeners.
+ */
+ public void removeAllElements()
+ {
+ selectedItem = null;
+ int size = getSize();
+ if (size > 0)
+ {
+ list.clear();
+ fireIntervalRemoved(this, 0, size - 1);
+ }
+ }
+
+ /**
+ * Returns the number of items in the model's item list.
+ *
+ * @return The number of items in the model's item list.
+ */
+ public int getSize()
+ {
+ return list.size();
+ }
+
+ /**
+ * Sets the selected item for the model and sends a {@link ListDataEvent} to
+ * all registered listeners. The start and end index of the event is set to
+ * -1 to indicate the model's selection has changed, and not its contents.
+ *
+ * @param object the new selected item (null permitted).
+ */
+ public void setSelectedItem(Object object)
+ {
+ // No item is selected and object is null, so no change required.
+ if (selectedItem == null && object == null)
+ return;
+
+ // object is already selected so no change required.
+ if (selectedItem != null && selectedItem.equals(object))
+ return;
+
+ // Simply return if object is not in the list.
+ if (object != null && getIndexOf(object) == -1)
+ return;
+
+ // Here we know that object is either an item in the list or null.
+
+ // Handle the three change cases: selectedItem is null, object is
+ // non-null; selectedItem is non-null, object is null;
+ // selectedItem is non-null, object is non-null and they're not
+ // equal.
+ selectedItem = object;
+ fireContentsChanged(this, -1, -1);
+ }
+
+ /**
+ * Returns the selected item.
+ *
+ * @return The selected item (possibly null).
+ */
+ public Object getSelectedItem()
+ {
+ return selectedItem;
+ }
+
+ /**
+ * Returns the element at the specified index in the model's item list.
+ *
+ * @param index the element index.
+ *
+ * @return The element at the specified index in the model's item list, or
+ * null if the index is outside the bounds
+ * of the list.
+ */
+ public Object getElementAt(int index)
+ {
+ if (index < 0 || index >= list.size())
+ return null;
+ return list.elementAt(index);
+ }
+
+ /**
+ * Returns the index of the specified element in the model's item list.
+ *
+ * @param object the element.
+ *
+ * @return The index of the specified element in the model's item list.
+ */
+ public int getIndexOf(Object object)
+ {
+ return list.indexOf(object);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultDesktopManager.java b/libjava/classpath/javax/swing/DefaultDesktopManager.java
new file mode 100644
index 000000000..9613449ff
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultDesktopManager.java
@@ -0,0 +1,644 @@
+/* DefaultDesktopManager.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.beans.PropertyVetoException;
+import java.io.Serializable;
+
+import javax.swing.JInternalFrame.JDesktopIcon;
+
+/**
+ * The default implementation of DesktopManager for
+ * Swing. It implements the basic beaviours for JInternalFrames in arbitrary
+ * parents. The methods provided by the class are not meant to be called by
+ * the user, instead, the JInternalFrame methods will call these methods.
+ */
+public class DefaultDesktopManager implements DesktopManager, Serializable
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 4657624909838017887L;
+
+ /** The property change event fired when the wasIcon property changes. */
+ static final String WAS_ICON_ONCE_PROPERTY = "wasIconOnce";
+
+ /**
+ * The method of dragging used by the JDesktopPane that parents the
+ * JInternalFrame that is being dragged.
+ */
+ private int currentDragMode = 0;
+
+ /**
+ * The cache of the bounds used to draw the outline rectangle when
+ * OUTLINE_DRAG_MODE is used.
+ */
+ private transient Rectangle dragCache = new Rectangle();
+
+ /**
+ * A cached JDesktopPane that is stored when the JInternalFrame is initially
+ * dragged.
+ */
+ private transient Container pane;
+
+ /**
+ * An array of Rectangles that holds the bounds of the JDesktopIcons in the
+ * JDesktopPane when looking for where to place a new icon.
+ */
+ private transient Rectangle[] iconRects;
+
+ /**
+ * This creates a new DefaultDesktopManager object.
+ */
+ public DefaultDesktopManager()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not normally called since the user will typically add the
+ * JInternalFrame to a Container. If this is called, it will try to
+ * determine the parent of the JInternalFrame and remove any icon that
+ * represents this JInternalFrame and add this JInternalFrame.
+ *
+ * @param frame The JInternalFrame to open.
+ */
+ public void openFrame(JInternalFrame frame)
+ {
+ Container c = frame.getParent();
+ if (c == null)
+ c = frame.getDesktopIcon().getParent();
+ if (c == null)
+ return;
+
+ c.remove(frame.getDesktopIcon());
+ c.add(frame);
+ frame.setVisible(true);
+ }
+
+ /**
+ * This method removes the JInternalFrame and JDesktopIcon (if one is
+ * present) from their parents.
+ *
+ * @param frame The JInternalFrame to close.
+ */
+ public void closeFrame(JInternalFrame frame)
+ {
+ Container c = frame.getParent();
+ if (c != null)
+ {
+ if (frame.isIcon())
+ c.remove(frame.getDesktopIcon());
+ else
+ c.remove(frame);
+ c.repaint();
+ }
+ }
+
+ /**
+ * This method resizes the JInternalFrame to match its parent's bounds.
+ *
+ * @param frame The JInternalFrame to maximize.
+ */
+ public void maximizeFrame(JInternalFrame frame)
+ {
+ // Can't maximize from iconified state.
+ // It can only return to maximized state, but that would fall under
+ // deiconify.
+ if (frame.isIcon())
+ return;
+ frame.setNormalBounds(frame.getBounds());
+
+ Container p = frame.getParent();
+ if (p != null)
+ {
+ Rectangle pBounds = p.getBounds();
+ Insets insets = p.getInsets();
+ pBounds.width -= insets.left + insets.right;
+ pBounds.height -= insets.top + insets.bottom;
+
+ setBoundsForFrame(frame, 0, 0, pBounds.width, pBounds.height);
+ }
+ if (p instanceof JDesktopPane)
+ ((JDesktopPane) p).setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+
+ /**
+ * This method restores the JInternalFrame's bounds to what they were
+ * previous to the setMaximize call.
+ *
+ * @param frame The JInternalFrame to minimize.
+ */
+ public void minimizeFrame(JInternalFrame frame)
+ {
+ Rectangle normalBounds = frame.getNormalBounds();
+
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ p.setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+
+ setBoundsForFrame(frame, normalBounds.x, normalBounds.y,
+ normalBounds.width, normalBounds.height);
+ }
+
+ /**
+ * This method removes the JInternalFrame from its parent and adds its
+ * JDesktopIcon representation.
+ *
+ * @param frame The JInternalFrame to iconify.
+ */
+ public void iconifyFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ JDesktopIcon icon = frame.getDesktopIcon();
+ if (p != null && p.getSelectedFrame() == frame)
+ p.setSelectedFrame(null);
+ else
+ {
+ try
+ {
+ frame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if attempt is vetoed.
+ }
+ }
+
+ Container c = frame.getParent();
+
+ if (!wasIcon(frame))
+ {
+ Rectangle r = getBoundsForIconOf(frame);
+ icon.setBounds(r);
+ setWasIcon(frame, Boolean.TRUE);
+ }
+
+ if (c != null)
+ {
+ if (icon != null)
+ {
+ c.add(icon);
+ icon.setVisible(true);
+ }
+ Rectangle b = frame.getBounds();
+ c.remove(frame);
+ c.repaint(b.x, b.y, b.width, b.height);
+ }
+ }
+
+ /**
+ * This method removes the JInternalFrame's JDesktopIcon representation and
+ * adds the JInternalFrame back to its parent.
+ *
+ * @param frame The JInternalFrame to deiconify.
+ */
+ public void deiconifyFrame(JInternalFrame frame)
+ {
+ JDesktopIcon icon = frame.getDesktopIcon();
+ Container c = icon.getParent();
+
+ removeIconFor(frame);
+ c.add(frame);
+ frame.setVisible(true);
+
+ if (!frame.isSelected())
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ p.setSelectedFrame(frame);
+ else
+ {
+ try
+ {
+ frame.setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing.
+ }
+ }
+ }
+
+ c.invalidate();
+ }
+
+ /**
+ * This method activates the JInternalFrame by moving it to the front and
+ * selecting it.
+ *
+ * @param frame The JInternalFrame to activate.
+ */
+ public void activateFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ JInternalFrame active = null;
+ if (p != null)
+ active = p.getSelectedFrame();
+ if (active == null)
+ {
+ if (p != null)
+ {
+ p.setSelectedFrame(frame);
+ }
+ }
+ else if (active != frame)
+ {
+ if (active.isSelected())
+ {
+ try
+ {
+ active.setSelected(false);
+ }
+ catch (PropertyVetoException ex)
+ {
+ // Not allowed.
+ }
+ }
+ if (p != null)
+ {
+ p.setSelectedFrame(frame);
+ }
+
+ }
+ frame.toFront();
+ }
+
+ /**
+ * This method is called when the JInternalFrame loses focus.
+ *
+ * @param frame The JInternalFram to deactivate.
+ */
+ public void deactivateFrame(JInternalFrame frame)
+ {
+ JDesktopPane p = frame.getDesktopPane();
+ if (p != null)
+ {
+ if (p.getSelectedFrame() == frame)
+ p.setSelectedFrame(null);
+ }
+ else
+ {
+ try
+ {
+ frame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if attempt is vetoed.
+ }
+ }
+ }
+
+ /**
+ * This method is called to indicate that the DesktopManager should prepare
+ * to drag the JInternalFrame. Any state information needed to drag the
+ * frame will be prepared now.
+ *
+ * @param component The JComponent to drag, usually a JInternalFrame.
+ */
+ public void beginDraggingFrame(JComponent component)
+ {
+ if (component instanceof JDesktopIcon)
+ pane = ((JDesktopIcon) component).getInternalFrame().getDesktopPane();
+ else
+ pane = ((JInternalFrame) component).getDesktopPane();
+ if (pane == null)
+ return;
+
+ dragCache = component.getBounds();
+
+ if (! (pane instanceof JDesktopPane))
+ currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
+ else
+ currentDragMode = ((JDesktopPane) pane).getDragMode();
+ }
+
+ /**
+ * This method is called to drag the JInternalFrame to a new location.
+ *
+ * @param component The JComponent to drag, usually a JInternalFrame.
+ *
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ */
+ public void dragFrame(JComponent component, int newX, int newY)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ // FIXME: Do outline drag mode painting.
+ }
+ else
+ {
+ Rectangle b = component.getBounds();
+ if (component instanceof JDesktopIcon)
+ component.setBounds(newX, newY, b.width, b.height);
+ else
+ setBoundsForFrame((JInternalFrame) component, newX, newY, b.width,
+ b.height);
+ }
+ }
+
+ /**
+ * This method indicates that the dragging is done. Any state information
+ * stored by the DesktopManager can be cleared.
+ *
+ * @param component The JComponent that has finished dragging.
+ */
+ public void endDraggingFrame(JComponent component)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
+ dragCache.width, dragCache.height);
+ pane = null;
+ dragCache = null;
+ component.repaint();
+ }
+ }
+
+ /**
+ * This method is called to indicate that the given JComponent will be
+ * resized. Any state information necessary to resize the JComponent will
+ * be prepared now.
+ *
+ * @param component The JComponent to resize, usually a JInternalFrame.
+ * @param direction The direction to drag in (a SwingConstant).
+ */
+ public void beginResizingFrame(JComponent component, int direction)
+ {
+ pane = ((JInternalFrame) component).getDesktopPane();
+ if (pane == null)
+ return;
+
+ dragCache = component.getBounds();
+ if (! (pane instanceof JDesktopPane))
+ currentDragMode = JDesktopPane.LIVE_DRAG_MODE;
+ else
+ currentDragMode = ((JDesktopPane) pane).getDragMode();
+ }
+
+ /**
+ * This method resizes the give JComponent.
+ *
+ * @param component The JComponent to resize.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void resizeFrame(JComponent component, int newX, int newY,
+ int newWidth, int newHeight)
+ {
+ dragCache.setBounds(newX, newY, newWidth, newHeight);
+
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ // FIXME: Do outline drag painting.
+ }
+ else
+ setBoundsForFrame(component, dragCache.x, dragCache.y, dragCache.width,
+ dragCache.height);
+ }
+
+ /**
+ * This method is called to indicate that the given JComponent has finished
+ * dragging. Any state information stored by the DesktopManager can be
+ * cleared.
+ *
+ * @param component The JComponent that finished resizing.
+ */
+ public void endResizingFrame(JComponent component)
+ {
+ if (currentDragMode == JDesktopPane.OUTLINE_DRAG_MODE)
+ {
+ setBoundsForFrame((JInternalFrame) component, dragCache.x, dragCache.y,
+ dragCache.width, dragCache.height);
+ pane = null;
+ dragCache = null;
+ component.repaint();
+ }
+ }
+
+ /**
+ * This method calls setBounds with the given parameters and repaints the
+ * JComponent.
+ *
+ * @param component The JComponent to set bounds for.
+ * @param newX The new x coordinate.
+ * @param newY The new y coordinate.
+ * @param newWidth The new width.
+ * @param newHeight The new height.
+ */
+ public void setBoundsForFrame(JComponent component, int newX, int newY,
+ int newWidth, int newHeight)
+ {
+ component.setBounds(newX, newY, newWidth, newHeight);
+ }
+
+ /**
+ * This is a helper method that removes the JDesktopIcon of the given
+ * JInternalFrame from the parent.
+ *
+ * @param frame The JInternalFrame to remove an icon for.
+ */
+ protected void removeIconFor(JInternalFrame frame)
+ {
+ JDesktopIcon icon = frame.getDesktopIcon();
+ Container c = icon.getParent();
+ if (c != null && icon != null)
+ {
+ Rectangle b = icon.getBounds();
+ c.remove(icon);
+ c.repaint(b.x, b.y, b.width, b.height);
+ }
+ }
+
+ /**
+ * This method is called by iconifyFrame to determine the bounds of the
+ * JDesktopIcon for the given JInternalFrame.
+ *
+ * @param frame The JInternalFrame to find the bounds of its JDesktopIcon
+ * for.
+ *
+ * @return The bounds of the JDesktopIcon.
+ */
+ protected Rectangle getBoundsForIconOf(JInternalFrame frame)
+ {
+ // IconRects has no order to it.
+ // The icon _must_ be placed in the first free slot (working from
+ // the bottom left corner)
+ // The icon also must not be placed where another icon is placed
+ // (regardless whether that frame is an icon currently or not)
+ JDesktopPane desktopPane = frame.getDesktopPane();
+
+ if (desktopPane == null)
+ return frame.getDesktopIcon().getBounds();
+
+ Rectangle paneBounds = desktopPane.getBounds();
+ Insets insets = desktopPane.getInsets();
+ Dimension pref = frame.getDesktopIcon().getPreferredSize();
+
+ Component[] frames = desktopPane.getComponents();
+
+ int count = 0;
+ for (int i = 0, j = 0; i < frames.length; i++)
+ if (frames[i] instanceof JDesktopIcon
+ || frames[i] instanceof JInternalFrame
+ && ((JInternalFrame) frames[i]).getWasIcon() && frames[i] != frame)
+ count++;
+ iconRects = new Rectangle[count];
+ for (int i = 0, j = 0; i < frames.length; i++)
+ if (frames[i] instanceof JDesktopIcon)
+ iconRects[--count] = frames[i].getBounds();
+ else if (frames[i] instanceof JInternalFrame
+ && ((JInternalFrame) frames[i]).getWasIcon()
+ && frames[i] != frame)
+ iconRects[--count] = ((JInternalFrame) frames[i])
+ .getDesktopIcon().getBounds();
+
+ int startingX = insets.left;
+ int startingY = paneBounds.height - insets.bottom - pref.height;
+ Rectangle ideal = new Rectangle(startingX, startingY, pref.width,
+ pref.height);
+ boolean clear = true;
+
+ while (iconRects.length > 0)
+ {
+ clear = true;
+ for (int i = 0; i < iconRects.length; i++)
+ {
+ if (iconRects[i] != null && iconRects[i].intersects(ideal))
+ {
+ clear = false;
+ break;
+ }
+ }
+ if (clear)
+ return ideal;
+
+ startingX += pref.width;
+ if (startingX + pref.width > paneBounds.width - insets.right)
+ {
+ startingX = insets.left;
+ startingY -= pref.height;
+ }
+ ideal.setBounds(startingX, startingY, pref.width, pref.height);
+ }
+
+ return ideal;
+ }
+
+ /**
+ * This method sets the bounds of the JInternalFrame right before the
+ * maximizeFrame call.
+ *
+ * @param frame The JInternalFrame being maximized.
+ * @param rect The normal bounds.
+ */
+ protected void setPreviousBounds(JInternalFrame frame, Rectangle rect)
+ {
+ frame.setNormalBounds(rect);
+ }
+
+ /**
+ * This method returns the normal bounds of the JInternalFrame from before
+ * the maximize call.
+ *
+ * @param frame The JInternalFrame that is being restored.
+ *
+ * @return The previous bounds of the JInternalFrame.
+ */
+ protected Rectangle getPreviousBounds(JInternalFrame frame)
+ {
+ return frame.getNormalBounds();
+ }
+
+ /**
+ * This method sets the value to true if the given JInternalFrame has been
+ * iconized and the bounds of its DesktopIcon are valid.
+ *
+ * @param frame The JInternalFrame for the JDesktopIcon.
+ * @param value True if the JInternalFrame has been iconized and the bounds
+ * of the JDesktopIcon are valid.
+ */
+ protected void setWasIcon(JInternalFrame frame, Boolean value)
+ {
+ frame.setWasIcon(value.booleanValue(), WAS_ICON_ONCE_PROPERTY);
+ }
+
+ /**
+ * This method returns true if the given JInternalFrame has been iconized
+ * and the bounds of its DesktopIcon are valid.
+ *
+ * @param frame The JInternalFrame for the JDesktopIcon.
+ *
+ * @return True if the given JInternalFrame has been iconized and the bounds
+ * of its DesktopIcon are valid.
+ */
+ protected boolean wasIcon(JInternalFrame frame)
+ {
+ return frame.getWasIcon();
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultFocusManager.java b/libjava/classpath/javax/swing/DefaultFocusManager.java
new file mode 100644
index 000000000..3c5daa0e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultFocusManager.java
@@ -0,0 +1,169 @@
+/* DefaultFocusManager.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.event.KeyEvent;
+import java.util.Stack;
+
+/**
+ * This class has been obsoleted by the new
+ * {@link java.awt.KeyboardFocusManager} and
+ * {@link java.awt.DefaultKeyboardFocusManager} API.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultFocusManager extends FocusManager
+{
+
+ /**
+ * historyStack
+ */
+ private Stack historyStack;
+
+ /**
+ * Constructor DefaultFocusManager
+ */
+ public DefaultFocusManager()
+ {
+ // TODO
+ } // DefaultFocusManager()
+
+ /**
+ * processKeyEvent
+ *
+ * @param component
+ * TODO
+ * @param event
+ * TODO
+ */
+ public void processKeyEvent(Component component, KeyEvent event)
+ {
+ // TODO
+ } // processKeyEvent()
+
+ /**
+ * focusNextComponent
+ *
+ * @param component
+ * TODO
+ */
+ public void focusNextComponent(Component component)
+ {
+ // TODO
+ } // focusNextComponent()
+
+ /**
+ * focusPreviousComponent
+ *
+ * @param component
+ * TODO
+ */
+ public void focusPreviousComponent(Component component)
+ {
+ // TODO
+ } // focusPreviousComponent()
+
+ /**
+ * getFirstComponent
+ *
+ * @param container
+ * TODO
+ * @return Component
+ */
+ public Component getFirstComponent(Container container)
+ {
+ return null; // TODO
+ } // getFirstComponent()
+
+ /**
+ * getLastComponent
+ *
+ * @param container
+ * TODO
+ * @return Component
+ */
+ public Component getLastComponent(Container container)
+ {
+ return null; // TODO
+ } // getLastComponent()
+
+ /**
+ * getComponentBefore
+ *
+ * @param container
+ * TODO
+ * @param component
+ * TODO
+ * @return Component
+ */
+ public Component getComponentBefore(Container container, Component component)
+ {
+ return null; // TODO
+ } // getComponentBefore()
+
+ /**
+ * getComponentAfter
+ *
+ * @param container
+ * TODO
+ * @param component
+ * TODO
+ * @return Component
+ */
+ public Component getComponentAfter(Container container, Component component)
+ {
+ return null; // TODO
+ } // getComponentAfter()
+
+ /**
+ * compareTabOrder
+ *
+ * @param component1
+ * TODO
+ * @param component2
+ * TODO
+ * @return boolean
+ */
+ public boolean compareTabOrder(Component component1, Component component2)
+ {
+ return false; // TODO
+ } // compareTabOrder()
+
+} // DefaultFocusManager
diff --git a/libjava/classpath/javax/swing/DefaultListCellRenderer.java b/libjava/classpath/javax/swing/DefaultListCellRenderer.java
new file mode 100644
index 000000000..598627fac
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListCellRenderer.java
@@ -0,0 +1,199 @@
+/* DefaultListCellRenderer.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.border.Border;
+import javax.swing.border.EmptyBorder;
+
+/**
+ * The default implementation {@link ListCellRenderer}. It provides a standard
+ * renderer for data objects of all types via {@link Object#toString()}.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultListCellRenderer extends JLabel
+ implements ListCellRenderer, Serializable
+{
+ private static final long serialVersionUID = 7708947179685189462L;
+
+ /**
+ * Subclasses DefaultListCellRenderers and implements
+ * {@link javax.swing.plaf.UIResource}. This is used by
+ * {@link javax.swing.plaf.ListUI} subclasses to provide a default for
+ * the List.cellRenderer property. If you want to override
+ * this property, use DefaultListCellRenderer or a subclass.
+ */
+ public static class UIResource extends DefaultListCellRenderer
+ implements javax.swing.plaf.UIResource
+ {
+ public UIResource()
+ {
+ super();
+ }
+ }
+
+ /**
+ * This border is used whenever renderer doesn't have a focus.
+ */
+ protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
+
+ /**
+ * getListCellRendererComponent
+ *
+ * @param list JList list for the 'value'
+ * @param value object that should be rendered in the cell
+ * @param index index of the cell
+ * @param isSelected draw cell highlighted if isSelected is true
+ * @param cellHasFocus draw focus rectangle around cell if the cell has
+ * focus
+ *
+ * @return Component that will be painted to the desired cell.
+ */
+ public Component getListCellRendererComponent(JList list, Object value,
+ int index, boolean isSelected,
+ boolean cellHasFocus)
+ {
+ String s = value != null ? value.toString() : "";
+ setText(s);
+ setOpaque(true);
+ setHorizontalAlignment(LEFT);
+
+ if (isSelected)
+ {
+ setBackground(list.getSelectionBackground());
+ setForeground(list.getSelectionForeground());
+ }
+ else
+ {
+ setBackground(list.getBackground());
+ setForeground(list.getForeground());
+ }
+
+ setEnabled(list.isEnabled());
+ setFont(list.getFont());
+
+ // Use focusCellHighlightBorder when renderer has focus and
+ // noFocusBorder otherwise
+
+ if (cellHasFocus)
+ setBorder(UIManager.getBorder("List.focusCellHighlightBorder"));
+ else
+ setBorder(noFocusBorder);
+
+ return this;
+ }
+
+ public void validate()
+ {
+ // Overridden to do nothing.
+ }
+
+ public void revalidate()
+ {
+ // Overridden to do nothing.
+ }
+
+ public void repaint(long tm, int x, int y, int w, int h)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void repaint(Rectangle rect)
+ {
+ // Overridden to do nothing.
+ }
+
+ protected void firePropertyChange(String propertyName, Object oldValue,
+ Object newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, byte oldValue,
+ byte newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, char oldValue,
+ char newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, short oldValue,
+ short newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, int oldValue,
+ int newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, long oldValue,
+ long newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, float oldValue,
+ float newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, double oldValue,
+ double newValue)
+ {
+ // Overridden to do nothing.
+ }
+
+ public void firePropertyChange(String propertyName, boolean oldValue,
+ boolean newValue)
+ {
+ // Overridden to do nothing.
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultListModel.java b/libjava/classpath/javax/swing/DefaultListModel.java
new file mode 100644
index 000000000..aa2600760
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListModel.java
@@ -0,0 +1,521 @@
+/* DefaultListModel.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * The default implementation of {@link AbstractListModel}, used by
+ * {@link javax.swing.JList} and similar objects as the model of a list of
+ * values. The implementation is based on an underlying {@link
+ * java.util.Vector}.
+ *
+ * @author Andrew Selkirk
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public class DefaultListModel extends AbstractListModel
+{
+ private static final long serialVersionUID = 2315945659722172272L;
+
+ /**
+ * The vector of elements in this list model.
+ */
+ private Vector elements = new Vector();
+
+ /**
+ * Gets an element of the list at the provided index.
+ *
+ * @param index The index of the element to get
+ *
+ * @return The object at the given index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public Object elementAt(int index)
+ {
+ return elements.elementAt(index);
+ }
+
+ /**
+ * Convert the list to a string representation.
+ *
+ * @return A string representation of the list
+ */
+ public String toString()
+ {
+ return elements.toString();
+ }
+
+ /**
+ * Gets the first index of a particular element in the list.
+ *
+ * @param element The element to search for
+ *
+ * @return The first index in the list at which an object
+ * obj exists such that obj.equals(element) is
+ * true; if no such object exists, the method returns
+ * -1
+ */
+ public int indexOf(Object element)
+ {
+ return elements.indexOf(element);
+ }
+
+ /**
+ * Gets the first index of a particular element in a list which occurs
+ * at or after a particular index.
+ *
+ * @param element The element to search for
+ * @param startIndex The index to begin searching at
+ *
+ * @return The first index in the list, greater than or equal to
+ * startIndex, at which an object obj exists
+ * such that obj.equals(element) is true; if no
+ * such object exists, the method returns -1
+ */
+ public int indexOf(Object element, int startIndex)
+ {
+ return elements.indexOf(element, startIndex);
+ }
+
+ /**
+ * Gets the last index of a particular element in the list.
+ *
+ * @param element The element to search for
+ *
+ * @return The last index in the list at which an object
+ * obj exists such that obj.equals(element) is
+ * true; if no such object exists, the method returns
+ * -1
+ */
+ public int lastIndexOf(Object element)
+ {
+ return elements.lastIndexOf(element);
+ }
+
+ /**
+ * Gets the last index of a particular element in a list which occurs
+ * at or before a particular index.
+ *
+ * @param element The element to search for
+ * @param endIndex The index to finish searching at
+ *
+ * @return The last index in the list, less than to or equal to
+ * endIndexIndex, at which an object obj exists
+ * such that obj.equals(element) is true; if no
+ * such object exists, the method returns -1
+ */
+ public int lastIndexOf(Object element, int endIndex)
+ {
+ return elements.lastIndexOf(element, endIndex);
+ }
+
+ /**
+ * Gets the list element at a particular index.
+ *
+ * @param index The index to get the list value at
+ *
+ * @return The list value at the provided index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public Object get(int index)
+ {
+ return elements.get(index);
+ }
+
+ /**
+ * Sets the list element at a particular index.
+ *
+ * @param index The list index at which to set a value
+ * @param element The value to set at the specified index
+ *
+ * @return The value previously held at the specified index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public Object set(int index, Object element)
+ {
+ Object result;
+ result = elements.set(index, element);
+ fireContentsChanged(this, index, index);
+ return result;
+ }
+
+ /**
+ * Inserts an element at a particular index in the list. Each element at
+ * index i >= index is shifted to position i + 1.
+ * If index is equal to size(), this is
+ * equivalent to appending an element to the array. Any
+ * index greater than size() is illegal.
+ *
+ * @param index The index to insert the element at
+ * @param element The element to insert at the index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds [0, size()]
+ */
+ public void add(int index, Object element)
+ {
+ elements.add(index, element);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Inserts an element at the end of the list. This is equivalent to
+ * calling list.add(list.size(), element).
+ *
+ * @param element The element to add to the list
+ */
+ public void addElement(Object element)
+ {
+ int s = elements.size();
+ elements.add(element);
+ fireIntervalAdded(this, s, s);
+ }
+
+ /**
+ * Gets the number of elements in the list.
+ *
+ * @return The number of elements in the list
+ */
+ public int size()
+ {
+ return elements.size();
+ }
+
+ /**
+ * Gets an array containing the elements of the list.
+ *
+ * @return An array of the objects in the list, in the order they occur
+ * in the list
+ */
+ public Object[] toArray()
+ {
+ return elements.toArray();
+ }
+
+ /**
+ * Determines whether a particular element is a member of the list.
+ *
+ * @param element The element to search for
+ *
+ * @return true if element is a member of the
+ * list, otherwise false
+ */
+ public boolean contains(Object element)
+ {
+ return elements.contains(element);
+ }
+
+ /**
+ * Copies the list into a provided array. The provided array must be at
+ * least as large as the list.
+ *
+ * @param array The array to copy the list into
+ *
+ * @throws IndexOutOfBoundsException if the array is too small to hold the
+ * elements of the list
+ */
+ public void copyInto(Object[] array)
+ {
+ elements.copyInto(array);
+ }
+
+ /**
+ * Erases all the elements of the list, setting the list's size to 0.
+ */
+ public void clear()
+ {
+ int s = elements.size();
+ if (s > 0)
+ {
+ elements.clear();
+ fireIntervalRemoved(this, 0, s - 1);
+ }
+ }
+
+ /**
+ * Removes the element at a particular index from the list.
+ *
+ * @param index The index of the element to remove
+ *
+ * @return The value at the index, which has been removed from the list
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public Object remove(int index)
+ {
+ Object result;
+ result = elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ return result;
+ }
+
+ /**
+ * Determines whether the list is empty.
+ *
+ * @return true if the list is empty, otherwise
+ * false
+ */
+ public boolean isEmpty()
+ {
+ return elements.isEmpty();
+ }
+
+ /**
+ * Returns an {@link java.util.Enumeration} over the elements of the list.
+ *
+ * @return A new enumeration which iterates over the list
+ */
+ public Enumeration> elements()
+ {
+ return elements.elements();
+ }
+
+ /**
+ * Sets the capacity of the list to be equal to its size. The list's capacity
+ * is the number of elements it can hold before it needs to be reallocated.
+ * The list's size is the number of elements it currently holds.
+ */
+ public void trimToSize()
+ {
+ elements.trimToSize();
+ }
+
+ /**
+ * Ensures that the list's capacity is at least equal to
+ * size. The list's capacity is the number of elements it
+ * can hold before it needs to be reallocated.
+ *
+ * @param size The capacity to ensure the list can hold
+ */
+ public void ensureCapacity(int size)
+ {
+ elements.ensureCapacity(size);
+ }
+
+ /**
+ * Sets the size of the list to a particular value. If the specified size
+ * is greater than the current size, the values at the excess list
+ * indices are set to null. If the specified size is less
+ * than the current size, the excess elements are removed from the list.
+ *
+ * @param size The new size to set the list to
+ */
+ public void setSize(int size)
+ {
+ int oldSize = elements.size();
+ elements.setSize(size);
+ if (oldSize < size)
+ {
+ fireIntervalAdded(this, oldSize, size - 1);
+ }
+ else if (oldSize > size)
+ {
+ this.fireIntervalRemoved(this, size, oldSize - 1);
+ }
+ }
+
+ /**
+ * Gets the capacity of the list. The list's capacity is the number of
+ * elements it can hold before it needs to be reallocated.
+ *
+ * @return The capacity of the list
+ */
+ public int capacity()
+ {
+ return elements.capacity();
+ }
+
+ /**
+ * Gets the first element in the list.
+ *
+ * @return The first element in the list
+ */
+ public Object firstElement()
+ {
+ return elements.firstElement();
+ }
+
+ /**
+ * Gets the last element in the list.
+ *
+ * @return The last element in the list
+ */
+ public Object lastElement()
+ {
+ return elements.lastElement();
+ }
+
+ /**
+ * Sets the list element at a particular index.
+ *
+ * @param element The value to set at the specified index
+ * @param index The list index at which to set a value
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public void setElementAt(Object element, int index)
+ {
+ elements.setElementAt(element, index);
+ fireContentsChanged(this, index, index);
+ }
+
+ /**
+ * Removes the element at a particular index from the list.
+ *
+ * @param index The index of the element to remove
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public void removeElementAt(int index)
+ {
+ elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ }
+
+ /**
+ * Inserts an element at a particular index in the list. Each element at
+ * index i >= index is shifted to position i + 1.
+ * If index is equal to size(), this is
+ * equivalent to appending an element to the array. Any
+ * index greater than size() is illegal.
+ *
+ * @param element The element to insert at the index
+ * @param index The index to insert the element at
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds [0, size()]
+ */
+ public void insertElementAt(Object element, int index)
+ {
+ elements.insertElementAt(element, index);
+ fireIntervalAdded(this, index, index);
+ }
+
+ /**
+ * Removes the first occurrence of a particular element in the list. If the
+ * element does not exist in the list, nothing happens.
+ *
+ * @param element The element to remove
+ *
+ * @return true if the element existed in the list (and was
+ * removed), false otherwise
+ */
+ public boolean removeElement(Object element)
+ {
+ int index;
+ index = elements.indexOf(element);
+ if (index != -1)
+ {
+ elements.remove(index);
+ fireIntervalRemoved(this, index, index);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove all elements in the list.
+ */
+ public void removeAllElements()
+ {
+ int size;
+ size = size();
+ if (size > 0)
+ {
+ elements.clear();
+ fireIntervalRemoved(this, 0, size - 1);
+ }
+ }
+
+ /**
+ * Remove all elements between startIndex and
+ * endIndex inclusive.
+ *
+ * @param startIndex The first index in the range to remove
+ * @param endIndex The last index in the range to remove
+ *
+ * @throws ArrayIndexOutOfBoundsException if either index is outside the
+ * valid range of indices for this list [0, size())
+ * @throws IllegalArgumentException if startIndex > endIndex
+ */
+ public void removeRange(int startIndex, int endIndex)
+ {
+ int index;
+ if (startIndex > endIndex)
+ throw new IllegalArgumentException();
+ for (index = endIndex; index >= startIndex; index--)
+ elements.remove(index);
+ fireIntervalRemoved(this, startIndex, endIndex);
+ }
+
+ /**
+ * Gets the size of the list.
+ *
+ * @return The number of elements currently in the list
+ */
+ public int getSize()
+ {
+ return elements.size();
+ }
+
+ /**
+ * Gets the list element at a particular index.
+ *
+ * @param index The index to get the list value at
+ *
+ * @return The list value at the provided index
+ *
+ * @throws ArrayIndexOutOfBoundsException If the provided index is
+ * outside the bounds of the list [0, size())
+ */
+ public Object getElementAt(int index)
+ {
+ return elements.get(index);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultListSelectionModel.java b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
new file mode 100644
index 000000000..273ca0d15
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultListSelectionModel.java
@@ -0,0 +1,855 @@
+/* DefaultListSelectionModel.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.BitSet;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+/**
+ * The default implementation of {@link ListSelectionModel},
+ * which is used by {@link javax.swing.JList} and
+ * similar classes to manage the selection status of a number of data
+ * elements.
+ *
+ *
The class is organized abstractly as a set of intervals of
+ * integers. Each interval indicates an inclusive range of indices in a
+ * list -- held by some other object and unknown to this class -- which is
+ * considered "selected". There are various accessors for querying and
+ * modifying the set of intervals, with simplified forms accepting a single
+ * index, representing an interval with only one element.
+ */
+public class DefaultListSelectionModel implements Cloneable,
+ ListSelectionModel,
+ Serializable
+{
+ private static final long serialVersionUID = -5718799865110415860L;
+
+ /** The list of ListSelectionListeners subscribed to this selection model. */
+ protected EventListenerList listenerList = new EventListenerList();
+
+
+ /**
+ * The current list selection mode. Must be one of the numeric constants
+ * SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION
+ * or MULTIPLE_INTERVAL_SELECTION from {@link
+ * ListSelectionModel}. The default value is
+ * MULTIPLE_INTERVAL_SELECTION.
+ */
+ int selectionMode = MULTIPLE_INTERVAL_SELECTION;
+
+ /**
+ * The index of the "lead" of the most recent selection. The lead is the
+ * second argument in any call to {@link #setSelectionInterval}, {@link
+ * #addSelectionInterval} or {@link #removeSelectionInterval}. Generally
+ * the lead refers to the most recent position a user dragged their mouse
+ * over.
+ */
+ int leadSelectionIndex = -1;
+
+ /**
+ * The index of the "anchor" of the most recent selection. The anchor is
+ * the first argument in any call to {@link #setSelectionInterval},
+ * {@link #addSelectionInterval} or {@link
+ * #removeSelectionInterval}. Generally the anchor refers to the first
+ * recent position a user clicks when they begin to drag their mouse over
+ * a list.
+ *
+ * @see #getAnchorSelectionIndex
+ * @see #setAnchorSelectionIndex
+ */
+ int anchorSelectionIndex = -1;
+
+ /**
+ * controls the range of indices provided in any {@link
+ * ListSelectionEvent} fired by the selectionModel. Let
+ * [A,L] be the range of indices between {@link
+ * #anchorSelectionIndex} and {@link #leadSelectionIndex} inclusive, and
+ * let [i0,i1] be the range of indices changed in a given
+ * call which generates a {@link ListSelectionEvent}. Then when this
+ * property is true, the {@link ListSelectionEvent} contains
+ * the range [A,L] union [i0,i1]; when false it
+ * will contain only [i0,i1]. The default is
+ * true.
+ *
+ * @see #isLeadAnchorNotificationEnabled
+ * @see #setLeadAnchorNotificationEnabled
+ */
+ protected boolean leadAnchorNotificationEnabled = true;
+
+ /**
+ * Whether the selection is currently "adjusting". Any {@link
+ * ListSelectionEvent} events constructed in response to changes in this
+ * list selection model will have their {@link
+ * ListSelectionEvent#isAdjusting} field set to this value.
+ *
+ * @see #getValueIsAdjusting
+ * @see #setValueIsAdjusting
+ */
+ boolean valueIsAdjusting = false;
+
+
+ /**
+ * The current set of "intervals", represented simply by a {@link
+ * java.util.BitSet}. A set bit indicates a selected index, whereas a
+ * cleared bit indicates a non-selected index.
+ */
+ BitSet sel = new BitSet();
+
+ /**
+ * A variable to store the previous value of sel.
+ * Used to make sure we only fireValueChanged when the BitSet
+ * actually does change.
+ */
+ Object oldSel;
+
+ /**
+ * Whether this call of setLeadSelectionInterval was called locally
+ * from addSelectionInterval
+ */
+ boolean setLeadCalledFromAdd = false;
+
+ /**
+ * Returns the selection mode, which is one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}. The default value is
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ public int getSelectionMode()
+ {
+ return selectionMode;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionMode} property.
+ *
+ * @param mode The new value of the property
+ */
+ public void setSelectionMode(int mode)
+ {
+ if (mode < ListSelectionModel.SINGLE_SELECTION
+ || mode > ListSelectionModel.MULTIPLE_INTERVAL_SELECTION)
+ throw new IllegalArgumentException("Unrecognised mode: " + mode);
+ selectionMode = mode;
+ }
+
+ /**
+ * Gets the value of the {@link #anchorSelectionIndex} property.
+ *
+ * @return The current property value
+ *
+ * @see #setAnchorSelectionIndex
+ */
+ public int getAnchorSelectionIndex()
+ {
+ return anchorSelectionIndex;
+ }
+
+ /**
+ * Sets the value of the {@link #anchorSelectionIndex} property.
+ *
+ * @param index The new property value
+ *
+ * @see #getAnchorSelectionIndex
+ */
+ public void setAnchorSelectionIndex(int index)
+ {
+ if (anchorSelectionIndex != index)
+ {
+ int old = anchorSelectionIndex;
+ anchorSelectionIndex = index;
+ if (leadAnchorNotificationEnabled)
+ fireValueChanged(index, old);
+ }
+ }
+
+ /**
+ * Gets the value of the {@link #leadSelectionIndex} property.
+ *
+ * @return The current property value
+ *
+ * @see #setLeadSelectionIndex
+ */
+ public int getLeadSelectionIndex()
+ {
+ return leadSelectionIndex;
+ }
+
+ /**
+ *
Sets the value of the {@link #anchorSelectionIndex} property. As a
+ * side effect, alters the selection status of two ranges of indices. Let
+ * OL be the old lead selection index, NL be
+ * the new lead selection index, and A be the anchor
+ * selection index. Then if A is a valid selection index,
+ * one of two things happens depending on the seleciton status of
+ * A:
+ *
+ *
+ *
+ *
isSelectedIndex(A) == true: set [A,OL]
+ * to deselected, then set [A,NL] to
+ * selected.
+ *
+ *
isSelectedIndex(A) == false: set [A,OL]
+ * to selected, then set [A,NL] to
+ * deselected.
+ *
+ *
+ *
+ *
This method generates at most a single {@link ListSelectionEvent}
+ * despite changing multiple ranges. The range of values provided to the
+ * {@link ListSelectionEvent} includes only the minimum range of values
+ * which changed selection status between the beginning and end of the
+ * method.
+ *
+ * @param leadIndex The new property value
+ *
+ * @see #getAnchorSelectionIndex
+ */
+ public void setLeadSelectionIndex(int leadIndex)
+ {
+ // Only set the lead selection index to < 0 if anchorSelectionIndex < 0.
+ if (leadIndex < 0)
+ {
+ if (anchorSelectionIndex < 0)
+ leadSelectionIndex = -1;
+ else
+ return;
+ }
+
+ // Only touch the lead selection index if the anchor is >= 0.
+ if (anchorSelectionIndex < 0)
+ return;
+
+ if (selectionMode == SINGLE_SELECTION)
+ setSelectionInterval (leadIndex, leadIndex);
+
+ int oldLeadIndex = leadSelectionIndex;
+ if (oldLeadIndex == -1)
+ oldLeadIndex = leadIndex;
+ if (setLeadCalledFromAdd == false)
+ oldSel = sel.clone();
+ leadSelectionIndex = leadIndex;
+
+ if (anchorSelectionIndex == -1)
+ return;
+
+ int R1 = Math.min(anchorSelectionIndex, oldLeadIndex);
+ int R2 = Math.max(anchorSelectionIndex, oldLeadIndex);
+ int S1 = Math.min(anchorSelectionIndex, leadIndex);
+ int S2 = Math.max(anchorSelectionIndex, leadIndex);
+
+ int lo = Math.min(R1, S1);
+ int hi = Math.max(R2, S2);
+
+ if (isSelectedIndex(anchorSelectionIndex))
+ {
+ sel.clear(R1, R2+1);
+ sel.set(S1, S2+1);
+ }
+ else
+ {
+ sel.set(R1, R2+1);
+ sel.clear(S1, S2+1);
+ }
+
+ int beg = sel.nextSetBit(0), end = -1;
+ for(int i=beg; i >= 0; i=sel.nextSetBit(i+1))
+ end = i;
+
+ BitSet old = (BitSet) oldSel;
+
+ // The new and previous lead location requires repainting.
+ old.set(oldLeadIndex, !sel.get(oldLeadIndex));
+ old.set(leadSelectionIndex, !sel.get(leadSelectionIndex));
+
+ fireDifference(sel, old);
+ }
+
+ /**
+ * Moves the lead selection index to leadIndex without
+ * changing the selection values.
+ *
+ * If leadAnchorNotificationEnabled is true, send a notification covering the
+ * old and new lead cells.
+ *
+ * @param leadIndex the new lead selection index
+ * @since 1.5
+ */
+ public void moveLeadSelectionIndex (int leadIndex)
+ {
+ if (leadSelectionIndex == leadIndex)
+ return;
+
+ leadSelectionIndex = leadIndex;
+ if (isLeadAnchorNotificationEnabled())
+ fireValueChanged(Math.min(leadSelectionIndex, leadIndex),
+ Math.max(leadSelectionIndex, leadIndex));
+ }
+
+ /**
+ * Gets the value of the {@link #leadAnchorNotificationEnabled} property.
+ *
+ * @return The current property value
+ *
+ * @see #setLeadAnchorNotificationEnabled
+ */
+ public boolean isLeadAnchorNotificationEnabled()
+ {
+ return leadAnchorNotificationEnabled;
+ }
+
+ /**
+ * Sets the value of the {@link #leadAnchorNotificationEnabled} property.
+ *
+ * @param l The new property value
+ *
+ * @see #isLeadAnchorNotificationEnabled
+ */
+ public void setLeadAnchorNotificationEnabled(boolean l)
+ {
+ leadAnchorNotificationEnabled = l;
+ }
+
+ /**
+ * Gets the value of the {@link #valueIsAdjusting} property.
+ *
+ * @return The current property value
+ *
+ * @see #setValueIsAdjusting
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return valueIsAdjusting;
+ }
+
+ /**
+ * Sets the value of the {@link #valueIsAdjusting} property.
+ *
+ * @param v The new property value
+ *
+ * @see #getValueIsAdjusting
+ */
+ public void setValueIsAdjusting(boolean v)
+ {
+ valueIsAdjusting = v;
+ }
+
+ /**
+ * Determines whether the selection is empty.
+ *
+ * @return true if the selection is empty, otherwise
+ * false
+ */
+ public boolean isSelectionEmpty()
+ {
+ return sel.isEmpty();
+ }
+
+ /**
+ * Gets the smallest index which is currently a member of a selection
+ * interval.
+ *
+ * @return The least integer i such that i >=
+ * 0 and i is a member of a selected interval, or
+ * -1 if there are no selected intervals
+ *
+ * @see #getMaxSelectionIndex
+ */
+ public int getMinSelectionIndex()
+ {
+ if (isSelectionEmpty())
+ return -1;
+
+ return sel.nextSetBit(0);
+ }
+
+ /**
+ * Gets the largest index which is currently a member of a selection
+ * interval.
+ *
+ * @return The greatest integer i such that i >=
+ * 0 and i is a member of a selected interval, or
+ * -1 if there are no selected intervals
+ *
+ * @see #getMinSelectionIndex
+ */
+ public int getMaxSelectionIndex()
+ {
+ if (isSelectionEmpty())
+ return -1;
+
+ int mx = -1;
+ for(int i=sel.nextSetBit(0); i >= 0; i=sel.nextSetBit(i+1))
+ {
+ mx = i;
+ }
+ return mx;
+ }
+
+ /**
+ * Determines whether a particular index is a member of a selection
+ * interval.
+ *
+ * @param a The index to search for
+ *
+ * @return true if the index is a member of a selection interval,
+ * otherwise false
+ */
+ public boolean isSelectedIndex(int a)
+ {
+ // TODO: Probably throw an exception here?
+ if (a >= sel.length() || a < 0)
+ return false;
+ return sel.get(a);
+ }
+
+ /**
+ * If the {@link #selectionMode} property is equal to
+ * SINGLE_SELECTION equivalent to calling
+ * setSelectionInterval(index1, index2);
+ * If the {@link #selectionMode} property is equal to
+ * SINGLE_INTERVAL_SELECTION and the interval being
+ * added is not adjacent to an already selected interval,
+ * equivalent to setSelectionInterval(index1, index2).
+ * Otherwise adds the range [index0, index1]
+ * to the selection interval set.
+ *
+ * @param index0 The beginning of the range of indices to select
+ * @param index1 The end of the range of indices to select
+ *
+ * @see #setSelectionInterval
+ * @see #removeSelectionInterval
+ */
+ public void addSelectionInterval(int index0, int index1)
+ {
+ if (index0 == -1 || index1 == -1)
+ return;
+
+ if (selectionMode == SINGLE_SELECTION)
+ setSelectionInterval(index0, index1);
+ else
+ {
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+ oldSel = sel.clone();
+
+
+ // COMPAT: Like Sun (but not like IBM), we allow calls to
+ // addSelectionInterval when selectionMode is
+ // SINGLE_SELECTION_INTERVAL iff the interval being added
+ // is adjacent to an already selected interval
+ if (selectionMode == SINGLE_INTERVAL_SELECTION)
+ if (!(isSelectedIndex(index0) ||
+ isSelectedIndex(index1) ||
+ isSelectedIndex(Math.max(lo-1,0)) ||
+ isSelectedIndex(Math.min(hi+1,sel.size()))))
+ sel.clear();
+
+ // We have to update the anchorSelectionIndex and leadSelectionIndex
+ // variables
+
+ // The next if statements breaks down to "if this selection is adjacent
+ // to the previous selection and going in the same direction"
+ if ((isSelectedIndex(leadSelectionIndex))
+ && ((index0 - 1 == leadSelectionIndex
+ && (index1 >= index0)
+ && (leadSelectionIndex >= anchorSelectionIndex))
+ || (index0 + 1 == leadSelectionIndex && (index1 <= index0)
+ && (leadSelectionIndex <= anchorSelectionIndex)))
+ && (anchorSelectionIndex != -1 || leadSelectionIndex != -1))
+ {
+ // setting setLeadCalledFromAdd to true tells setLeadSelectionIndex
+ // not to update oldSel
+ setLeadCalledFromAdd = true;
+ setLeadSelectionIndex(index1);
+ setLeadCalledFromAdd = false;
+ }
+ else
+ {
+ leadSelectionIndex = index1;
+ anchorSelectionIndex = index0;
+ sel.set(lo, hi+1);
+ fireDifference(sel, (BitSet) oldSel);
+ }
+ }
+ }
+
+
+ /**
+ * Deselects all indices in the inclusive range
+ * [index0,index1].
+ *
+ * @param index0 The beginning of the range of indices to deselect
+ * @param index1 The end of the range of indices to deselect
+ *
+ * @see #addSelectionInterval
+ * @see #setSelectionInterval
+ */
+ public void removeSelectionInterval(int index0,
+ int index1)
+ {
+ if (index0 == -1 || index1 == -1)
+ return;
+
+ oldSel = sel.clone();
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+
+ // if selectionMode is SINGLE_INTERVAL_SELECTION and removing the interval
+ // (index0,index1) would leave two disjoint selection intervals, remove all
+ // selected indices from lo to the last selected index
+ if (getMinSelectionIndex() > 0 && getMinSelectionIndex() < lo &&
+ selectionMode == SINGLE_INTERVAL_SELECTION)
+ hi = sel.size() - 1;
+
+ sel.clear(lo, hi+1);
+ //update anchorSelectionIndex and leadSelectionIndex variables
+ //TODO: will probably need MouseDragged to test properly and know if this works
+ setAnchorSelectionIndex(index0);
+ leadSelectionIndex = index1;
+
+ fireDifference(sel, (BitSet) oldSel);
+ }
+
+ /**
+ * Removes all intervals in the selection set.
+ */
+ public void clearSelection()
+ {
+ // Find the selected interval.
+ int from = sel.nextSetBit(0);
+ if (from < 0)
+ return; // Empty selection - nothing to do.
+ int to = from;
+
+ int i;
+
+ for (i = from; i>=0; i=sel.nextSetBit(i+1))
+ to = i;
+
+ sel.clear();
+ fireValueChanged(from, to, valueIsAdjusting);
+ }
+
+ /**
+ * Fire the change event, covering the difference between the two sets.
+ *
+ * @param current the current set
+ * @param x the previous set, the object will be reused.
+ */
+ private void fireDifference(BitSet current, BitSet x)
+ {
+ x.xor(current);
+ int from = x.nextSetBit(0);
+ if (from < 0)
+ return; // No difference.
+ int to = from;
+ int i;
+
+ for (i = from; i >= 0; i = x.nextSetBit(i+1))
+ to = i;
+
+ fireValueChanged(from, to, valueIsAdjusting);
+ }
+
+ /**
+ * Clears the current selection and marks a given interval as "selected". If
+ * the current selection mode is SINGLE_SELECTION only the
+ * index index2 is selected.
+ *
+ * @param anchor the anchor selection index.
+ * @param lead the lead selection index.
+ */
+ public void setSelectionInterval(int anchor, int lead)
+ {
+ if (anchor == -1 || lead == -1)
+ return;
+ if (selectionMode == SINGLE_SELECTION)
+ {
+ int lo = lead;
+ int hi = lead;
+ int selected = sel.nextSetBit(0);
+ if (selected == lead)
+ return; // the selection is not changing
+ if (selected >= 0)
+ {
+ lo = Math.min(lo, selected);
+ hi = Math.max(hi, selected);
+ }
+ if (anchorSelectionIndex >= 0)
+ {
+ lo = Math.min(lo, anchorSelectionIndex);
+ hi = Math.max(hi, anchorSelectionIndex);
+ }
+ sel.clear();
+ sel.set(lead);
+ leadSelectionIndex = lead;
+ anchorSelectionIndex = lead;
+ fireValueChanged(lo, hi);
+ }
+ else if (selectionMode == SINGLE_INTERVAL_SELECTION)
+ {
+ // determine the current interval
+ int first = sel.nextSetBit(0);
+ int last = first;
+ if (first >= 0)
+ last += (sel.cardinality() - 1);
+
+ // update the selection
+ int lo = Math.min(anchor, lead);
+ int hi = Math.max(anchor, lead);
+ if (lo == first && hi == last)
+ return; // selected interval is not being changed
+ sel.clear();
+ sel.set(lo, hi + 1);
+
+ // include the old selection in the event range
+ if (first >= 0)
+ lo = Math.min(lo, first);
+ if (last >= 0)
+ hi = Math.max(hi, last);
+ if (anchorSelectionIndex >= 0)
+ {
+ lo = Math.min(lo, anchorSelectionIndex);
+ hi = Math.max(hi, anchorSelectionIndex);
+ }
+ anchorSelectionIndex = anchor;
+ leadSelectionIndex = lead;
+ fireValueChanged(lo, hi);
+ }
+ else
+ {
+ BitSet oldSel = (BitSet) sel.clone();
+ sel.clear();
+ if (selectionMode == SINGLE_SELECTION)
+ anchor = lead;
+
+ int lo = Math.min(anchor, lead);
+ int hi = Math.max(anchor, lead);
+ sel.set(lo, hi+1);
+ // update the anchorSelectionIndex and leadSelectionIndex variables
+ setAnchorSelectionIndex(anchor);
+ leadSelectionIndex = lead;
+
+ fireDifference(sel, oldSel);
+ }
+ }
+
+ /**
+ * Inserts a number of indices either before or after a particular
+ * position in the set of indices. Renumbers all indices after the
+ * inserted range. The new indices in the inserted range are not
+ * selected. This method is typically called to synchronize the selection
+ * model with an inserted range of elements in a {@link ListModel}.
+ *
+ * @param index The position to insert indices at
+ * @param length The number of indices to insert
+ * @param before Indicates whether to insert the indices before the index
+ * or after it
+ */
+ public void insertIndexInterval(int index,
+ int length,
+ boolean before)
+ {
+ if (!before)
+ {
+ index++;
+ length--;
+ }
+ BitSet tmp = sel.get(index, sel.size());
+ sel.clear(index, sel.size());
+ int n = tmp.size();
+ for (int i = 0; i < n; ++i)
+ sel.set(index + length + i, tmp.get(i));
+ }
+
+ /**
+ * Removes a range from the set of indices. Renumbers all indices after
+ * the removed range. This method is typically called to synchronize the
+ * selection model with a deleted range of elements in a {@link
+ * ListModel}.
+ *
+ * @param index0 The first index to remove (inclusive)
+ * @param index1 The last index to remove (inclusive)
+ */
+ public void removeIndexInterval(int index0,
+ int index1)
+ {
+ int lo = Math.min(index0, index1);
+ int hi = Math.max(index0, index1);
+
+ BitSet tmp = sel.get(hi, sel.size());
+ sel.clear(lo, sel.size());
+ int n = tmp.size();
+ for (int i = 0; i < n; ++i)
+ sel.set(lo + i, tmp.get(i));
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model to
+ * indicate that a series of adjustment has just ended.
+ *
+ * The values of {@link #getMinSelectionIndex} and
+ * {@link #getMaxSelectionIndex} are used in the {@link ListSelectionEvent}
+ * that gets fired.
+ *
+ * @param isAdjusting true if this is the final change
+ * in a series of adjustments, false/code> otherwise
+ */
+ protected void fireValueChanged(boolean isAdjusting)
+ {
+ fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(),
+ isAdjusting);
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model.
+ *
+ * @param firstIndex The low index of the changed range
+ * @param lastIndex The high index of the changed range
+ */
+ protected void fireValueChanged(int firstIndex, int lastIndex)
+ {
+ fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
+ }
+
+ /**
+ * Fires a {@link ListSelectionEvent} to all the listeners of type {@link
+ * ListSelectionListener} registered with this selection model.
+ *
+ * @param firstIndex The low index of the changed range
+ * @param lastIndex The high index of the changed range
+ * @param isAdjusting Whether this change is part of a seqence of adjustments
+ * made to the selection, such as during interactive scrolling
+ */
+ protected void fireValueChanged(int firstIndex, int lastIndex,
+ boolean isAdjusting)
+ {
+ ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
+ lastIndex, isAdjusting);
+ ListSelectionListener[] listeners = getListSelectionListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].valueChanged(evt);
+ }
+
+ /**
+ * Adds a listener.
+ *
+ * @param listener The listener to add
+ *
+ * @see #removeListSelectionListener
+ * @see #getListSelectionListeners
+ */
+ public void addListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.add(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a registered listener.
+ *
+ * @param listener The listener to remove
+ *
+ * @see #addListSelectionListener
+ * @see #getListSelectionListeners
+ */
+ public void removeListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.remove(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns an array of all registerers listeners.
+ *
+ * @param listenerType The type of listener to retrieve
+ *
+ * @return The array
+ *
+ * @see #getListSelectionListeners
+ * @since 1.3
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Returns an array of all registerd list selection listeners.
+ *
+ * @return the array
+ *
+ * @see #addListSelectionListener
+ * @see #removeListSelectionListener
+ * @see #getListeners
+ * @since 1.4
+ */
+ public ListSelectionListener[] getListSelectionListeners()
+ {
+ return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
+ }
+
+ /**
+ * Returns a clone of this object.
+ * listenerList don't gets duplicated.
+ *
+ * @return the cloned object
+ *
+ * @throws CloneNotSupportedException if an error occurs
+ */
+ public Object clone()
+ throws CloneNotSupportedException
+ {
+ DefaultListSelectionModel model =
+ (DefaultListSelectionModel) super.clone();
+ model.sel = (BitSet) sel.clone();
+ model.listenerList = new EventListenerList();
+ return model;
+ }
+}
diff --git a/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
new file mode 100644
index 000000000..93d129ebb
--- /dev/null
+++ b/libjava/classpath/javax/swing/DefaultSingleSelectionModel.java
@@ -0,0 +1,191 @@
+/* DefaultSingleSelectionModel.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * The default implementation of {@link SingleSelectionModel}, used in
+ * {@link JTabbedPane}, {@link JMenuBar} and {@link JPopupMenu}.
+ *
+ * @author Andrew Selkirk
+ */
+public class DefaultSingleSelectionModel
+ implements SingleSelectionModel, Serializable
+{
+ private static final long serialVersionUID = 3676229404753786004L;
+
+ /**
+ * changeEvent
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * listenerList
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * The selected index (or -1 for no selection).
+ */
+ private int index = -1;
+
+ /**
+ * Creates a new DefaultSingleSelectionModel with no current
+ * selection.
+ */
+ public DefaultSingleSelectionModel()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Returns the selected index or -1 if there is no selection.
+ *
+ * @return The selected index.
+ *
+ * @see #setSelectedIndex(int)
+ */
+ public int getSelectedIndex()
+ {
+ return index;
+ }
+
+ /**
+ * Sets the selected index and, if this is different to the previous
+ * selection, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param index the index (use -1 to represent no selection).
+ *
+ * @see #getSelectedIndex()
+ * @see #clearSelection
+ */
+ public void setSelectedIndex(int index)
+ {
+ if (this.index != index)
+ {
+ this.index = index;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Clears the selection by setting the selected index to -1 and
+ * sends a {@link ChangeEvent} to all registered listeners. If the selected
+ * index is already -1, this method does nothing.
+ */
+ public void clearSelection()
+ {
+ setSelectedIndex(-1);
+ }
+
+ /**
+ * Returns true if there is a selection, and false
+ * otherwise.
+ *
+ * @return A boolean.
+ */
+ public boolean isSelected()
+ {
+ return index != -1;
+ }
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from
+ * this model whenever the selected index changes.
+ *
+ * @param listener the listener to add.
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from this model.
+ *
+ * @param listener the listener to remove.
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * fireStateChanged
+ */
+ protected void fireStateChanged()
+ {
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ ChangeListener[] listeners = getChangeListeners();
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * getListeners
+ *
+ * @param listenerClass the type fo listener
+ *
+ * @return an array of listeners
+ *
+ * @since 1.3
+ */
+ public T[] getListeners(Class listenerClass)
+ {
+ return listenerList.getListeners(listenerClass);
+ }
+
+ /**
+ * getChangeListeners
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/DesktopManager.java b/libjava/classpath/javax/swing/DesktopManager.java
new file mode 100644
index 000000000..620c7ffb8
--- /dev/null
+++ b/libjava/classpath/javax/swing/DesktopManager.java
@@ -0,0 +1,177 @@
+/* DesktopManager.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+/**
+ * DesktopManagers are responsible for implementing the behaviours for the
+ * JInternalFrames that belong to JDesktopPanes. Actions such as maximizing,
+ * minimizing, iconifying, etc will be delegated to the DesktopManager.
+ */
+public interface DesktopManager
+{
+ /**
+ * This method will cause the JInternalFrame to be displayed in the set
+ * location. This usually is not needed since the user will add the
+ * JInternalFrame to a Container separately.
+ *
+ * @param frame The JInternalFrame to open.
+ */
+ void openFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JInternalFrame from its parent.
+ *
+ * @param frame The JInternalFrame to close.
+ */
+ void closeFrame(JInternalFrame frame);
+
+ /**
+ * This method should maximize the JInternalFrame to match its parent's
+ * bounds.
+ *
+ * @param frame The JInternalFrame to maximize.
+ */
+ void maximizeFrame(JInternalFrame frame);
+
+ /**
+ * This method should restore the JInternalFrame to its normal bounds.
+ *
+ * @param frame The JInternalFrame to minimize.
+ */
+ void minimizeFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JInternalFrame from its parent and replace
+ * it with a JDesktopIcon.
+ *
+ * @param frame The JInternalFrame to iconify.
+ */
+ void iconifyFrame(JInternalFrame frame);
+
+ /**
+ * This method should remove the JDesktopIcon from its parent and replace it
+ * with the JInternalFrame that the JDesktopIcon represents.
+ *
+ * @param frame The JInternalFrame to deiconify.
+ */
+ void deiconifyFrame(JInternalFrame frame);
+
+ /**
+ * This method should give focus to the JInternalFrame and its default focus
+ * owner.
+ *
+ * @param vframe The JInternalFrame to activate.
+ */
+ void activateFrame(JInternalFrame vframe);
+
+ /**
+ * This method should be called when the JInternalFrame gets deselected and
+ * subsequently loses focus.
+ *
+ * @param frame The JInternalFrame to deactivate.
+ */
+ void deactivateFrame(JInternalFrame frame);
+
+ /**
+ * This method should be called in preparation for dragging. This needs to
+ * be called prior to dragFrame calls so that the DesktopManager can
+ * prepare any state information.
+ *
+ * @param frame The JInternalFrame to prepare for dragging.
+ */
+ void beginDraggingFrame(JComponent frame);
+
+ /**
+ * This method drags the given JInternalFrame to the given x and y
+ * coordinates.
+ *
+ * @param frame The JInternalFrame to drag.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ */
+ void dragFrame(JComponent frame, int x, int y);
+
+ /**
+ * This method should be called after dragFrame calls. Any information used
+ * by the DesktopManager for dragging the JInternalFrame can be cleared.
+ *
+ * @param frame The JInternalFrame that finished dragging.
+ */
+ void endDraggingFrame(JComponent frame);
+
+ /**
+ * This method should be called prior to any resizeFrame calls. Any state
+ * information needed by the DesktopManager to resize the JInternalFrame
+ * will be prepared here.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param direction One of eight directions specified by SwingConstants.
+ */
+ void beginResizingFrame(JComponent frame, int direction);
+
+ /**
+ * This method is called to resize the given JInternalFrame to the given
+ * bounds.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ * @param width The new width.
+ * @param height The new height.
+ */
+ void resizeFrame(JComponent frame, int x, int y, int width, int height);
+
+ /**
+ * This method is called to signify that the resize is finished. Any
+ * information used to resize the JInternalFrame can now be cleared.
+ *
+ * @param frame The JInternalFrame that just finished dragging.
+ */
+ void endResizingFrame(JComponent frame);
+
+ /**
+ * This method does the actual work for reshaping the JInternalFrame.
+ *
+ * @param frame The JInternalFrame to resize.
+ * @param x The new x coordinate.
+ * @param y The new y coordinate.
+ * @param width The new width.
+ * @param height The new height.
+ */
+ void setBoundsForFrame(JComponent frame, int x, int y, int width, int height);
+} // DesktopManager
diff --git a/libjava/classpath/javax/swing/FocusManager.java b/libjava/classpath/javax/swing/FocusManager.java
new file mode 100644
index 000000000..b8459d4d5
--- /dev/null
+++ b/libjava/classpath/javax/swing/FocusManager.java
@@ -0,0 +1,524 @@
+/* FocusManager.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.DefaultKeyboardFocusManager;
+import java.awt.FocusTraversalPolicy;
+import java.awt.KeyEventDispatcher;
+import java.awt.KeyEventPostProcessor;
+import java.awt.KeyboardFocusManager;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.VetoableChangeListener;
+import java.util.Set;
+
+/**
+ * This class has been obsoleted by the new
+ * {@link java.awt.KeyboardFocusManager} and
+ * {@link java.awt.DefaultKeyboardFocusManager} API.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class FocusManager
+ extends DefaultKeyboardFocusManager
+{
+ /**
+ * A FocusManager that wraps an AWT KeyboardFocusManager and forwards all
+ * method calls to it. This is used for compatibility with the new focus
+ * system.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class WrappingFocusManager
+ extends FocusManager
+ {
+ /**
+ * The wrapped KeyboardFocusManager.
+ */
+ private KeyboardFocusManager wrapped;
+
+ /**
+ * Creates a new instance of WrappedFocusManager.
+ *
+ * @param fm the focus manager to wrap
+ */
+ WrappingFocusManager(KeyboardFocusManager fm)
+ {
+ wrapped = fm;
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#dispatchEvent(AWTEvent)}.
+ *
+ * @param ev the event to dispatch
+ *
+ * @return true if the event has been dispatched,
+ * false otherwise
+ */
+ public boolean dispatchEvent(AWTEvent ev)
+ {
+ return wrapped.dispatchEvent(ev);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#dispatchKeyEvent(KeyEvent)}.
+ *
+ * @param ev the event to dispatch
+ *
+ * @return true if the event has been dispatched,
+ * false otherwise
+ */
+ public boolean dispatchKeyEvent(KeyEvent ev)
+ {
+ return wrapped.dispatchKeyEvent(ev);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#downFocusCycle(Container)}.
+ *
+ * @param c the container
+ */
+ public void downFocusCycle(Container c)
+ {
+ wrapped.downFocusCycle(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#upFocusCycle(Container)}.
+ *
+ * @param c the container
+ */
+ public void upFocusCycle(Container c)
+ {
+ wrapped.upFocusCycle(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#focusNextComponent(Component)}.
+ *
+ * @param c the component
+ */
+ public void focusNextComponent(Component c)
+ {
+ wrapped.focusNextComponent(c);
+ }
+
+ /**
+ * Wraps
+ * {@link DefaultKeyboardFocusManager#focusPreviousComponent(Component)}.
+ *
+ * @param c the component
+ */
+ public void focusPreviousComponent(Component c)
+ {
+ wrapped.focusPreviousComponent(c);
+ }
+
+ /**
+ * Wraps {@link DefaultKeyboardFocusManager#postProcessKeyEvent(KeyEvent)}.
+ *
+ * @param e the key event
+ *
+ * @return a boolead
+ */
+ public boolean postProcessKeyEvent(KeyEvent e)
+ {
+ return wrapped.postProcessKeyEvent(e);
+ }
+
+ /**
+ * Wraps
+ * {@link DefaultKeyboardFocusManager#processKeyEvent(Component, KeyEvent)}.
+ *
+ * @param c the component
+ * @param e the key event
+ */
+ public void processKeyEvent(Component c, KeyEvent e)
+ {
+ wrapped.processKeyEvent(c, e);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#addKeyEventDispatcher(KeyEventDispatcher)}.
+ *
+ * @param d the dispatcher
+ */
+ public void addKeyEventDispatcher(KeyEventDispatcher d)
+ {
+ wrapped.addKeyEventDispatcher(d);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#addKeyEventPostProcessor(KeyEventPostProcessor)}.
+ *
+ * @param p the post processor
+ */
+ public void addKeyEventPostProcessor(KeyEventPostProcessor p)
+ {
+ wrapped.addKeyEventPostProcessor(p);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(PropertyChangeListener)}.
+ *
+ * @param l the property change listener
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ wrapped.addPropertyChangeListener(l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addPropertyChangeListener(String, PropertyChangeListener)}.
+ *
+ * @param p the property name
+ * @param l the property change listener
+ */
+ public void addPropertyChangeListener(String p, PropertyChangeListener l)
+ {
+ wrapped.addPropertyChangeListener(p, l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(String, VetoableChangeListener)}.
+ *
+ * @param p the property name
+ * @param l the vetoable change listener
+ */
+ public void addVetoableChangeListener(String p, VetoableChangeListener l)
+ {
+ wrapped.addVetoableChangeListener(p, l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#addVetoableChangeListener(VetoableChangeListener)}.
+ *
+ * @param l the vetoable change listener
+ */
+ public void addVetoableChangeListener(VetoableChangeListener l)
+ {
+ wrapped.addVetoableChangeListener(l);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#clearGlobalFocusOwner()}.
+ */
+ public void clearGlobalFocusOwner()
+ {
+ wrapped.clearGlobalFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getActiveWindow()}.
+ *
+ * @return the active window
+ */
+ public Window getActiveWindow()
+ {
+ return wrapped.getActiveWindow();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getCurrentFocusCycleRoot()}.
+ *
+ * @return the focus cycle root
+ */
+ public Container getCurrentFocusCycleRoot()
+ {
+ return wrapped.getCurrentFocusCycleRoot();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalKeys(int)}.
+ *
+ * @param i the ID
+ *
+ * @return the focus traversal keys
+ */
+ public Set getDefaultFocusTraversalKeys(int i)
+ {
+ return wrapped.getDefaultFocusTraversalKeys(i);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getDefaultFocusTraversalPolicy()}.
+ *
+ * @return the focus traversal policy
+ */
+ public FocusTraversalPolicy getDefaultFocusTraversalPolicy()
+ {
+ return wrapped.getDefaultFocusTraversalPolicy();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getFocusedWindow()}.
+ *
+ * @return the focused window
+ */
+ public Window getFocusedWindow()
+ {
+ return wrapped.getFocusedWindow();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getFocusOwner()}.
+ *
+ * @return the focus owner
+ */
+ public Component getFocusOwner()
+ {
+ return wrapped.getFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPermanentFocusOwner()}.
+ *
+ * @return the focus owner
+ */
+ public Component getPermanentFocusOwner()
+ {
+ return wrapped.getPermanentFocusOwner();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners()}.
+ *
+ * @return the property change listeners
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners()
+ {
+ return wrapped.getPropertyChangeListeners();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getPropertyChangeListeners(String)}.
+ *
+ * @param n the property name
+ *
+ * @return the property change listeners
+ */
+ public PropertyChangeListener[] getPropertyChangeListeners(String n)
+ {
+ return wrapped.getPropertyChangeListeners(n);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners()}.
+ *
+ * @return the vetoable change listeners
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners()
+ {
+ return wrapped.getVetoableChangeListeners();
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#getVetoableChangeListeners(String)}.
+ *
+ * @param n the property name
+ *
+ * @return the vetoable change listeners
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners(String n)
+ {
+ return wrapped.getVetoableChangeListeners(n);
+ }
+
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeKeyEventDispatcher(KeyEventDispatcher)}.
+ *
+ * @param d the key event dispatcher to remove
+ */
+ public void removeKeyEventDispatcher(KeyEventDispatcher d)
+ {
+ wrapped.removeKeyEventDispatcher(d);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeKeyEventPostProcessor(KeyEventPostProcessor)}.
+ *
+ * @param p the post processor
+ */
+ public void removeKeyEventPostProcessor(KeyEventPostProcessor p)
+ {
+ wrapped.removeKeyEventPostProcessor(p);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removePropertyChangeListener(PropertyChangeListener)}.
+ *
+ * @param l the listener
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ wrapped.removePropertyChangeListener(l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removePropertyChangeListener(String, PropertyChangeListener)}.
+ *
+ * @param n the property name
+ * @param l the listener
+ */
+ public void removePropertyChangeListener(String n, PropertyChangeListener l)
+ {
+ wrapped.removePropertyChangeListener(n, l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeVetoableChangeListener(VetoableChangeListener)}.
+ *
+ * @param l the listener
+ */
+ public void removeVetoableChangeListener(VetoableChangeListener l)
+ {
+ wrapped.removeVetoableChangeListener(l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#removeVetoableChangeListener(String, VetoableChangeListener)}.
+ *
+ * @param n the property name
+ * @param l the listener
+ */
+ public void removeVetoableChangeListener(String n, VetoableChangeListener l)
+ {
+ wrapped.removeVetoableChangeListener(n, l);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#setDefaultFocusTraversalKeys(int, Set)}.
+ *
+ * @param id the ID
+ * @param k the keystrokes
+ */
+ public void setDefaultFocusTraversalKeys(int id, Set k)
+ {
+ wrapped.setDefaultFocusTraversalKeys(id, k);
+ }
+
+ /**
+ * Wraps {@link KeyboardFocusManager#setDefaultFocusTraversalPolicy(FocusTraversalPolicy)}.
+ *
+ * @param p the focus traversal policy
+ */
+ public void setDefaultFocusTraversalPolicy(FocusTraversalPolicy p)
+ {
+ wrapped.setDefaultFocusTraversalPolicy(p);
+ }
+
+ /**
+ * Wraps
+ * {@link KeyboardFocusManager#setGlobalCurrentFocusCycleRoot(Container)}.
+ *
+ * @param r the focus cycle root
+ */
+ public void setGlobalCurrentFocusCycleRoot(Container r)
+ {
+ wrapped.setGlobalCurrentFocusCycleRoot(r);
+ }
+ }
+
+ /**
+ * FOCUS_MANAGER_CLASS_PROPERTY
+ */
+ public static final String FOCUS_MANAGER_CLASS_PROPERTY =
+ "FocusManagerClassName";
+
+ /**
+ * Constructor FocusManager
+ */
+ public FocusManager()
+ {
+ super();
+ }
+
+ /**
+ * getCurrentManager
+ * @return FocusManager
+ */
+ public static FocusManager getCurrentManager()
+ {
+ KeyboardFocusManager m =
+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
+ return new WrappingFocusManager(m);
+ }
+
+ /**
+ * setCurrentManager
+ * @param manager TODO
+ */
+ public static void setCurrentManager(FocusManager manager)
+ {
+ KeyboardFocusManager.setCurrentKeyboardFocusManager(manager);
+ }
+
+ /**
+ * disableSwingFocusManager
+ * @deprecated 1.4
+ */
+ public static void disableSwingFocusManager()
+ {
+ // TODO
+ }
+
+ /**
+ * isFocusManagerEnabled
+ * @return boolean
+ * @deprecated 1.4
+ */
+ public static boolean isFocusManagerEnabled()
+ {
+ return false; // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/GrayFilter.java b/libjava/classpath/javax/swing/GrayFilter.java
new file mode 100644
index 000000000..a7f969386
--- /dev/null
+++ b/libjava/classpath/javax/swing/GrayFilter.java
@@ -0,0 +1,97 @@
+/* GrayFilter.java -- Java class for filtering Pixels to produce Gray Pictures
+ Copyright (C) 1999, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Image;
+import java.awt.Toolkit;
+import java.awt.image.FilteredImageSource;
+import java.awt.image.RGBImageFilter;
+
+/**
+ * Produces grayscale images out of colored images. This is used to provide
+ * default disabled icons for buttons.
+ *
+ * @author original author unknown
+ */
+public class GrayFilter extends RGBImageFilter
+{
+ private boolean b;
+ private double p;
+
+ /**
+ * Create a GrayFilter. If b is true then brighten. Also, indicate how much
+ * gray.
+ *
+ * @param b if brighten
+ * @param p percent of gray, 0 - 100
+ */
+ public GrayFilter(boolean b, int p)
+ {
+ this.b = b; //FIXME - HANDLE THIS
+ this.p = (1. - (p / 100.)) / 3.;
+ }
+
+ /**
+ * Create grayed image
+ *
+ * @param src image to gray
+ *
+ * @return a grayed image
+ */
+ public static Image createDisabledImage(Image src)
+ {
+ return (Toolkit.getDefaultToolkit().createImage(new FilteredImageSource(
+ src.getSource(), new GrayFilter(true, 0))));
+ }
+
+ /**
+ * Filter RGB to gray
+ */
+ public int filterRGB(int x, int y, int rgb)
+ {
+ int alpha = 0xff000000 & rgb;
+ int red = (0xff0000 & rgb) >> 16;
+ int green = (0xff00 & rgb) >> 8;
+ int blue = (0xff & rgb);
+ int gray = (int) ((0.299 * red + 0.587 * green + 0.114 * blue) * p);
+ if (b)
+ gray = Math.min(gray + 128, 255);
+ return gray | gray << 8 | gray << 16 | alpha ;
+ }
+}
diff --git a/libjava/classpath/javax/swing/Icon.java b/libjava/classpath/javax/swing/Icon.java
new file mode 100644
index 000000000..ed7f94fda
--- /dev/null
+++ b/libjava/classpath/javax/swing/Icon.java
@@ -0,0 +1,73 @@
+/* Icon.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Graphics;
+
+/**
+ * Defines the methods that an object must implement if it should be used
+ * as an icon in Swing.
+ */
+public interface Icon
+{
+ /**
+ * Returns the height of the icon.
+ *
+ * @return The height of the icon.
+ */
+ int getIconHeight();
+
+ /**
+ * Returns the width of the icon.
+ *
+ * @return The width of the icon.
+ */
+ int getIconWidth();
+
+ /**
+ * Draws the icon at the location (x, y) on the specified graphics device.
+ *
+ * @param c a component related to the icon in some way (can be ignored by
+ some implementing classes).
+ * @param g the graphics device.
+ * @param x the x-coordinate.
+ * @param y the y-coordinate.
+ */
+ void paintIcon(Component c, Graphics g, int x, int y);
+}
diff --git a/libjava/classpath/javax/swing/ImageIcon.java b/libjava/classpath/javax/swing/ImageIcon.java
new file mode 100644
index 000000000..c78d04c58
--- /dev/null
+++ b/libjava/classpath/javax/swing/ImageIcon.java
@@ -0,0 +1,471 @@
+/* ImageIcon.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.IllegalComponentStateException;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
+import java.io.Serializable;
+import java.net.URL;
+import java.util.Locale;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleIcon;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleStateSet;
+
+/**
+ * An {@link Icon} implementation that is backed by an {@link Image}.
+ */
+public class ImageIcon
+ implements Icon, Serializable, Accessible
+{
+ /**
+ * Provides the accessibility features for the ImageIcon
+ * class.
+ */
+ protected class AccessibleImageIcon
+ extends AccessibleContext
+ implements AccessibleIcon, Serializable
+ {
+ private static final long serialVersionUID = 2113430526551336564L;
+
+ /**
+ * Creates a new instance of AccessibleImageIcon.
+ */
+ protected AccessibleImageIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the ImageIcon.
+ *
+ * @return {@link AccessibleRole#ICON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.ICON;
+ }
+
+ /**
+ * Returns the accessible state for the ImageIcon. To
+ * match the reference implementation, this method always returns
+ * null.
+ *
+ * @return null.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the accessible parent of this object. To match the reference
+ * implementation, this method always returns null.
+ *
+ * @return null.
+ */
+ public Accessible getAccessibleParent()
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the index of this object in its accessible parent. To match
+ * the reference implementation, this method always returns -1.
+ *
+ * @return -1.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ // refer to Sun's bug report 4269253
+ return -1;
+ }
+
+ /**
+ * Returns the number of accessible children of this component,
+ * which is 0, because an {@link ImageIcon} has no children.
+ *
+ * @return 0.
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the accessible child at index i, which is
+ * null in this case because an {@link ImageIcon} has no
+ * children.
+ *
+ * @param i the index of the child to be fetched
+ *
+ * @return null.
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale of this object. To match the reference
+ * implementation, this method always returns null.
+ *
+ * @return null.
+ */
+ public Locale getLocale()
+ throws IllegalComponentStateException
+ {
+ // refer to Sun's bug report 4269253
+ return null;
+ }
+
+ /**
+ * Returns the accessible icon description. This returns the
+ * description property of the underlying {@link ImageIcon}.
+ *
+ * @return The description (possibly null).
+ *
+ * @see #setAccessibleIconDescription(String)
+ */
+ public String getAccessibleIconDescription()
+ {
+ return getDescription();
+ }
+
+ /**
+ * Sets the accessible icon description. This sets the
+ * description property of the underlying {@link ImageIcon}.
+ *
+ * @param newDescr the description (null permitted).
+ *
+ * @see #getAccessibleIconDescription()
+ */
+ public void setAccessibleIconDescription(String newDescr)
+ {
+ setDescription(newDescr);
+ }
+
+ /**
+ * Returns the icon height. This returns the iconHeight
+ * property of the underlying {@link ImageIcon}.
+ *
+ * @return The icon height.
+ */
+ public int getAccessibleIconHeight()
+ {
+ return getIconHeight();
+ }
+
+ /**
+ * Returns the icon width. This returns the iconWidth property
+ * of the underlying {@link ImageIcon}.
+ *
+ * @return The icon width.
+ */
+ public int getAccessibleIconWidth()
+ {
+ return getIconWidth();
+ }
+ } // AccessibleIcon
+
+ private static final long serialVersionUID = 532615968316031794L;
+
+ /** A dummy Component that is used in the MediaTracker. */
+ protected static final Component component = new Component()
+ {
+ // No need to implement this.
+ };
+
+ /** The MediaTracker used to monitor the loading of images. */
+ protected static final MediaTracker tracker = new MediaTracker(component);
+
+ /** The ID that is used in the tracker. */
+ private static int id;
+
+ Image image;
+ String description;
+ ImageObserver observer;
+
+ /** The image loading status. */
+ private int loadStatus;
+
+ /** The AccessibleContext of this ImageIcon. */
+ private AccessibleContext accessibleContext;
+
+ /**
+ * Creates an ImageIcon without any properties set.
+ */
+ public ImageIcon()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Constructs an ImageIcon given a filename. The icon's description
+ * is initially set to the filename itself. A filename of "" means
+ * create a blank icon.
+ *
+ * @param filename name of file to load or "" for a blank icon
+ */
+ public ImageIcon(String filename)
+ {
+ this(filename, filename);
+ }
+
+ /**
+ * Constructs an ImageIcon from the given filename, setting its
+ * description to the given description. A filename of "" means
+ * create a blank icon.
+ *
+ * @param filename name of file to load or "" for a blank icon
+ * @param description human-readable description of this icon
+ */
+ public ImageIcon(String filename, String description)
+ {
+ this(Toolkit.getDefaultToolkit().getImage(filename), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given byte array without any
+ * description set.
+ */
+ public ImageIcon(byte[] imageData)
+ {
+ this(imageData, null);
+ }
+
+ /**
+ * Creates an ImageIcon from the given byte array and sets the given
+ * description.
+ */
+ public ImageIcon(byte[] imageData, String description)
+ {
+ this(Toolkit.getDefaultToolkit().createImage(imageData), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given URL and sets the description
+ * to the URL String representation.
+ */
+ public ImageIcon(URL url)
+ {
+ this(url, url.toString());
+ }
+
+ /**
+ * Creates an ImageIcon from the given URL and sets the given
+ * description.
+ */
+ public ImageIcon(URL url, String description)
+ {
+ this(Toolkit.getDefaultToolkit().getImage(url), description);
+ }
+
+ /**
+ * Creates an ImageIcon from the given Image without any description
+ * set.
+ */
+ public ImageIcon(Image image)
+ {
+ this(image, null);
+ }
+
+ /**
+ * Creates an ImageIcon from the given Image and sets the given
+ * description.
+ */
+ public ImageIcon(Image image, String description)
+ {
+ setImage(image);
+ setDescription(description);
+ }
+
+ /**
+ * Returns the ImageObserver that is used for all Image
+ * operations. Defaults to null when not explicitly set.
+ */
+ public ImageObserver getImageObserver()
+ {
+ return observer;
+ }
+
+ /**
+ * Sets the ImageObserver that will be used for all Image
+ * operations. Can be set to null (the default) when no observer is
+ * needed.
+ */
+ public void setImageObserver(ImageObserver newObserver)
+ {
+ observer = newObserver;
+ }
+
+ /**
+ * Returns the backing Image for this ImageIcon. Might be set to
+ * null in which case no image is shown.
+ */
+ public Image getImage()
+ {
+ return image;
+ }
+
+ /**
+ * Explicitly sets the backing Image for this ImageIcon. Will call
+ * loadImage() to make sure that the Image is completely loaded
+ * before returning.
+ */
+ public void setImage(Image image)
+ {
+ loadImage(image);
+ this.image = image;
+ }
+
+ /**
+ * Returns a human readable description for this ImageIcon or null
+ * when no description is set or available.
+ */
+ public String getDescription()
+ {
+ return description;
+ }
+
+ /**
+ * Sets a human readable description for this ImageIcon. Can be set
+ * to null when no description is available.
+ */
+ public void setDescription(String description)
+ {
+ this.description = description;
+ }
+
+ /**
+ * Returns the the height of the backing Image, or -1 if the backing
+ * Image is null. The getHeight() method of the Image will be called
+ * with the set observer of this ImageIcon.
+ */
+ public int getIconHeight()
+ {
+ if (image == null)
+ return -1;
+
+ return image.getHeight(observer);
+ }
+
+ /**
+ * Returns the the width of the backing Image, or -1 if the backing
+ * Image is null. The getWidth() method of the Image will be called
+ * with the set observer of this ImageIcon.
+ */
+ public int getIconWidth()
+ {
+ if (image == null)
+ return -1;
+
+ return image.getWidth(observer);
+ }
+
+ /**
+ * Calls g.drawImage() on the backing Image using the
+ * set observer of this ImageIcon. If the set observer is null, the
+ * given Component is used as observer.
+ */
+ public void paintIcon(Component c, Graphics g, int x, int y)
+ {
+ g.drawImage(image, x, y, observer != null ? observer : c);
+ }
+
+ /**
+ * Loads the image and blocks until the loading operation is finished.
+ *
+ * @param image the image to be loaded
+ */
+ protected void loadImage(Image image)
+ {
+ try
+ {
+ tracker.addImage(image, id);
+ id++;
+ tracker.waitForID(id - 1);
+ }
+ catch (InterruptedException ex)
+ {
+ // Ignore this for now.
+ }
+ finally
+ {
+ loadStatus = tracker.statusID(id - 1, false);
+ tracker.removeImage(image, id - 1);
+ }
+ }
+
+ /**
+ * Returns the load status of the icon image.
+ *
+ * @return the load status of the icon image
+ *
+ * @see MediaTracker#COMPLETE
+ * @see MediaTracker#ABORTED
+ * @see MediaTracker#ERRORED
+ */
+ public int getImageLoadStatus()
+ {
+ return loadStatus;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * ImageIcon instance.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleImageIcon}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleImageIcon();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/InputMap.java b/libjava/classpath/javax/swing/InputMap.java
new file mode 100644
index 000000000..40c43daa4
--- /dev/null
+++ b/libjava/classpath/javax/swing/InputMap.java
@@ -0,0 +1,235 @@
+/* InputMap.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Maps {@link KeyStroke}s to arbitrary objects, usually Strings. This
+ * is used in combination with {@link ActionMap}s.
+ *
+ * If a component receives an input event, this is looked up in
+ * the component's InputMap. The result is an object which
+ * serves as a key to the component's ActionMap. Finally
+ * the Action that is stored is executed.
+ *
+ * @author Andrew Selkirk
+ * @author Michael Koch
+ *
+ * @since 1.3
+ */
+public class InputMap
+ implements Serializable
+{
+ private static final long serialVersionUID = -5429059542008604257L;
+
+ /**
+ * Storage for the KeyStroke --> Object mappings.
+ */
+ private Map inputMap;
+
+ /**
+ * An optional parent map.
+ */
+ private InputMap parent;
+
+ /**
+ * Creates a new InputMap instance. This default instance
+ * contains no mappings and has no parent.
+ */
+ public InputMap()
+ {
+ // nothing to do
+ }
+
+ /**
+ * Returns the binding for the specified keystroke, if there is one.
+ *
+ * @param keystroke the key of the entry (null is ignored).
+ *
+ * @return The binding associated with the specified keystroke (or
+ * null).
+ */
+ public Object get(KeyStroke keystroke)
+ {
+ Object result = null;
+ if (inputMap != null)
+ result = inputMap.get(keystroke);
+
+ if (result == null && parent != null)
+ result = parent.get(keystroke);
+ return result;
+ }
+
+ /**
+ * Puts a new entry into the InputMap. If
+ * actionMapKey is null any existing entry will be
+ * removed.
+ *
+ * @param keystroke the keystroke for the entry (null is
+ * ignored).
+ * @param actionMapKey the action (null permitted).
+ */
+ public void put(KeyStroke keystroke, Object actionMapKey)
+ {
+ if (keystroke == null)
+ return;
+ if (inputMap == null)
+ inputMap = new HashMap();
+ if (actionMapKey == null)
+ inputMap.remove(keystroke);
+ else
+ inputMap.put(keystroke, actionMapKey);
+ }
+
+ /**
+ * Removes an entry from this InputMap. Note that this will
+ * not remove any entry from the parent map, if there is one.
+ *
+ * @param keystroke the key of the entry to remove (null is
+ * ignored).
+ */
+ public void remove(KeyStroke keystroke)
+ {
+ if (inputMap != null)
+ inputMap.remove(keystroke);
+ }
+
+ /**
+ * Returns the parent of this InputMap. The default value
+ * is null.
+ *
+ * @return The parent map (possibly null).
+ *
+ * @see #setParent(InputMap)
+ */
+ public InputMap getParent()
+ {
+ return parent;
+ }
+
+ /**
+ * Sets a parent for this InputMap. If a parent is specified,
+ * the {@link #get(KeyStroke)} method will look in the parent if it cannot
+ * find an entry in this map.
+ *
+ * @param parentMap the new parent (null permitted).
+ *
+ * @see #getParent()
+ */
+ public void setParent(InputMap parentMap)
+ {
+ parent = parentMap;
+ }
+
+ /**
+ * Returns the number of entries in this InputMap. This count
+ * does not include any entries from the parent map, if there is one.
+ *
+ * @return The number of entries.
+ */
+ public int size()
+ {
+ int result = 0;
+ if (inputMap != null)
+ result = inputMap.size();
+ return result;
+ }
+
+ /**
+ * Clears the entries from this InputMap. The parent map, if
+ * there is one, is not cleared.
+ */
+ public void clear()
+ {
+ if (inputMap != null)
+ inputMap.clear();
+ }
+
+ /**
+ * Returns all keys of entries in this InputMap. This does not
+ * include keys defined in the parent, if there is one (use the
+ * {@link #allKeys()} method for that case).
+ *
+ * Following the behaviour of the reference implementation, this method will
+ * return null when no entries have been added to the map,
+ * and a zero length array if entries have been added but subsequently
+ * removed (or cleared) from the map.
+ *
+ * @return An array of keys (may be null or have zero length).
+ */
+ public KeyStroke[] keys()
+ {
+ if (inputMap != null)
+ {
+ KeyStroke[] array = new KeyStroke[size()];
+ return (KeyStroke[]) inputMap.keySet().toArray(array);
+ }
+ return null;
+ }
+
+ /**
+ * Returns all keys of entries in this InputMap and all its
+ * parents.
+ *
+ * @return An array of keys (may be null or have zero length).
+ */
+ public KeyStroke[] allKeys()
+ {
+ Set set = new HashSet();
+
+ if (parent != null)
+ {
+ Object[] parentKeys = parent.allKeys();
+ if (parentKeys != null)
+ set.addAll(Arrays.asList(parentKeys));
+ }
+ if (inputMap != null)
+ set.addAll(inputMap.keySet());
+ if (set.size() == 0)
+ return null;
+ KeyStroke[] array = new KeyStroke[set.size()];
+ return (KeyStroke[]) set.toArray(array);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/InputVerifier.java b/libjava/classpath/javax/swing/InputVerifier.java
new file mode 100644
index 000000000..eeb81b5d5
--- /dev/null
+++ b/libjava/classpath/javax/swing/InputVerifier.java
@@ -0,0 +1,79 @@
+/* InputVerifier.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+
+/**
+ * Verifies the user input on a component before the focus is shifted.
+ * It is sometimes necessary that input components have a valid state before
+ * they loose focus. Such components can have a InputVerifier
+ * subclass registered, that permits or vetos a focus change request.
+ *
+ * @author Andrew Selkirk
+ */
+public abstract class InputVerifier
+{
+ /**
+ * Creates a InputVerifier
+ */
+ public InputVerifier()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * verify
+ *
+ * @param component the component to verify
+ *
+ * @return true if valid, false otherwise.
+ */
+ public abstract boolean verify(JComponent component);
+
+ /**
+ * shouldYieldFocus
+ *
+ * @param component the component to verify
+ *
+ * @return true if valid, false otherwise.
+ */
+ public boolean shouldYieldFocus(JComponent component)
+ {
+ return verify(component);
+ }
+}
diff --git a/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java b/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java
new file mode 100644
index 000000000..9187f2924
--- /dev/null
+++ b/libjava/classpath/javax/swing/InternalFrameFocusTraversalPolicy.java
@@ -0,0 +1,60 @@
+/* InternalFrameFocusTraversalPolicy.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.FocusTraversalPolicy;
+
+/**
+ * A {@link FocusTraversalPolicy} that provides the additional capability
+ * to determine a {@link JInternalFrame}'s initially focused component
+ * when it is selected.
+ *
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public abstract class InternalFrameFocusTraversalPolicy
+ extends FocusTraversalPolicy
+{
+ public Component getInitialComponent(JInternalFrame frame)
+ {
+ return getDefaultComponent(frame);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JApplet.java b/libjava/classpath/javax/swing/JApplet.java
new file mode 100644
index 000000000..d725131e6
--- /dev/null
+++ b/libjava/classpath/javax/swing/JApplet.java
@@ -0,0 +1,224 @@
+/* JApplet.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.applet.Applet;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.LayoutManager;
+import java.awt.event.KeyEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A top-level container that is usually used in web browsers.
+ *
+ * @author original author unknown
+ */
+public class JApplet extends Applet
+ implements RootPaneContainer, Accessible
+{
+ /**
+ * Provides accessibility support for JApplet.
+ */
+ protected class AccessibleJApplet extends Applet.AccessibleApplet
+ {
+ /**
+ * Creates a new instance of AccessibleJApplet.
+ */
+ protected AccessibleJApplet()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * The accessible context for this JApplet.
+ */
+ protected AccessibleContext accessibleContext;
+
+ private static final long serialVersionUID = 7269359214497372587L;
+
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ public JApplet()
+ {
+ super.setLayout(new BorderLayout(1, 1));
+ getRootPane(); // Will do set/create.
+ setRootPaneCheckingEnabled(true); // Init stage is now over.
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJApplet();
+ return accessibleContext;
+ }
+
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JButton.java b/libjava/classpath/javax/swing/JButton.java
new file mode 100644
index 000000000..40c104194
--- /dev/null
+++ b/libjava/classpath/javax/swing/JButton.java
@@ -0,0 +1,276 @@
+/* JButton.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ButtonUI;
+
+
+/**
+ * A general purpose push button. JButtons can display a label,
+ * an {@link Icon} or both.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JButton extends AbstractButton
+ implements Accessible
+{
+
+ /**
+ * Accessibility support for JButtons.
+ */
+ protected class AccessibleJButton
+ extends AbstractButton.AccessibleAbstractButton
+ {
+ /**
+ * Returns the accessible role that this component represents.
+ * This is {@link AccessibleRole#PUSH_BUTTON} for JButtons.
+ *
+ * @return the accessible role that this component represents
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PUSH_BUTTON;
+ }
+ }
+
+ private static final long serialVersionUID = -1907255238954382202L;
+
+ /**
+ * Indicates if this button is capable to become the default button.
+ */
+ private boolean defaultCapable;
+
+ /**
+ * Creates a new button with an empty string for the button text and no
+ * icon.
+ */
+ public JButton()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new button from the specified action.
+ *
+ * @param a the action (null permitted).
+ *
+ * @see AbstractButton#setAction(Action)
+ */
+ public JButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Creates a new button with the specified icon (and an empty string for
+ * the button text).
+ *
+ * @param icon the icon (null permitted).
+ */
+ public JButton(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new button with the specified text and no icon.
+ *
+ * @param text the button text (null permitted, will be
+ * substituted by an empty string).
+ */
+ public JButton(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new button with the specified text and icon.
+ *
+ * @param text the button text (null permitted, will be
+ * substituted by an empty string).
+ * @param icon the icon (null permitted).
+ */
+ public JButton(String text, Icon icon)
+ {
+ super();
+ setModel(new DefaultButtonModel());
+ init(text, icon);
+ defaultCapable = true;
+ }
+
+ protected void configurePropertiesFromAction(Action a)
+ {
+ super.configurePropertiesFromAction(a);
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JButton component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJButton}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJButton();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the suffix ("ButtonUI" in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a JButton.
+ *
+ * @return "ButtonUI".
+ */
+ public String getUIClassID()
+ {
+ // Returns a string that specifies the name of the L&F class that renders
+ // this component.
+ return "ButtonUI";
+ }
+
+ /**
+ * Returns true if this button is the default button in
+ * its JRootPane. The default button gets automatically
+ * activated when the user presses ENTER (or whatever
+ * key this is bound to in the current Look and Feel).
+ *
+ * @return true if this button is the default button in
+ * its JRootPane
+ *
+ * @see #isDefaultCapable()
+ * @see #setDefaultCapable(boolean)
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public boolean isDefaultButton()
+ {
+ // The default button is managed by the JRootPane, so the safest way
+ // to determine this property is to ask the root pane of this button,
+ // if it exists.
+ JRootPane rp = SwingUtilities.getRootPane(this);
+ boolean isDefault = false;
+ if (rp != null)
+ isDefault = rp.getDefaultButton() == this;
+ return isDefault;
+ }
+
+ /**
+ * Returns true if this button can act as the default button.
+ * This is true by default.
+ *
+ * @return true if this button can act as the default button
+ *
+ * @see #setDefaultCapable(boolean)
+ * @see #isDefaultButton()
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public boolean isDefaultCapable()
+ {
+ // Returns whether or not this button is capable of being the default
+ // button on the RootPane.
+ return defaultCapable;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JButton.
+ *
+ * @return A string describing the attributes of this JButton
+ * (never null).
+ */
+ protected String paramString()
+ {
+ String superParam = super.paramString();
+
+ // 41 is the maximum number of chars which may be needed.
+ CPStringBuilder sb = new CPStringBuilder(41);
+ sb.append(",defaultButton=").append(isDefaultButton());
+ sb.append(",defaultCapable=").append(defaultCapable);
+
+ return superParam + sb.toString();
+ }
+
+ /**
+ * Overrides JComponent.removeNotify to check if this button is currently
+ * set as the default button on the RootPane, and if so, sets the RootPane's
+ * default button to null to ensure the RootPane doesn't hold onto an invalid
+ * button reference.
+ */
+ public void removeNotify()
+ {
+ JRootPane root = SwingUtilities.getRootPane(this);
+ if (root != null && root.getDefaultButton() == this)
+ root.setDefaultButton(null);
+ super.removeNotify();
+ }
+
+ /**
+ * Sets the defaultCapable property which indicates if
+ * this button may become the default button in its JRootPane.
+ *
+ * @param defaultCapable true if this button can become the
+ * default button in its JRootPane, false otherwise
+ *
+ * @see #setDefaultCapable(boolean)
+ * @see #isDefaultButton()
+ * @see JRootPane#getDefaultButton()
+ * @see JRootPane#setDefaultButton(JButton)
+ */
+ public void setDefaultCapable(boolean defaultCapable)
+ {
+ this.defaultCapable = defaultCapable;
+ }
+
+ /**
+ * Sets this button's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ButtonUI) UIManager.getUI(this));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JCheckBox.java b/libjava/classpath/javax/swing/JCheckBox.java
new file mode 100644
index 000000000..0bf871a50
--- /dev/null
+++ b/libjava/classpath/javax/swing/JCheckBox.java
@@ -0,0 +1,176 @@
+/* JCheckBox.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A small box that displays a check or not, depending on it's
+ * selected state. This works very similar to
+ * {@link JToggleButton} and {@link JRadioButton}, but in UI design it
+ * has different semantics. JCheckBoxes are usually
+ * used in multiple-choice scenarios, where a user can select 0..n
+ * of n different options. (This is in contrast to the general RadioButton
+ * semantics where the user can select exactly one of n options).
+ *
+ * Note however that this semantics is in no way enforced by the
+ * JCheckBox.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JCheckBox extends JToggleButton implements Accessible
+{
+
+ /**
+ * Provides accessibility support for JCheckBox.
+ */
+ protected class AccessibleJCheckBox extends AccessibleJToggleButton
+ {
+ /**
+ * Creates a new instance of AccessibleJCheckBox.
+ */
+ protected AccessibleJCheckBox()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessble role of JCheckBox,
+ * {@link AccessibleRole#CHECK_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.CHECK_BOX;
+ }
+ }
+
+ private static final long serialVersionUID = -5246739313864538930L;
+
+ public static final String BORDER_PAINTED_FLAT_CHANGED_PROPERTY =
+ "borderPaintedFlat";
+
+ private boolean borderPaintedFlat;
+
+ private void init()
+ {
+ borderPainted = false;
+ contentAreaFilled = false;
+ }
+
+ public JCheckBox()
+ {
+ this(null, null, false);
+ }
+
+ public JCheckBox(Action action)
+ {
+ super(action);
+ }
+
+ public JCheckBox(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ public JCheckBox(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ public JCheckBox(String text)
+ {
+ this(text, null, false);
+ }
+
+ public JCheckBox(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ public JCheckBox(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ public JCheckBox(String text, Icon icon, boolean selected)
+ {
+ super(text, icon, selected);
+ setHorizontalAlignment(LEADING);
+ setBorderPainted(false);
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel class
+ * that renders this component.
+ */
+ public String getUIClassID()
+ {
+ return "CheckBoxUI";
+ }
+
+ protected String paramString()
+ {
+ return super.paramString() + ",borderPaintedFlat=" + borderPaintedFlat;
+ }
+
+ public boolean isBorderPaintedFlat()
+ {
+ return borderPaintedFlat;
+ }
+
+ public void setBorderPaintedFlat(boolean newValue)
+ {
+ firePropertyChange("borderPaintedFlat", borderPaintedFlat, newValue);
+ borderPaintedFlat = newValue;
+ }
+
+ /**
+ * Returns the accessible context for this JCheckBox.
+ *
+ * @return the accessible context for this JCheckBox
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJCheckBox();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JCheckBoxMenuItem.java b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java
new file mode 100644
index 000000000..b3045be27
--- /dev/null
+++ b/libjava/classpath/javax/swing/JCheckBoxMenuItem.java
@@ -0,0 +1,275 @@
+/* JCheckBoxMenuItem.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+ This file is part of GNU Classpath.
+
+ GNU Classpath is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Classpath 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 for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Classpath; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA.
+
+ Linking this library statically or dynamically with other modules is
+ making a combined work based on this library. Thus, the terms and
+ conditions of the GNU General Public License cover the whole
+ combination.
+
+ As a special exception, the copyright holders of this library give you
+ permission to link this library with independent modules to produce an
+ executable, regardless of the license terms of these independent
+ 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;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A menu item that displays a checkbox. Its behaviour is very similar to
+ * {@link JCheckBox}. Just like the JCheckBox, user can check
+ * and uncheck this menu item by clicking on it. Also
+ * {@link AbstractButton#setSelected} and {@link #setState} can be use used for
+ * the same purpose. JCheckBoxMenuItem uses
+ * ToggleButtonModel to keep track of its selection.
+ *
+ * @author original author unknown
+ */
+public class JCheckBoxMenuItem
+ extends JMenuItem
+ implements SwingConstants, Accessible
+{
+ private static final long serialVersionUID = - 6676402307973384715L;
+
+ /** name for the UI delegate for this menuItem. */
+ private static final String uiClassID = "CheckBoxMenuItemUI";
+
+ /** Indicates whether this menu item is checked. */
+ private boolean state;
+
+ /**
+ * This array contains text of this menu item if this menu item is in checked
+ * state and null it is not.
+ */
+ private Object[] selectedObjects = new Object[1];
+
+ /**
+ * Creates a new JCheckBoxMenuItem object.
+ */
+ public JCheckBoxMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem with given icon
+ *
+ * @param icon Icon for this menu item
+ */
+ public JCheckBoxMenuItem(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem with given label
+ *
+ * @param text Label for this menu item
+ */
+ public JCheckBoxMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem using given action
+ *
+ * @param action Action for this menu item.
+ */
+ public JCheckBoxMenuItem(Action action)
+ {
+ this();
+ setAction(action);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object with given label and icon
+ *
+ * @param text Label for this menu item
+ * @param icon Icon for this menu item
+ */
+ public JCheckBoxMenuItem(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object using specified label and marked as
+ * checked if given 'state' is true.
+ *
+ * @param text Label for this menu item
+ * @param state true if this item should be in checked state
+ * and false otherwise
+ */
+ public JCheckBoxMenuItem(String text, boolean state)
+ {
+ this(text, null, state);
+ }
+
+ /**
+ * Creates a new JCheckBoxMenuItem object with given label, icon, and marked
+ * as checked if given 'state' is true.
+ *
+ * @param text Label for this menu item
+ * @param icon icon for this menu item
+ * @param state true if this item should be in checked state
+ * and false otherwise
+ */
+ public JCheckBoxMenuItem(String text, Icon icon, boolean state)
+ {
+ super(text, icon);
+ setModel(new JToggleButton.ToggleButtonModel());
+ this.state = state;
+ if (state == true)
+ this.setSelected(true);
+ setFocusable(false);
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "JCheckBoxMenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return uiClassID;
+ }
+
+ /**
+ * Returns checked state for this check box menu item.
+ *
+ * @return Returns true if this menu item is in checked state and false
+ * otherwise.
+ */
+ public boolean getState()
+ {
+ return state;
+ }
+
+ /**
+ * Sets state for this check box menu item. If given 'state' is true, then
+ * mark menu item as checked, and uncheck this menu item otherwise.
+ *
+ * @param state new state for this menu item
+ */
+ public synchronized void setState(boolean state)
+ {
+ this.state = state;
+ }
+
+ /**
+ * This method returns array containing label of this menu item if it is
+ * selected and null otherwise.
+ *
+ * @return Array containing label of this menu item if this menu item is
+ * selected or null otherwise.
+ */
+ public Object[] getSelectedObjects()
+ {
+ if (state == true)
+ selectedObjects[0] = this.getText();
+ else
+ selectedObjects[0] = null;
+
+ return selectedObjects;
+ }
+
+ /**
+ * This method overrides JComponent.requestFocus with an empty implementation,
+ * since JCheckBoxMenuItems should not receive focus in general.
+ */
+ public void requestFocus()
+ {
+ // Should do nothing here
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * JCheckBoxMenuItem component, for use in debugging. The
+ * return value is guaranteed to be non-null, but the format
+ * of the string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * JCheckBoxMenuItem.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient to match the reference
+ // implementation here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JCheckBoxMenuItem component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJCheckBoxMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJCheckBoxMenuItem();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the JCheckBoxMenuItem
+ * component.
+ *
+ * @see JCheckBoxMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJCheckBoxMenuItem
+ extends AccessibleJMenuItem
+ {
+ private static final long serialVersionUID = 1079958073579370777L;
+
+ /**
+ * Creates a new AccessibleJCheckBoxMenuItem instance.
+ */
+ protected AccessibleJCheckBoxMenuItem()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the JCheckBoxMenuItem
+ * component.
+ *
+ * @return {@link AccessibleRole#CHECK_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.CHECK_BOX;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JColorChooser.java b/libjava/classpath/javax/swing/JColorChooser.java
new file mode 100644
index 000000000..674f0fd82
--- /dev/null
+++ b/libjava/classpath/javax/swing/JColorChooser.java
@@ -0,0 +1,644 @@
+/* JColorChooser.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTError;
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dialog;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.colorchooser.AbstractColorChooserPanel;
+import javax.swing.colorchooser.ColorSelectionModel;
+import javax.swing.colorchooser.DefaultColorSelectionModel;
+import javax.swing.plaf.ColorChooserUI;
+
+
+/**
+ * A Swing widget that offers users different ways to
+ * select a color. By default, three different panels are presented to the
+ * user that are capable of changing the selected color. There are three ways
+ * to utilize JColorChooser. The first is to build a JColorChooser and add it
+ * to the content pane. The second is to use the createDialog method to
+ * create a JDialog that holds a JColorChooser. The third is to show a
+ * JColorChooser in a JDialog directly using the showDialog method.
+ *
+ * @author original author unknown
+ */
+public class JColorChooser extends JComponent implements Accessible
+{
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 9168066781620640889L;
+
+ /**
+ * Accessibility support for JColorChooser.
+ */
+ protected class AccessibleJColorChooser
+ extends JComponent.AccessibleJComponent
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -2038297864782299082L;
+
+ /**
+ * Constructor AccessibleJColorChooser
+ */
+ protected AccessibleJColorChooser()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ *
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.COLOR_CHOOSER;
+ } // getAccessibleRole()
+ } // AccessibleJColorChooser
+
+ /** The model used with the JColorChooser. */
+ private ColorSelectionModel selectionModel;
+
+ /** The preview panel associated with the JColorChooser. */
+ private JComponent previewPanel;
+
+ /**
+ * The set of AbstractColorChooserPanels associated with the JColorChooser.
+ */
+ private AbstractColorChooserPanel[] chooserPanels;
+
+ /** A Drag and Drop property. */
+ private boolean dragEnabled;
+
+ /**
+ * The property fired by the JColorChooser when the selectionModel property
+ * changes.
+ */
+ public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
+
+ /**
+ * The property fired by the JColorChooser when the previewPanel property
+ * changes.
+ */
+ public static final String PREVIEW_PANEL_PROPERTY = "previewPanel";
+
+ /**
+ * The property fired by the JColorChooser when the chooserPanels property
+ * changes.
+ */
+ public static final String CHOOSER_PANELS_PROPERTY = "chooserPanels";
+
+ /** accessibleContext */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * This method creates a new JColorChooser with the default initial color.
+ */
+ public JColorChooser()
+ {
+ this(new DefaultColorSelectionModel());
+ } // JColorChooser()
+
+ /**
+ * This method creates a new JColorChooser with the given initial color.
+ *
+ * @param initial The initial color.
+ */
+ public JColorChooser(Color initial)
+ {
+ this(new DefaultColorSelectionModel(initial));
+ } // JColorChooser()
+
+ /**
+ * This method creates a new JColorChooser with the given model. The model
+ * will dictate what the initial color for the JColorChooser is.
+ *
+ * @param model The Model to use with the JColorChooser.
+ */
+ public JColorChooser(ColorSelectionModel model)
+ {
+ if (model == null)
+ model = new DefaultColorSelectionModel();
+ selectionModel = model;
+ updateUI();
+ } // JColorChooser()
+
+ /**
+ * This method sets the current color for the JColorChooser.
+ *
+ * @param color The new color for the JColorChooser.
+ */
+ public void setColor(Color color)
+ {
+ if (color != null)
+ selectionModel.setSelectedColor(color);
+ } // setColor()
+
+ /**
+ * This method sets the current color for the JColorChooser using RGB
+ * values.
+ *
+ * @param r The red value.
+ * @param g The green value.
+ * @param b The blue value.
+ */
+ public void setColor(int r, int g, int b)
+ {
+ selectionModel.setSelectedColor(new Color(r, g, b));
+ } // setColor()
+
+ /**
+ * This method sets the current color for the JColorChooser using the
+ * integer value. Bits 0-7 represent the blue value. Bits 8-15 represent
+ * the green value. Bits 16-23 represent the red value.
+ *
+ * @param color The new current color of the JColorChooser.
+ */
+ public void setColor(int color)
+ {
+ setColor(new Color(color, false));
+ } // setColor()
+
+ /**
+ * This method shows a JColorChooser inside a JDialog. The JDialog will
+ * block until it is hidden. The JDialog comes with three buttons: OK,
+ * Cancel, and Reset. Pressing OK or Cancel hide the JDialog. Pressing
+ * Reset will reset the JColorChooser to its initial value.
+ *
+ * @param component The Component that parents the JDialog.
+ * @param title The title displayed in the JDialog.
+ * @param initial The initial color.
+ *
+ * @return The selected color.
+ */
+ public static Color showDialog(Component component, String title,
+ Color initial)
+ {
+ JColorChooser choose = new JColorChooser(initial);
+
+ JDialog dialog = createDialog(component, title, true, choose, null, null);
+
+ dialog.getContentPane().add(choose);
+ dialog.pack();
+ dialog.show();
+
+ return choose.getColor();
+ } // showDialog()
+
+ /**
+ * This is a helper method to make the given JDialog block until it is
+ * hidden. This is package-private to avoid an accessor method.
+ *
+ * @param dialog The JDialog to block.
+ */
+ static void makeModal(JDialog dialog)
+ {
+ try
+ {
+ synchronized (dialog)
+ {
+ while (dialog.isVisible())
+ dialog.wait();
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // TODO: Should this be handled?
+ }
+ }
+
+ /**
+ * This is a helper method to find the first Frame or Dialog ancestor of the
+ * given Component.
+ *
+ * @param c The Component to find ancestors for.
+ *
+ * @return A Frame or Dialog ancestor. Null if none are found.
+ */
+ private static Component findParent(Component c)
+ {
+ Component parent = SwingUtilities.getAncestorOfClass(Frame.class, c);
+ if (parent != null)
+ return parent;
+ parent = SwingUtilities.getAncestorOfClass(Dialog.class, c);
+ return parent;
+ }
+
+ /**
+ * This method will take the given JColorChooser and place it in a JDialog
+ * with the given modal property. Three buttons are displayed in the
+ * JDialog: OK, Cancel and Reset. If OK or Cancel are pressed, the JDialog
+ * is hidden. If Reset is pressed, then the JColorChooser will take on its
+ * default color value. The given okListener will be registered to the OK
+ * button and the cancelListener will be registered to the Cancel button.
+ * If the modal property is set, then the JDialog will block until it is
+ * hidden.
+ *
+ * @param component The Component that will parent the JDialog.
+ * @param title The title displayed in the JDialog.
+ * @param modal The modal property.
+ * @param chooserPane The JColorChooser to place in the JDialog.
+ * @param okListener The ActionListener to register to the OK button.
+ * @param cancelListener The ActionListener to register to the Cancel
+ * button.
+ *
+ * @return A JDialog with the JColorChooser inside of it.
+ *
+ * @throws AWTError If the component is not a suitable parent.
+ */
+ public static JDialog createDialog(Component component, String title,
+ boolean modal, JColorChooser chooserPane,
+ ActionListener okListener,
+ ActionListener cancelListener)
+ {
+ Component parent = findParent(component);
+ if (parent == null)
+ throw new AWTError("No suitable parent found for Component.");
+ JDialog dialog;
+ if (parent instanceof Frame)
+ dialog = new JDialog((Frame) parent, title, true);
+ else
+ dialog = new JDialog((Dialog) parent, title, true);
+
+ dialog.getContentPane().setLayout(new BorderLayout());
+
+ JPanel panel = new JPanel();
+ panel.setLayout(new FlowLayout());
+
+ ActionListener al = new DefaultOKCancelListener(dialog);
+
+ JButton ok = new JButton("OK");
+ ok.addActionListener(okListener);
+ ok.addActionListener(al);
+
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(cancelListener);
+ cancel.addActionListener(al);
+
+ JButton reset = new JButton("Reset");
+ reset.addActionListener(new DefaultResetListener(chooserPane));
+
+ dialog.getContentPane().add(chooserPane, BorderLayout.NORTH);
+
+ panel.add(ok);
+ panel.add(cancel);
+ panel.add(reset);
+
+ dialog.getContentPane().add(panel, BorderLayout.SOUTH);
+
+ return dialog;
+ } // createDialog()
+
+ /**
+ * This method returns the UI Component used for this JColorChooser.
+ *
+ * @return The UI Component for this JColorChooser.
+ */
+ public ColorChooserUI getUI()
+ {
+ return (ColorChooserUI) ui;
+ } // getUI()
+
+ /**
+ * This method sets the UI Component used for this JColorChooser.
+ *
+ * @param ui The UI Component to use with this JColorChooser.
+ */
+ public void setUI(ColorChooserUI ui)
+ {
+ super.setUI(ui);
+ } // setUI()
+
+ /**
+ * This method resets the UI Component property to the Look and Feel
+ * default.
+ */
+ public void updateUI()
+ {
+ setUI((ColorChooserUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a String identifier for the UI Class to be used with
+ * the JColorChooser.
+ *
+ * @return The String identifier for the UI Class.
+ */
+ public String getUIClassID()
+ {
+ return "ColorChooserUI";
+ } // getUIClassID()
+
+ /**
+ * This method returns the current color for the JColorChooser.
+ *
+ * @return The current color for the JColorChooser.
+ */
+ public Color getColor()
+ {
+ return selectionModel.getSelectedColor(); // TODO
+ } // getColor()
+
+ /**
+ * This method changes the previewPanel property for the JTabbedPane. The
+ * previewPanel is responsible for indicating the current color of the
+ * JColorChooser.
+ *
+ * @param component The Component that will act as the previewPanel.
+ */
+ public void setPreviewPanel(JComponent component)
+ {
+ if (component != previewPanel)
+ {
+ JComponent old = previewPanel;
+ previewPanel = component;
+ firePropertyChange(PREVIEW_PANEL_PROPERTY, old, previewPanel);
+ }
+ } // setPreviewPanel()
+
+ /**
+ * This method returns the current previewPanel used with this
+ * JColorChooser.
+ *
+ * @return The current previewPanel.
+ */
+ public JComponent getPreviewPanel()
+ {
+ return previewPanel; // TODO
+ } // getPreviewPanel()
+
+ /**
+ * This method adds the given AbstractColorChooserPanel to the list of the
+ * JColorChooser's chooserPanels.
+ *
+ * @param panel The AbstractColorChooserPanel to add.
+ */
+ public void addChooserPanel(AbstractColorChooserPanel panel)
+ {
+ if (panel == null)
+ return;
+ AbstractColorChooserPanel[] old = chooserPanels;
+ AbstractColorChooserPanel[] newPanels =
+ new AbstractColorChooserPanel[(old == null) ? 1 : old.length + 1];
+ if (old != null)
+ System.arraycopy(old, 0, newPanels, 0, old.length);
+ newPanels[newPanels.length - 1] = panel;
+ chooserPanels = newPanels;
+ panel.installChooserPanel(this);
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, newPanels);
+ } // addChooserPanel()
+
+ /**
+ * This method removes the given AbstractColorChooserPanel from the
+ * JColorChooser's list of chooserPanels.
+ *
+ * @param panel The AbstractColorChooserPanel to remove.
+ *
+ * @return The AbstractColorChooserPanel that was removed.
+ */
+ public AbstractColorChooserPanel removeChooserPanel(AbstractColorChooserPanel panel)
+ {
+ int index = -1;
+ for (int i = 0; i < chooserPanels.length; i++)
+ if (panel == chooserPanels[i])
+ {
+ index = i;
+ break;
+ }
+
+ if (index == -1)
+ return null;
+
+ AbstractColorChooserPanel[] old = chooserPanels;
+ if (chooserPanels.length == 1)
+ chooserPanels = null;
+ else
+ {
+ AbstractColorChooserPanel[] newPanels =
+ new AbstractColorChooserPanel[chooserPanels.length - 1];
+ System.arraycopy(chooserPanels, 0, newPanels, 0, index);
+ System.arraycopy(chooserPanels, index, newPanels, index - 1,
+ chooserPanels.length - index);
+ chooserPanels = newPanels;
+ }
+ panel.uninstallChooserPanel(this);
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, chooserPanels);
+ return panel;
+ }
+
+ /**
+ * This method sets the chooserPanels property for this JColorChooser.
+ *
+ * @param panels The new set of AbstractColorChooserPanels to use.
+ */
+ public void setChooserPanels(AbstractColorChooserPanel[] panels)
+ {
+ if (panels != chooserPanels)
+ {
+ if (chooserPanels != null)
+ for (int i = 0; i < chooserPanels.length; i++)
+ if (chooserPanels[i] != null)
+ chooserPanels[i].uninstallChooserPanel(this);
+
+ AbstractColorChooserPanel[] old = chooserPanels;
+ chooserPanels = panels;
+
+ if (panels != null)
+ for (int i = 0; i < panels.length; i++)
+ if (panels[i] != null)
+ panels[i].installChooserPanel(this);
+
+ firePropertyChange(CHOOSER_PANELS_PROPERTY, old, chooserPanels);
+ }
+ } // setChooserPanels()
+
+ /**
+ * This method returns the AbstractColorChooserPanels used with this
+ * JColorChooser.
+ *
+ * @return The AbstractColorChooserPanels used with this JColorChooser.
+ */
+ public AbstractColorChooserPanel[] getChooserPanels()
+ {
+ return chooserPanels;
+ } // getChooserPanels()
+
+ /**
+ * This method returns the ColorSelectionModel used with this JColorChooser.
+ *
+ * @return The ColorSelectionModel.
+ */
+ public ColorSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ } // getSelectionModel()
+
+ /**
+ * This method sets the ColorSelectionModel to be used with this
+ * JColorChooser.
+ *
+ * @param model The ColorSelectionModel to be used with this JColorChooser.
+ *
+ * @throws AWTError If the given model is null.
+ */
+ public void setSelectionModel(ColorSelectionModel model)
+ {
+ if (model == null)
+ throw new AWTError("ColorSelectionModel is not allowed to be null.");
+ selectionModel = model;
+ } // setSelectionModel()
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param b DOCUMENT ME!
+ */
+ public void setDragEnabled(boolean b)
+ {
+ dragEnabled = b;
+ }
+
+ /**
+ * This method returns a String describing the JColorChooser.
+ *
+ * @return A String describing the JColorChooser.
+ */
+ protected String paramString()
+ {
+ return "JColorChooser";
+ } // paramString()
+
+ /**
+ * getAccessibleContext
+ *
+ * @return AccessibleContext
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJColorChooser();
+
+ return accessibleContext;
+ }
+
+ /**
+ * A helper class that hides a JDialog when the action is performed.
+ */
+ static class DefaultOKCancelListener implements ActionListener
+ {
+ /** The JDialog to hide. */
+ private JDialog dialog;
+
+ /**
+ * Creates a new DefaultOKCancelListener with the given JDialog to hide.
+ *
+ * @param dialog The JDialog to hide.
+ */
+ public DefaultOKCancelListener(JDialog dialog)
+ {
+ super();
+ this.dialog = dialog;
+ }
+
+ /**
+ * This method hides the JDialog when called.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ dialog.hide();
+ }
+ }
+
+ /**
+ * This method resets the JColorChooser color to the initial color when the
+ * action is performed.
+ */
+ static class DefaultResetListener implements ActionListener
+ {
+ /** The JColorChooser to reset. */
+ private JColorChooser chooser;
+
+ /** The initial color. */
+ private Color init;
+
+ /**
+ * Creates a new DefaultResetListener with the given JColorChooser.
+ *
+ * @param chooser The JColorChooser to reset.
+ */
+ public DefaultResetListener(JColorChooser chooser)
+ {
+ super();
+ this.chooser = chooser;
+ init = chooser.getColor();
+ }
+
+ /**
+ * This method resets the JColorChooser to its initial color.
+ *
+ * @param e The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ chooser.setColor(init);
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JComboBox.java b/libjava/classpath/javax/swing/JComboBox.java
new file mode 100644
index 000000000..0d2be0c17
--- /dev/null
+++ b/libjava/classpath/javax/swing/JComboBox.java
@@ -0,0 +1,1499 @@
+/* JComboBox.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.ItemSelectable;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.ComboBoxUI;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.basic.ComboPopup;
+
+/**
+ * A component that allows a user to select any item in its list and
+ * displays the selected item to the user. JComboBox also can show/hide a
+ * popup menu containing its list of item whenever the mouse is pressed
+ * over it.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ * @author Robert Schuster
+ */
+public class JComboBox extends JComponent implements ItemSelectable,
+ ListDataListener,
+ ActionListener,
+ Accessible
+{
+
+ private static final long serialVersionUID = 5654585963292734470L;
+
+ /**
+ * Classes implementing this interface are
+ * responsible for matching key characters typed by the user with combo
+ * box's items.
+ */
+ public static interface KeySelectionManager
+ {
+ int selectionForKey(char aKey, ComboBoxModel aModel);
+ }
+
+ /**
+ * Maximum number of rows that should be visible by default in the
+ * JComboBox's popup
+ */
+ private static final int DEFAULT_MAXIMUM_ROW_COUNT = 8;
+
+ /**
+ * Data model used by JComboBox to keep track of its list data and currently
+ * selected element in the list.
+ */
+ protected ComboBoxModel dataModel;
+
+ /**
+ * Renderer renders(paints) every object in the combo box list in its
+ * associated list cell. This ListCellRenderer is used only when this
+ * JComboBox is uneditable.
+ */
+ protected ListCellRenderer renderer;
+
+ /**
+ * Editor that is responsible for editing an object in a combo box list.
+ */
+ protected ComboBoxEditor editor;
+
+ /**
+ * Number of rows that will be visible in the JComboBox's popup.
+ */
+ protected int maximumRowCount;
+
+ /**
+ * This field indicates if textfield of this JComboBox is editable or not.
+ */
+ protected boolean isEditable;
+
+ /**
+ * This field is reference to the current selection of the combo box.
+ */
+ protected Object selectedItemReminder;
+
+ /**
+ * keySelectionManager
+ */
+ protected KeySelectionManager keySelectionManager;
+
+ /**
+ * This actionCommand is used in ActionEvent that is fired to JComboBox's
+ * ActionListeneres.
+ */
+ protected String actionCommand;
+
+ /**
+ * This property indicates if heavyweight popup or lightweight popup will be
+ * used to diplay JComboBox's elements.
+ */
+ protected boolean lightWeightPopupEnabled;
+
+ /**
+ * The action taken when new item is selected in the JComboBox
+ */
+ private Action action;
+
+ /**
+ * since 1.4 If this field is set then comboBox's display area for the
+ * selected item will be set by default to this value.
+ */
+ private Object prototypeDisplayValue;
+
+ /**
+ * Constructs JComboBox object with specified data model for it.
+ *
Note that the JComboBox will not change the value that
+ * is preselected by your ComboBoxModel implementation.
+ *
+ * @param model Data model that will be used by this JComboBox to keep track
+ * of its list of items.
+ */
+ public JComboBox(ComboBoxModel model)
+ {
+ setEditable(false);
+ setEnabled(true);
+ setMaximumRowCount(DEFAULT_MAXIMUM_ROW_COUNT);
+ setModel(model);
+ setActionCommand("comboBoxChanged");
+
+ lightWeightPopupEnabled = true;
+ isEditable = false;
+
+ updateUI();
+ }
+
+ /**
+ * Constructs JComboBox with specified list of items.
+ *
+ * @param itemArray array containing list of items for this JComboBox
+ */
+ public JComboBox(Object[] itemArray)
+ {
+ this(new DefaultComboBoxModel(itemArray));
+
+ if (itemArray.length > 0)
+ setSelectedIndex(0);
+ }
+
+ /**
+ * Constructs JComboBox object with specified list of items.
+ *
+ * @param itemVector vector containing list of items for this JComboBox.
+ */
+ public JComboBox(Vector> itemVector)
+ {
+ this(new DefaultComboBoxModel(itemVector));
+
+ if (itemVector.size() > 0)
+ setSelectedIndex(0);
+ }
+
+ /**
+ * Constructor. Creates new empty JComboBox. ComboBox's data model is set to
+ * DefaultComboBoxModel.
+ */
+ public JComboBox()
+ {
+ this(new DefaultComboBoxModel());
+ }
+
+ /**
+ * This method returns true JComboBox is editable and false otherwise
+ *
+ * @return boolean true if JComboBox is editable and false otherwise
+ */
+ public boolean isEditable()
+ {
+ return isEditable;
+ }
+
+ /*
+ * This method adds ancestor listener to this JComboBox.
+ */
+ protected void installAncestorListener()
+ {
+ /* FIXME: Need to implement.
+ *
+ * Need to add ancestor listener to this JComboBox. This listener
+ * should close combo box's popup list of items whenever it
+ * receives an AncestorEvent.
+ */
+ }
+
+ /**
+ * Set the "UI" property of the combo box, which is a look and feel class
+ * responsible for handling comboBox's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(ComboBoxUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this comboBox's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ComboBoxUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String identifier for the UI class to the used
+ * with the JComboBox.
+ *
+ * @return The String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "ComboBoxUI";
+ }
+
+ /**
+ * This method returns the UI used to display the JComboBox.
+ *
+ * @return The UI used to display the JComboBox.
+ */
+ public ComboBoxUI getUI()
+ {
+ return (ComboBoxUI) ui;
+ }
+
+ /**
+ * Set the data model for this JComboBox. This un-registers all listeners
+ * associated with the current model, and re-registers them with the new
+ * model.
+ *
+ * @param newDataModel The new data model for this JComboBox
+ */
+ public void setModel(ComboBoxModel newDataModel)
+ {
+ // dataModel is null if it this method is called from inside the constructors.
+ if (dataModel != null)
+ {
+ // Prevents unneccessary updates.
+ if (dataModel == newDataModel)
+ return;
+
+ // Removes itself (as DataListener) from the to-be-replaced model.
+ dataModel.removeListDataListener(this);
+ }
+
+ /* Adds itself as a DataListener to the new model.
+ * It is intentioned that this operation will fail with a NullPointerException if the
+ * caller delivered a null argument.
+ */
+ newDataModel.addListDataListener(this);
+
+ // Stores old data model for event notification.
+ ComboBoxModel oldDataModel = dataModel;
+ dataModel = newDataModel;
+ selectedItemReminder = newDataModel.getSelectedItem();
+
+ // Notifies the listeners of the model change.
+ firePropertyChange("model", oldDataModel, dataModel);
+ }
+
+ /**
+ * This method returns data model for this comboBox.
+ *
+ * @return ComboBoxModel containing items for this combo box.
+ */
+ public ComboBoxModel getModel()
+ {
+ return dataModel;
+ }
+
+ /**
+ * This method sets JComboBox's popup to be either lightweight or
+ * heavyweight. If 'enabled' is true then lightweight popup is used and
+ * heavyweight otherwise. By default lightweight popup is used to display
+ * this JComboBox's elements.
+ *
+ * @param enabled indicates if lightweight popup or heavyweight popup should
+ * be used to display JComboBox's elements.
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * This method returns whether popup menu that is used to display list of
+ * combo box's item is lightWeight or not.
+ *
+ * @return boolean true if popup menu is lightweight and false otherwise.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * This method sets editability of the combo box. If combo box is editable
+ * the user can choose component from the combo box list by typing
+ * component's name in the editor(JTextfield by default). Otherwise if not
+ * editable, the user should use the list to choose the component. This
+ * method fires PropertyChangeEvents to JComboBox's registered
+ * PropertyChangeListeners to indicate that 'editable' property of the
+ * JComboBox has changed.
+ *
+ * @param editable indicates if the JComboBox's textfield should be editable
+ * or not.
+ */
+ public void setEditable(boolean editable)
+ {
+ if (isEditable != editable)
+ {
+ isEditable = editable;
+ firePropertyChange("editable", !isEditable, isEditable);
+ }
+ }
+
+ /**
+ * Sets number of rows that should be visible in this JComboBox's popup. If
+ * this JComboBox's popup has more elements that maximum number or rows
+ * then popup will have a scroll pane to allow users to view other
+ * elements.
+ *
+ * @param rowCount number of rows that will be visible in JComboBox's popup.
+ */
+ public void setMaximumRowCount(int rowCount)
+ {
+ if (maximumRowCount != rowCount)
+ {
+ int oldMaximumRowCount = maximumRowCount;
+ maximumRowCount = rowCount;
+ firePropertyChange("maximumRowCount", oldMaximumRowCount,
+ maximumRowCount);
+ }
+ }
+
+ /**
+ * This method returns number of rows visible in the JComboBox's list of
+ * items.
+ *
+ * @return int maximun number of visible rows in the JComboBox's list.
+ */
+ public int getMaximumRowCount()
+ {
+ return maximumRowCount;
+ }
+
+ /**
+ * This method sets cell renderer for this JComboBox that will be used to
+ * paint combo box's items. The Renderer should only be used only when
+ * JComboBox is not editable. In the case when JComboBox is editable the
+ * editor must be used. This method also fires PropertyChangeEvent when
+ * cellRendered for this JComboBox has changed.
+ *
+ * @param aRenderer cell renderer that will be used by this JComboBox to
+ * paint its elements.
+ */
+ public void setRenderer(ListCellRenderer aRenderer)
+ {
+ if (renderer != aRenderer)
+ {
+ ListCellRenderer oldRenderer = renderer;
+ renderer = aRenderer;
+ firePropertyChange("renderer", oldRenderer, renderer);
+ }
+ }
+
+ /**
+ * This method returns renderer responsible for rendering selected item in
+ * the combo box
+ *
+ * @return ListCellRenderer
+ */
+ public ListCellRenderer getRenderer()
+ {
+ return renderer;
+ }
+
+ /**
+ * Sets editor for this JComboBox
+ *
+ * @param newEditor ComboBoxEditor for this JComboBox. This method fires
+ * PropertyChangeEvent when 'editor' property is changed.
+ */
+ public void setEditor(ComboBoxEditor newEditor)
+ {
+ if (editor == newEditor)
+ return;
+
+ if (editor != null)
+ editor.removeActionListener(this);
+
+ ComboBoxEditor oldEditor = editor;
+ editor = newEditor;
+
+ if (editor != null)
+ editor.addActionListener(this);
+
+ firePropertyChange("editor", oldEditor, editor);
+ }
+
+ /**
+ * Returns editor component that is responsible for displaying/editing
+ * selected item in the combo box.
+ *
+ * @return ComboBoxEditor
+ */
+ public ComboBoxEditor getEditor()
+ {
+ return editor;
+ }
+
+ /**
+ * Forces combo box to select given item
+ *
+ * @param item element in the combo box to select.
+ */
+ public void setSelectedItem(Object item)
+ {
+ dataModel.setSelectedItem(item);
+ fireActionEvent();
+ }
+
+ /**
+ * Returns currently selected item in the combo box.
+ * The result may be null to indicate that nothing is
+ * currently selected.
+ *
+ * @return element that is currently selected in this combo box.
+ */
+ public Object getSelectedItem()
+ {
+ return dataModel.getSelectedItem();
+ }
+
+ /**
+ * Forces JComboBox to select component located in the given index in the
+ * combo box.
+ *
If the index is below -1 or exceeds the upper bound an
+ * IllegalArgumentException is thrown.
+ *
If the index is -1 then no item gets selected.
+ *
+ * @param index index specifying location of the component that should be
+ * selected.
+ */
+ public void setSelectedIndex(int index)
+ {
+ if (index < -1 || index >= dataModel.getSize())
+ // Fails because index is out of bounds.
+ throw new IllegalArgumentException("illegal index: " + index);
+ else
+ // Selects the item at the given index or clears the selection if the
+ // index value is -1.
+ setSelectedItem((index == -1) ? null : dataModel.getElementAt(index));
+ }
+
+ /**
+ * Returns index of the item that is currently selected in the combo box. If
+ * no item is currently selected, then -1 is returned.
+ *
+ * Note: For performance reasons you should minimize invocation of this
+ * method. If the data model is not an instance of
+ * DefaultComboBoxModel the complexity is O(n) where n is the
+ * number of elements in the combo box.
+ *
+ *
+ * @return int Index specifying location of the currently selected item in the
+ * combo box or -1 if nothing is selected in the combo box.
+ */
+ public int getSelectedIndex()
+ {
+ Object selectedItem = getSelectedItem();
+
+ if (selectedItem != null)
+ {
+ if (dataModel instanceof DefaultComboBoxModel)
+ // Uses special method of DefaultComboBoxModel to retrieve the index.
+ return ((DefaultComboBoxModel) dataModel).getIndexOf(selectedItem);
+ else
+ {
+ // Iterates over all items to retrieve the index.
+ int size = dataModel.getSize();
+
+ for (int i = 0; i < size; i++)
+ {
+ Object o = dataModel.getElementAt(i);
+
+ // XXX: Is special handling of ComparableS neccessary?
+ if ((selectedItem != null) ? selectedItem.equals(o) : o == null)
+ return i;
+ }
+ }
+ }
+
+ // returns that no item is currently selected
+ return -1;
+ }
+
+ /**
+ * Returns an object that is used as the display value when calculating the
+ * preferred size for the combo box. This value is, of course, never
+ * displayed anywhere.
+ *
+ * @return The prototype display value (possibly null).
+ *
+ * @since 1.4
+ * @see #setPrototypeDisplayValue(Object)
+ */
+ public Object getPrototypeDisplayValue()
+ {
+ return prototypeDisplayValue;
+ }
+
+ /**
+ * Sets the object that is assumed to be the displayed item when calculating
+ * the preferred size for the combo box. A {@link PropertyChangeEvent} (with
+ * the name prototypeDisplayValue) is sent to all registered
+ * listeners.
+ *
+ * @param value the new value (null permitted).
+ *
+ * @since 1.4
+ * @see #getPrototypeDisplayValue()
+ */
+ public void setPrototypeDisplayValue(Object value)
+ {
+ Object oldValue = prototypeDisplayValue;
+ prototypeDisplayValue = value;
+ firePropertyChange("prototypeDisplayValue", oldValue, value);
+ }
+
+ /**
+ * This method adds given element to this JComboBox.
+ *
A RuntimeException is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.
+ *
+ * @param element element to add
+ */
+ public void addItem(Object element)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).addElement(element);
+ else
+ throw new RuntimeException("Unable to add the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * Inserts given element at the specified index to this JComboBox.
+ *
A RuntimeException is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.
+ *
+ * @param element element to insert
+ * @param index position where to insert the element
+ */
+ public void insertItemAt(Object element, int index)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).insertElementAt(element, index);
+ else
+ throw new RuntimeException("Unable to insert the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method removes given element from this JComboBox.
+ *
A RuntimeException is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.
+ *
+ * @param element element to remove
+ */
+ public void removeItem(Object element)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).removeElement(element);
+ else
+ throw new RuntimeException("Unable to remove the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method remove element location in the specified index in the
+ * JComboBox.
+ *
A RuntimeException is thrown if the data model is not
+ * an instance of {@link MutableComboBoxModel}.
+ *
+ * @param index index specifying position of the element to remove
+ */
+ public void removeItemAt(int index)
+ {
+ if (dataModel instanceof MutableComboBoxModel)
+ ((MutableComboBoxModel) dataModel).removeElementAt(index);
+ else
+ throw new RuntimeException("Unable to remove the item because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method removes all elements from this JComboBox.
+ *
+ * A RuntimeException is thrown if the data model is not an
+ * instance of {@link MutableComboBoxModel}.
+ *
+ */
+ public void removeAllItems()
+ {
+ if (dataModel instanceof DefaultComboBoxModel)
+ // Uses special method if we have a DefaultComboBoxModel.
+ ((DefaultComboBoxModel) dataModel).removeAllElements();
+ else if (dataModel instanceof MutableComboBoxModel)
+ {
+ // Iterates over all items and removes each.
+ MutableComboBoxModel mcbm = (MutableComboBoxModel) dataModel;
+
+ // We intentionally remove the items backwards to support models which
+ // shift their content to the beginning (e.g. linked lists)
+ for (int i = mcbm.getSize() - 1; i >= 0; i--)
+ mcbm.removeElementAt(i);
+ }
+ else
+ throw new RuntimeException("Unable to remove the items because the data "
+ + "model it is not an instance of "
+ + "MutableComboBoxModel.");
+ }
+
+ /**
+ * This method displays popup with list of combo box's items on the screen
+ */
+ public void showPopup()
+ {
+ setPopupVisible(true);
+ }
+
+ /**
+ * This method hides popup containing list of combo box's items
+ */
+ public void hidePopup()
+ {
+ setPopupVisible(false);
+ }
+
+ /**
+ * This method either displayes or hides the popup containing list of combo
+ * box's items.
+ *
+ * @param visible show popup if 'visible' is true and hide it otherwise
+ */
+ public void setPopupVisible(boolean visible)
+ {
+ getUI().setPopupVisible(this, visible);
+ }
+
+ /**
+ * Checks if popup is currently visible on the screen.
+ *
+ * @return boolean true if popup is visible and false otherwise
+ */
+ public boolean isPopupVisible()
+ {
+ return getUI().isPopupVisible(this);
+ }
+
+ /**
+ * This method sets actionCommand to the specified string. ActionEvent fired
+ * to this JComboBox registered ActionListeners will contain this
+ * actionCommand.
+ *
+ * @param aCommand new action command for the JComboBox's ActionEvent
+ */
+ public void setActionCommand(String aCommand)
+ {
+ actionCommand = aCommand;
+ }
+
+ /**
+ * Returns actionCommand associated with the ActionEvent fired by the
+ * JComboBox to its registered ActionListeners.
+ *
+ * @return String actionCommand for the ActionEvent
+ */
+ public String getActionCommand()
+ {
+ return actionCommand;
+ }
+
+ /**
+ * setAction
+ *
+ * @param a action to set
+ */
+ public void setAction(Action a)
+ {
+ Action old = action;
+ action = a;
+ configurePropertiesFromAction(action);
+ if (action != null)
+ // FIXME: remove from old action and add to new action
+ // PropertyChangeListener to listen to changes in the action
+ addActionListener(action);
+ }
+
+ /**
+ * This method returns Action that is invoked when selected item is changed
+ * in the JComboBox.
+ *
+ * @return Action
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * Configure properties of the JComboBox by reading properties of specified
+ * action. This method always sets the comboBox's "enabled" property to the
+ * value of the Action's "enabled" property.
+ *
+ * @param a An Action to configure the combo box from
+ */
+ protected void configurePropertiesFromAction(Action a)
+ {
+ if (a == null)
+ {
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ else
+ {
+ setEnabled(a.isEnabled());
+ setToolTipText((String) (a.getValue(Action.SHORT_DESCRIPTION)));
+ }
+ }
+
+ /**
+ * Creates PropertyChangeListener to listen for the changes in comboBox's
+ * action properties.
+ *
+ * @param action action to listen to for property changes
+ *
+ * @return a PropertyChangeListener that listens to changes in
+ * action properties.
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ configurePropertiesFromAction(act);
+ }
+ };
+ }
+
+ /**
+ * This method fires ItemEvent to this JComboBox's registered ItemListeners.
+ * This method is invoked when currently selected item in this combo box
+ * has changed.
+ *
+ * @param e the ItemEvent describing the change in the combo box's
+ * selection.
+ */
+ protected void fireItemStateChanged(ItemEvent e)
+ {
+ ItemListener[] ll = getItemListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].itemStateChanged(e);
+ }
+
+ /**
+ * This method fires ActionEvent to this JComboBox's registered
+ * ActionListeners. This method is invoked when user explicitly changes
+ * currently selected item.
+ */
+ protected void fireActionEvent()
+ {
+ ActionListener[] ll = getActionListeners();
+
+ for (int i = 0; i < ll.length; i++)
+ ll[i].actionPerformed(new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+ }
+
+ /**
+ * Fires a popupMenuCanceled() event to all PopupMenuListeners.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuCanceled()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuCanceled(e);
+ }
+
+ /**
+ * Fires a popupMenuWillBecomeInvisible() event to all
+ * PopupMenuListeners.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuWillBecomeInvisible()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuWillBecomeInvisible(e);
+ }
+
+ /**
+ * Fires a popupMenuWillBecomeVisible() event to all
+ * PopupMenuListeners.
+ *
+ * Note: This method is intended for use by plaf classes only.
+ */
+ public void firePopupMenuWillBecomeVisible()
+ {
+ PopupMenuListener[] listeners = getPopupMenuListeners();
+ PopupMenuEvent e = new PopupMenuEvent(this);
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].popupMenuWillBecomeVisible(e);
+ }
+
+ /**
+ * This method is invoked whenever selected item changes in the combo box's
+ * data model. It fires ItemEvent and ActionEvent to all registered
+ * ComboBox's ItemListeners and ActionListeners respectively, indicating
+ * the change.
+ */
+ protected void selectedItemChanged()
+ {
+ // Fire ItemEvent to indicated that previously selected item is now
+ // deselected
+ if (selectedItemReminder != null)
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ selectedItemReminder,
+ ItemEvent.DESELECTED));
+
+ // Fire ItemEvent to indicate that new item is selected
+ Object newSelection = getSelectedItem();
+ if (newSelection != null)
+ fireItemStateChanged(new ItemEvent(this, ItemEvent.ITEM_STATE_CHANGED,
+ newSelection, ItemEvent.SELECTED));
+
+ // Fire Action Event to JComboBox's registered listeners
+ fireActionEvent();
+
+ selectedItemReminder = newSelection;
+ }
+
+ /**
+ * Returns Object array of size 1 containing currently selected element in
+ * the JComboBox.
+ *
+ * @return Object[] Object array of size 1 containing currently selected
+ * element in the JComboBox.
+ */
+ public Object[] getSelectedObjects()
+ {
+ return new Object[] { getSelectedItem() };
+ }
+
+ /**
+ * This method handles actionEvents fired by the ComboBoxEditor. It changes
+ * this JComboBox's selection to the new value currently in the editor and
+ * hides list of combo box items.
+ *
+ * @param e the ActionEvent
+ */
+ public void actionPerformed(ActionEvent e)
+ {
+ setSelectedItem(getEditor().getItem());
+ setPopupVisible(false);
+ }
+
+ /**
+ * This method selects item in this combo box that matches specified
+ * specified keyChar and returns true if such item is found. Otherwise
+ * false is returned.
+ *
+ * @param keyChar character indicating which item in the combo box should be
+ * selected.
+ *
+ * @return boolean true if item corresponding to the specified keyChar
+ * exists in the combo box. Otherwise false is returned.
+ */
+ public boolean selectWithKeyChar(char keyChar)
+ {
+ if (keySelectionManager == null)
+ {
+ keySelectionManager = createDefaultKeySelectionManager();
+ }
+
+ int index = keySelectionManager.selectionForKey(keyChar, getModel());
+ boolean retVal = false;
+ if (index >= 0)
+ {
+ setSelectedIndex(index);
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when some items where added to the JComboBox's data model.
+ *
+ * @param event ListDataEvent describing the change
+ */
+ public void intervalAdded(ListDataEvent event)
+ {
+ // FIXME: Need to implement
+ repaint();
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when some items where removed from the JComboBox's data model.
+ *
+ * @param event ListDataEvent describing the change.
+ */
+ public void intervalRemoved(ListDataEvent event)
+ {
+ // FIXME: Need to implement
+ repaint();
+ }
+
+ /**
+ * The part of implementation of ListDataListener interface. This method is
+ * invoked when contents of the JComboBox's data model changed.
+ *
+ * @param event ListDataEvent describing the change
+ */
+ public void contentsChanged(ListDataEvent event)
+ {
+ // if first and last index of the given ListDataEvent are both -1,
+ // then it indicates that selected item in the combo box data model
+ // have changed.
+ if (event.getIndex0() == -1 && event.getIndex1() == -1)
+ selectedItemChanged();
+ }
+
+ /**
+ * This method disables or enables JComboBox. If the JComboBox is enabled,
+ * then user is able to make item choice, otherwise if JComboBox is
+ * disabled then user is not able to make a selection.
+ *
+ * @param enabled if 'enabled' is true then enable JComboBox and disable it
+ */
+ public void setEnabled(boolean enabled)
+ {
+ boolean oldEnabled = super.isEnabled();
+ if (enabled != oldEnabled)
+ {
+ super.setEnabled(enabled);
+ firePropertyChange("enabled", oldEnabled, enabled);
+ }
+ }
+
+ /**
+ * This method initializes specified ComboBoxEditor to display given item.
+ *
+ * @param anEditor ComboBoxEditor to initialize
+ * @param anItem Item that should displayed in the specified editor
+ */
+ public void configureEditor(ComboBoxEditor anEditor, Object anItem)
+ {
+ anEditor.setItem(anItem);
+ }
+
+ /**
+ * This method is fired whenever a key is pressed with the combo box
+ * in focus
+ *
+ * @param e The KeyEvent indicating which key was pressed.
+ */
+ public void processKeyEvent(KeyEvent e)
+ {
+ if (e.getKeyCode() == KeyEvent.VK_TAB)
+ setPopupVisible(false);
+ else
+ super.processKeyEvent(e);
+ }
+
+ /**
+ * setKeySelectionManager
+ *
+ * @param aManager
+ */
+ public void setKeySelectionManager(KeySelectionManager aManager)
+ {
+ keySelectionManager = aManager;
+ }
+
+ /**
+ * getKeySelectionManager
+ *
+ * @return JComboBox.KeySelectionManager
+ */
+ public KeySelectionManager getKeySelectionManager()
+ {
+ return keySelectionManager;
+ }
+
+ /**
+ * This method returns number of elements in this JComboBox
+ *
+ * @return int number of elements in this JComboBox
+ */
+ public int getItemCount()
+ {
+ return dataModel.getSize();
+ }
+
+ /**
+ * Returns elements located in the combo box at the given index.
+ *
+ * @param index index specifying location of the component to return.
+ *
+ * @return component in the combo box that is located in the given index.
+ */
+ public Object getItemAt(int index)
+ {
+ return dataModel.getElementAt(index);
+ }
+
+ /**
+ * createDefaultKeySelectionManager
+ *
+ * @return KeySelectionManager
+ */
+ protected KeySelectionManager createDefaultKeySelectionManager()
+ {
+ return new DefaultKeySelectionManager();
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JComboBox.
+ *
+ * @return A string describing the attributes of this JComboBox
+ * (never null).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isEditable=").append(isEditable());
+ sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
+ sb.append(",maximumRowCount=").append(getMaximumRowCount());
+
+ sb.append(",selectedItemReminder=");
+ if (selectedItemReminder != null)
+ sb.append(selectedItemReminder);
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JComboBox component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJComboBox}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJComboBox();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This methods adds specified ActionListener to this JComboBox.
+ *
+ * @param listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * This method removes specified ActionListener from this JComboBox.
+ *
+ * @param listener ActionListener
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * This method returns array of ActionListeners that are registered with
+ * this JComboBox.
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * This method registers given ItemListener with this JComboBox
+ *
+ * @param listener to remove
+ */
+ public void addItemListener(ItemListener listener)
+ {
+ listenerList.add(ItemListener.class, listener);
+ }
+
+ /**
+ * This method unregisters given ItemListener from this JComboBox
+ *
+ * @param listener to remove
+ */
+ public void removeItemListener(ItemListener listener)
+ {
+ listenerList.remove(ItemListener.class, listener);
+ }
+
+ /**
+ * This method returns array of ItemListeners that are registered with this
+ * JComboBox.
+ *
+ * @since 1.4
+ */
+ public ItemListener[] getItemListeners()
+ {
+ return (ItemListener[]) getListeners(ItemListener.class);
+ }
+
+ /**
+ * Adds PopupMenuListener to combo box to listen to the events fired by the
+ * combo box's popup menu containing its list of items
+ *
+ * @param listener to add
+ */
+ public void addPopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.add(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Removes PopupMenuListener to combo box to listen to the events fired by
+ * the combo box's popup menu containing its list of items
+ *
+ * @param listener to add
+ */
+ public void removePopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.remove(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Returns array of PopupMenuListeners that are registered with combo box.
+ */
+ public PopupMenuListener[] getPopupMenuListeners()
+ {
+ return (PopupMenuListener[]) getListeners(PopupMenuListener.class);
+ }
+
+ /**
+ * Accessibility support for JComboBox.
+ */
+ protected class AccessibleJComboBox extends AccessibleJComponent
+ implements AccessibleAction, AccessibleSelection
+ {
+ private static final long serialVersionUID = 8217828307256675666L;
+
+ /**
+ * @specnote This constructor was protected in 1.4, but made public
+ * in 1.5.
+ */
+ public AccessibleJComboBox()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of accessible children of this object. The
+ * implementation of AccessibleJComboBox delegates this call to the UI
+ * of the associated JComboBox.
+ *
+ * @return the number of accessible children of this object
+ *
+ * @see ComponentUI#getAccessibleChildrenCount(JComponent)
+ */
+ public int getAccessibleChildrenCount()
+ {
+ ComponentUI ui = getUI();
+ int count;
+ if (ui != null)
+ count = ui.getAccessibleChildrenCount(JComboBox.this);
+ else
+ count = super.getAccessibleChildrenCount();
+ return count;
+ }
+
+ /**
+ * Returns the number of accessible children of this object. The
+ * implementation of AccessibleJComboBox delegates this call to the UI
+ * of the associated JComboBox.
+ *
+ * @param index the index of the accessible child to fetch
+ *
+ * @return the number of accessible children of this object
+ *
+ * @see ComponentUI#getAccessibleChild(JComponent, int)
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ ComponentUI ui = getUI();
+ Accessible child = null;
+ if (ui != null)
+ child = ui.getAccessibleChild(JComboBox.this, index);
+ else
+ child = super.getAccessibleChild(index);
+ return child;
+ }
+
+ /**
+ * Returns the AccessibleSelection object associated with this object.
+ * AccessibleJComboBoxes handle their selection themselves, so this
+ * always returns this.
+ *
+ * @return the AccessibleSelection object associated with this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible selection from this AccssibleJComboBox.
+ *
+ * @param index the index of the selected child to fetch
+ *
+ * @return the accessible selection from this AccssibleJComboBox
+ */
+ public Accessible getAccessibleSelection(int index)
+ {
+ // Get hold of the actual popup.
+ Accessible popup = getUI().getAccessibleChild(JComboBox.this, 0);
+ Accessible selected = null;
+ if (popup != null && popup instanceof ComboPopup)
+ {
+ ComboPopup cPopup = (ComboPopup) popup;
+ // Query the list for the currently selected child.
+ JList l = cPopup.getList();
+ AccessibleContext listCtx = l.getAccessibleContext();
+ if (listCtx != null)
+ {
+ AccessibleSelection s = listCtx.getAccessibleSelection();
+ if (s != null)
+ {
+ selected = s.getAccessibleSelection(index);
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns true if the accessible child with the specified
+ * index is selected, false otherwise.
+ *
+ * @param index the index of the accessible child
+ *
+ * @return true if the accessible child with the specified
+ * index is selected, false otherwise
+ */
+ public boolean isAccessibleChildSelected(int index)
+ {
+ return getSelectedIndex() == index;
+ }
+
+ /**
+ * Returns the accessible role for the JComboBox component.
+ *
+ * @return {@link AccessibleRole#COMBO_BOX}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.COMBO_BOX;
+ }
+
+ /**
+ * Returns the accessible action associated to this accessible object.
+ * AccessibleJComboBox implements its own AccessibleAction, so this
+ * method returns this.
+ *
+ * @return the accessible action associated to this accessible object
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the description of the specified action. AccessibleJComboBox
+ * implements 1 action (toggle the popup menu) and thus returns
+ * UIManager.getString("ComboBox.togglePopupText")
+ *
+ * @param actionIndex the index of the action for which to return the
+ * description
+ *
+ * @return the description of the specified action
+ */
+ public String getAccessibleActionDescription(int actionIndex)
+ {
+ return UIManager.getString("ComboBox.togglePopupText");
+ }
+
+ /**
+ * Returns the number of accessible actions that can be performed by
+ * this object. AccessibleJComboBox implement s one accessible action
+ * (toggle the popup menu), so this method always returns 1.
+ *
+ * @return the number of accessible actions that can be performed by
+ * this object
+ */
+ public int getAccessibleActionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Performs the accessible action with the specified index.
+ * AccessibleJComboBox has 1 accessible action
+ * (actionIndex == 0), which is to toggle the
+ * popup menu. All other action indices have no effect and return
+ * false.
+ *
+ * @param actionIndex the index of the action to perform
+ *
+ * @return true if the action has been performed,
+ * false otherwise
+ */
+ public boolean doAccessibleAction(int actionIndex)
+ {
+ boolean actionPerformed = false;
+ if (actionIndex == 0)
+ {
+ setPopupVisible(! isPopupVisible());
+ actionPerformed = true;
+ }
+ return actionPerformed;
+ }
+
+ /**
+ * Returns the number of selected accessible children of this object. This
+ * returns 1 if the combobox has a selected entry,
+ * 0 otherwise.
+ *
+ * @return the number of selected accessible children of this object
+ */
+ public int getAccessibleSelectionCount()
+ {
+ Object sel = getSelectedItem();
+ int count = 0;
+ if (sel != null)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Sets the current selection to the specified index.
+ *
+ * @param index the index to set as selection
+ */
+ public void addAccessibleSelection(int index)
+ {
+ setSelectedIndex(index);
+ }
+
+ /**
+ * Removes the specified index from the current selection.
+ *
+ * @param index the index to remove from the selection
+ */
+ public void removeAccessibleSelection(int index)
+ {
+ if (getSelectedIndex() == index)
+ clearAccessibleSelection();
+ }
+
+ /**
+ * Clears the current selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ setSelectedIndex(-1);
+ }
+
+ /**
+ * Multiple selection is not supported by AccessibleJComboBox, so this
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ private class DefaultKeySelectionManager
+ implements KeySelectionManager
+ {
+
+ public int selectionForKey(char aKey, ComboBoxModel aModel)
+ {
+ int selectedIndex = getSelectedIndex();
+
+ // Start at currently selected item and iterate to end of list
+ for (int i = selectedIndex + 1; i < aModel.getSize(); i++)
+ {
+ String nextItem = aModel.getElementAt(i).toString();
+
+ if (nextItem.charAt(0) == aKey)
+ return i;
+ }
+
+ // Wrap to start of list if no match yet
+ for (int i = 0; i <= selectedIndex; i++)
+ {
+ String nextItem = aModel.getElementAt(i).toString();
+
+ if (nextItem.charAt(0) == aKey)
+ return i;
+ }
+
+ return - 1;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JComponent.java b/libjava/classpath/javax/swing/JComponent.java
new file mode 100644
index 000000000..365f62aa9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JComponent.java
@@ -0,0 +1,3801 @@
+/* JComponent.java -- Every component in swing inherits from this class.
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.applet.Applet;
+import java.awt.AWTEvent;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.FocusTraversalPolicy;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Window;
+import java.awt.dnd.DropTarget;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ContainerEvent;
+import java.awt.event.ContainerListener;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.awt.peer.LightweightPeer;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyVetoException;
+import java.beans.VetoableChangeListener;
+import java.beans.VetoableChangeSupport;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.EventListener;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedComponent;
+import javax.accessibility.AccessibleKeyBinding;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.border.Border;
+import javax.swing.border.CompoundBorder;
+import javax.swing.border.TitledBorder;
+import javax.swing.event.AncestorEvent;
+import javax.swing.event.AncestorListener;
+import javax.swing.event.EventListenerList;
+import javax.swing.plaf.ComponentUI;
+
+/**
+ * The base class of all Swing components.
+ * It contains generic methods to manage events, properties and sizes. Actual
+ * drawing of the component is channeled to a look-and-feel class that is
+ * implemented elsewhere.
+ *
+ * @author Ronald Veldema (rveldema&064;cs.vu.nl)
+ * @author Graydon Hoare (graydon&064;redhat.com)
+ */
+public abstract class JComponent extends Container implements Serializable
+{
+ private static final long serialVersionUID = -7908749299918704233L;
+
+ /**
+ * The accessible context of this JComponent.
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * Basic accessibility support for JComponent derived
+ * widgets.
+ */
+ public abstract class AccessibleJComponent
+ extends AccessibleAWTContainer
+ implements AccessibleExtendedComponent
+ {
+ /**
+ * Receives notification if the focus on the JComponent changes and
+ * fires appropriate PropertyChangeEvents to listeners registered with
+ * the AccessibleJComponent.
+ */
+ protected class AccessibleFocusHandler
+ implements FocusListener
+ {
+ /**
+ * Creates a new AccessibleFocusHandler.
+ */
+ protected AccessibleFocusHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when the JComponent gained focus and fires
+ * a PropertyChangeEvent to listeners registered on the
+ * AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and a new value
+ * of {@link AccessibleState#FOCUSED}.
+ */
+ public void focusGained(FocusEvent event)
+ {
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_STATE_PROPERTY, null,
+ AccessibleState.FOCUSED);
+ }
+
+ /**
+ * Receives notification when the JComponent lost focus and fires
+ * a PropertyChangeEvent to listeners registered on the
+ * AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_STATE_PROPERTY} and an old value
+ * of {@link AccessibleState#FOCUSED}.
+ */
+ public void focusLost(FocusEvent valevent)
+ {
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.FOCUSED, null);
+ }
+ }
+
+ /**
+ * Receives notification if there are child components are added or removed
+ * from the JComponent and fires appropriate PropertyChangeEvents to
+ * interested listeners on the AccessibleJComponent.
+ */
+ protected class AccessibleContainerHandler
+ implements ContainerListener
+ {
+ /**
+ * Creates a new AccessibleContainerHandler.
+ */
+ protected AccessibleContainerHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when a child component is added to the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
+ *
+ * @param event the container event
+ */
+ public void componentAdded(ContainerEvent event)
+ {
+ Component c = event.getChild();
+ if (c != null && c instanceof Accessible)
+ {
+ AccessibleContext childCtx = c.getAccessibleContext();
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, null, childCtx);
+ }
+ }
+
+ /**
+ * Receives notification when a child component is removed from the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent with a property name of
+ * {@link AccessibleContext#ACCESSIBLE_CHILD_PROPERTY}.
+ *
+ * @param event the container event
+ */
+ public void componentRemoved(ContainerEvent event)
+ {
+ Component c = event.getChild();
+ if (c != null && c instanceof Accessible)
+ {
+ AccessibleContext childCtx = c.getAccessibleContext();
+ AccessibleJComponent.this.firePropertyChange
+ (AccessibleContext.ACCESSIBLE_CHILD_PROPERTY, childCtx, null);
+ }
+ }
+ }
+
+ private static final long serialVersionUID = -7047089700479897799L;
+
+ /**
+ * Receives notification when a child component is added to the
+ * JComponent and fires a PropertyChangeEvent on listeners registered
+ * with the AccessibleJComponent.
+ *
+ * @specnote AccessibleAWTContainer has a protected field with the same
+ * name. Looks like a bug or nasty misdesign to me.
+ */
+ protected ContainerListener accessibleContainerHandler;
+
+ /**
+ * Receives notification if the focus on the JComponent changes and
+ * fires appropriate PropertyChangeEvents to listeners registered with
+ * the AccessibleJComponent.
+ *
+ * @specnote AccessibleAWTComponent has a protected field
+ * accessibleAWTFocusHandler. Looks like a bug or nasty misdesign
+ * to me.
+ */
+ protected FocusListener accessibleFocusHandler;
+
+ /**
+ * Creates a new AccessibleJComponent.
+ */
+ protected AccessibleJComponent()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds a property change listener to the list of registered listeners.
+ *
+ * This sets up the {@link #accessibleContainerHandler} and
+ * {@link #accessibleFocusHandler} fields and calls
+ * super.addPropertyChangeListener(listener).
+ *
+ * @param listener the listener to add
+ */
+ public void addPropertyChangeListener(PropertyChangeListener listener)
+ {
+ // Tests seem to indicate that this method also sets up the other two
+ // handlers.
+ if (accessibleContainerHandler == null)
+ {
+ accessibleContainerHandler = new AccessibleContainerHandler();
+ addContainerListener(accessibleContainerHandler);
+ }
+ if (accessibleFocusHandler == null)
+ {
+ accessibleFocusHandler = new AccessibleFocusHandler();
+ addFocusListener(accessibleFocusHandler);
+ }
+ super.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * Removes a property change listener from the list of registered listeners.
+ *
+ * This uninstalls the {@link #accessibleContainerHandler} and
+ * {@link #accessibleFocusHandler} fields and calls
+ * super.removePropertyChangeListener(listener).
+ *
+ * @param listener the listener to remove
+ */
+ public void removePropertyChangeListener(PropertyChangeListener listener)
+ {
+ // Tests seem to indicate that this method also resets the other two
+ // handlers.
+ if (accessibleContainerHandler != null)
+ {
+ removeContainerListener(accessibleContainerHandler);
+ accessibleContainerHandler = null;
+ }
+ if (accessibleFocusHandler != null)
+ {
+ removeFocusListener(accessibleFocusHandler);
+ accessibleFocusHandler = null;
+ }
+ super.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * Returns the number of accessible children of this object.
+ *
+ * @return the number of accessible children of this object
+ */
+ public int getAccessibleChildrenCount()
+ {
+ // TODO: The functionality should be performed in the superclass.
+ // Find out why this is overridden. However, it is very well possible
+ // that this is left over from times when there was no such superclass
+ // method.
+ return super.getAccessibleChildrenCount();
+ }
+
+ /**
+ * Returns the accessible child component at index i.
+ *
+ * @param i the index of the accessible child to return
+ *
+ * @return the accessible child component at index i
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // TODO: The functionality should be performed in the superclass.
+ // Find out why this is overridden. However, it is very well possible
+ // that this is left over from times when there was no such superclass
+ // method.
+ return super.getAccessibleChild(i);
+ }
+
+ /**
+ * Returns the accessible state set of this component.
+ *
+ * @return the accessible state set of this component
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // Note: While the java.awt.Component has an 'opaque' property, it
+ // seems that it is not added to the accessible state set there, even
+ // if this property is true. However, it is handled for JComponent, so
+ // we add it here.
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ if (isOpaque())
+ state.add(AccessibleState.OPAQUE);
+ return state;
+ }
+
+ /**
+ * Returns the localized name for this object. Generally this should
+ * almost never return {@link Component#getName()} since that is not
+ * a localized name. If the object is some kind of text component (like
+ * a menu item), then the value of the object may be returned. Also, if
+ * the object has a tooltip, the value of the tooltip may also be
+ * appropriate.
+ *
+ * @return the localized name for this object or null if this
+ * object has no name
+ */
+ public String getAccessibleName()
+ {
+ String name = super.getAccessibleName();
+
+ // There are two fallbacks provided by the JComponent in the case the
+ // superclass returns null:
+ // - If the component is inside a titled border, then it inherits the
+ // name from the border title.
+ // - If the component is not inside a titled border but has a label
+ // (via JLabel.setLabelFor()), then it gets the name from the label's
+ // accessible context.
+
+ if (name == null)
+ {
+ name = getTitledBorderText();
+ }
+
+ if (name == null)
+ {
+ Object l = getClientProperty(JLabel.LABEL_PROPERTY);
+ if (l instanceof Accessible)
+ {
+ AccessibleContext labelCtx =
+ ((Accessible) l).getAccessibleContext();
+ name = labelCtx.getAccessibleName();
+ }
+ }
+
+ return name;
+ }
+
+ /**
+ * Returns the localized description of this object.
+ *
+ * @return the localized description of this object or null
+ * if this object has no description
+ */
+ public String getAccessibleDescription()
+ {
+ // There are two fallbacks provided by the JComponent in the case the
+ // superclass returns null:
+ // - If the component has a tooltip, then inherit the description from
+ // the tooltip.
+ // - If the component is not inside a titled border but has a label
+ // (via JLabel.setLabelFor()), then it gets the name from the label's
+ // accessible context.
+ String descr = super.getAccessibleDescription();
+
+ if (descr == null)
+ {
+ descr = getToolTipText();
+ }
+
+ if (descr == null)
+ {
+ Object l = getClientProperty(JLabel.LABEL_PROPERTY);
+ if (l instanceof Accessible)
+ {
+ AccessibleContext labelCtx =
+ ((Accessible) l).getAccessibleContext();
+ descr = labelCtx.getAccessibleName();
+ }
+ }
+
+ return descr;
+ }
+
+ /**
+ * Returns the accessible role of this component.
+ *
+ * @return the accessible role of this component
+ *
+ * @see AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SWING_COMPONENT;
+ }
+
+ /**
+ * Recursivly searches a border hierarchy (starting at border) for
+ * a titled border and returns the title if one is found, null
+ * otherwise.
+ *
+ * @param border the border to start search from
+ *
+ * @return the border title of a possibly found titled border
+ */
+ protected String getBorderTitle(Border border)
+ {
+ String title = null;
+ if (border instanceof CompoundBorder)
+ {
+ CompoundBorder compound = (CompoundBorder) border;
+ Border inner = compound.getInsideBorder();
+ title = getBorderTitle(inner);
+ if (title == null)
+ {
+ Border outer = compound.getOutsideBorder();
+ title = getBorderTitle(outer);
+ }
+ }
+ else if (border instanceof TitledBorder)
+ {
+ TitledBorder titled = (TitledBorder) border;
+ title = titled.getTitle();
+ }
+ return title;
+ }
+
+ /**
+ * Returns the tooltip text for this accessible component.
+ *
+ * @return the tooltip text for this accessible component
+ */
+ public String getToolTipText()
+ {
+ return JComponent.this.getToolTipText();
+ }
+
+ /**
+ * Returns the title of the border of this accessible component if
+ * this component has a titled border, otherwise returns null.
+ *
+ * @return the title of the border of this accessible component if
+ * this component has a titled border, otherwise returns
+ * null
+ */
+ public String getTitledBorderText()
+ {
+ return getBorderTitle(getBorder());
+ }
+
+ /**
+ * Returns the keybindings associated with this accessible component or
+ * null if the component does not support key bindings.
+ *
+ * @return the keybindings associated with this accessible component
+ */
+ public AccessibleKeyBinding getAccessibleKeyBinding()
+ {
+ // The reference implementation seems to always return null here,
+ // independent of the key bindings of the JComponent. So do we.
+ return null;
+ }
+ }
+
+ /**
+ * A value between 0.0 and 1.0 indicating the preferred horizontal
+ * alignment of the component, relative to its siblings. The values
+ * {@link #LEFT_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
+ * #RIGHT_ALIGNMENT} can also be used, as synonyms for 0.0,
+ * 0.5, and 1.0, respectively. Not all layout
+ * managers use this property.
+ *
+ * @see #getAlignmentX
+ * @see #setAlignmentX
+ * @see javax.swing.OverlayLayout
+ * @see javax.swing.BoxLayout
+ */
+ float alignmentX = -1.0F;
+
+ /**
+ * A value between 0.0 and 1.0 indicating the preferred vertical
+ * alignment of the component, relative to its siblings. The values
+ * {@link #TOP_ALIGNMENT}, {@link #CENTER_ALIGNMENT}, and {@link
+ * #BOTTOM_ALIGNMENT} can also be used, as synonyms for 0.0,
+ * 0.5, and 1.0, respectively. Not all layout
+ * managers use this property.
+ *
+ * @see #getAlignmentY
+ * @see #setAlignmentY
+ * @see javax.swing.OverlayLayout
+ * @see javax.swing.BoxLayout
+ */
+ float alignmentY = -1.0F;
+
+ /**
+ * The border painted around this component.
+ *
+ * @see #paintBorder
+ */
+ Border border;
+
+ /**
+ * The popup menu for the component.
+ *
+ * @see #getComponentPopupMenu()
+ * @see #setComponentPopupMenu(JPopupMenu)
+ */
+ JPopupMenu componentPopupMenu;
+
+ /**
+ * A flag that controls whether the {@link #getComponentPopupMenu()} method
+ * looks to the component's parent when the componentPopupMenu
+ * field is null.
+ */
+ boolean inheritsPopupMenu;
+
+ /**
+ *
Whether to double buffer this component when painting. This flag
+ * should generally be true, to ensure good painting
+ * performance.
+ *
+ *
All children of a double buffered component are painted into the
+ * double buffer automatically, so only the top widget in a window needs
+ * to be double buffered.
+ *
+ * @see #setDoubleBuffered
+ * @see #isDoubleBuffered
+ * @see #paint
+ */
+ boolean doubleBuffered = true;
+
+ /**
+ * A set of flags indicating which debugging graphics facilities should
+ * be enabled on this component. The values should be a combination of
+ * {@link DebugGraphics#NONE_OPTION}, {@link DebugGraphics#LOG_OPTION},
+ * {@link DebugGraphics#FLASH_OPTION}, or {@link
+ * DebugGraphics#BUFFERED_OPTION}.
+ *
+ * @see #setDebugGraphicsOptions
+ * @see #getDebugGraphicsOptions
+ * @see DebugGraphics
+ * @see #getComponentGraphics
+ */
+ int debugGraphicsOptions;
+
+ /**
+ *
This property controls two independent behaviors simultaneously.
+ *
+ *
First, it controls whether to fill the background of this widget
+ * when painting its body. This affects calls to {@link
+ * JComponent#paintComponent}, which in turn calls {@link
+ * ComponentUI#update} on the component's {@link #ui} property. If the
+ * component is opaque during this call, the background will be filled
+ * before calling {@link ComponentUI#paint}. This happens merely as a
+ * convenience; you may fill the component's background yourself too,
+ * but there is no need to do so if you will be filling with the same
+ * color.
+ *
+ *
Second, it the opaque property informs swing's repaint system
+ * whether it will be necessary to paint the components "underneath" this
+ * component, in Z-order. If the component is opaque, it is considered to
+ * completely occlude components "underneath" it, so they will not be
+ * repainted along with the opaque component.
+ *
+ *
The default value for this property is false, but most
+ * components will want to set it to true when installing UI
+ * defaults in {@link ComponentUI#installUI}.
+ *
+ * @see #setOpaque
+ * @see #isOpaque
+ * @see #paintComponent
+ */
+ boolean opaque = false;
+
+ /**
+ * The user interface delegate for this component. Event delivery and
+ * repainting of the component are usually delegated to this object.
+ *
+ * @see #setUI
+ * @see #getUIClassID
+ * @see #updateUI
+ */
+ protected ComponentUI ui;
+
+ /**
+ * A hint to the focus system that this component should or should not
+ * get focus. If this is false, swing will not try to
+ * request focus on this component; if true, swing might
+ * try to request focus, but the request might fail. Thus it is only
+ * a hint guiding swing's behavior.
+ *
+ * @see #requestFocus()
+ * @see #isRequestFocusEnabled
+ * @see #setRequestFocusEnabled
+ */
+ boolean requestFocusEnabled;
+
+ /**
+ * Flag indicating behavior of this component when the mouse is dragged
+ * outside the component and the mouse stops moving. If
+ * true, synthetic mouse events will be delivered on regular
+ * timed intervals, continuing off in the direction the mouse exited the
+ * component, until the mouse is released or re-enters the component.
+ *
+ * @see #setAutoscrolls
+ * @see #getAutoscrolls
+ */
+ boolean autoscrolls = false;
+
+ /**
+ * Indicates whether the current paint call is already double buffered or
+ * not.
+ */
+ static boolean paintingDoubleBuffered = false;
+
+ /**
+ * Indicates whether we are calling paintDoubleBuffered() from
+ * paintImmadiately (RepaintManager) or from paint() (AWT refresh).
+ */
+ static boolean isRepainting = false;
+
+ /**
+ * Listeners for events other than {@link PropertyChangeEvent} are
+ * handled by this listener list. PropertyChangeEvents are handled in
+ * {@link #changeSupport}.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * Handles VetoableChangeEvents.
+ */
+ private VetoableChangeSupport vetoableChangeSupport;
+
+ /**
+ * Storage for "client properties", which are key/value pairs associated
+ * with this component by a "client", such as a user application or a
+ * layout manager. This is lazily constructed when the component gets its
+ * first client property.
+ */
+ private Hashtable clientProperties;
+
+ private InputMap inputMap_whenFocused;
+ private InputMap inputMap_whenAncestorOfFocused;
+ private ComponentInputMap inputMap_whenInFocusedWindow;
+ private ActionMap actionMap;
+ /** @since 1.3 */
+ private boolean verifyInputWhenFocusTarget = true;
+ private InputVerifier inputVerifier;
+
+ private TransferHandler transferHandler;
+
+ /**
+ * Indicates if this component is currently painting a tile or not.
+ */
+ private boolean paintingTile;
+
+ /**
+ * A temporary buffer used for fast dragging of components.
+ */
+ private Image dragBuffer;
+
+ /**
+ * Indicates if the dragBuffer is already initialized.
+ */
+ private boolean dragBufferInitialized;
+
+ /**
+ * A cached Rectangle object to be reused. Be careful when you use that,
+ * so that it doesn't get modified in another context within the same
+ * method call chain.
+ */
+ private static transient Rectangle rectCache;
+
+ /**
+ * The default locale of the component.
+ *
+ * @see #getDefaultLocale
+ * @see #setDefaultLocale
+ */
+ private static Locale defaultLocale;
+
+ public static final String TOOL_TIP_TEXT_KEY = "ToolTipText";
+
+ /**
+ * Constant used to indicate that no condition has been assigned to a
+ * particular action.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int UNDEFINED_CONDITION = -1;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_FOCUSED = 0;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component is an ancestor of the component which has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1;
+
+ /**
+ * Constant used to indicate that an action should be performed only when
+ * the component is in the window which has focus.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ */
+ public static final int WHEN_IN_FOCUSED_WINDOW = 2;
+
+
+ /**
+ * Used to optimize painting. This is set in paintImmediately2() to specify
+ * the exact component path to be painted by paintChildren.
+ */
+ Component paintChild;
+
+ /**
+ * Indicates if the opaque property has been set by a client program or by
+ * the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientOpaqueSet = false;
+
+ /**
+ * Indicates if the autoscrolls property has been set by a client program or
+ * by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientAutoscrollsSet = false;
+
+ /**
+ * Creates a new JComponent instance.
+ */
+ public JComponent()
+ {
+ super();
+ setDropTarget(new DropTarget());
+ setLocale(getDefaultLocale());
+ debugGraphicsOptions = DebugGraphics.NONE_OPTION;
+ setRequestFocusEnabled(true);
+ }
+
+ /**
+ * Helper to lazily construct and return the client properties table.
+ *
+ * @return The current client properties table
+ *
+ * @see #clientProperties
+ * @see #getClientProperty
+ * @see #putClientProperty
+ */
+ private Hashtable getClientProperties()
+ {
+ if (clientProperties == null)
+ clientProperties = new Hashtable();
+ return clientProperties;
+ }
+
+ /**
+ * Get a client property associated with this component and a particular
+ * key.
+ *
+ * @param key The key with which to look up the client property
+ *
+ * @return A client property associated with this object and key
+ *
+ * @see #clientProperties
+ * @see #getClientProperties
+ * @see #putClientProperty
+ */
+ public final Object getClientProperty(Object key)
+ {
+ return getClientProperties().get(key);
+ }
+
+ /**
+ * Add a client property value to this component, associated
+ * with key. If there is an existing client property
+ * associated with key, it will be replaced. A
+ * {@link PropertyChangeEvent} is sent to registered listeners (with the
+ * name of the property being key.toString()).
+ *
+ * @param key The key of the client property association to add
+ * @param value The value of the client property association to add
+ *
+ * @see #clientProperties
+ * @see #getClientProperties
+ * @see #getClientProperty
+ */
+ public final void putClientProperty(Object key, Object value)
+ {
+ Hashtable t = getClientProperties();
+ Object old = t.get(key);
+ if (value != null)
+ t.put(key, value);
+ else
+ t.remove(key);
+
+ // When both old and new value are null, no event is fired. This is
+ // different from what firePropertyChange() normally does, so we add this
+ // check here.
+ if (old != null || value != null)
+ firePropertyChange(key.toString(), old, value);
+ }
+
+ /**
+ * Unregister an AncestorListener.
+ *
+ * @param listener The listener to unregister
+ *
+ * @see #addAncestorListener
+ */
+ public void removeAncestorListener(AncestorListener listener)
+ {
+ listenerList.remove(AncestorListener.class, listener);
+ }
+
+ /**
+ * Unregister a VetoableChangeChangeListener.
+ *
+ * @param listener The listener to unregister
+ *
+ * @see #addVetoableChangeListener
+ */
+ public void removeVetoableChangeListener(VetoableChangeListener listener)
+ {
+ if (vetoableChangeSupport != null)
+ vetoableChangeSupport.removeVetoableChangeListener(listener);
+ }
+
+ /**
+ * Register an AncestorListener.
+ *
+ * @param listener The listener to register
+ *
+ * @see #removeVetoableChangeListener
+ */
+ public void addAncestorListener(AncestorListener listener)
+ {
+ listenerList.add(AncestorListener.class, listener);
+ }
+
+ /**
+ * Register a VetoableChangeListener.
+ *
+ * @param listener The listener to register
+ *
+ * @see #removeVetoableChangeListener
+ * @see #listenerList
+ */
+ public void addVetoableChangeListener(VetoableChangeListener listener)
+ {
+ // Lazily instantiate this, it's rarely needed.
+ if (vetoableChangeSupport == null)
+ vetoableChangeSupport = new VetoableChangeSupport(this);
+ vetoableChangeSupport.addVetoableChangeListener(listener);
+ }
+
+ /**
+ * Returns all registered {@link EventListener}s of the given
+ * listenerType.
+ *
+ * @param listenerType the class of listeners to filter (null
+ * not permitted).
+ *
+ * @return An array of registered listeners.
+ *
+ * @throws ClassCastException if listenerType does not implement
+ * the {@link EventListener} interface.
+ * @throws NullPointerException if listenerType is
+ * null.
+ *
+ * @see #getAncestorListeners()
+ * @see #listenerList
+ *
+ * @since 1.3
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ if (listenerType == PropertyChangeListener.class)
+ return (T[]) getPropertyChangeListeners();
+ else if (listenerType == VetoableChangeListener.class)
+ return (T[]) getVetoableChangeListeners();
+ else
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Return all registered AncestorListener objects.
+ *
+ * @return The set of AncestorListener objects in {@link
+ * #listenerList}
+ */
+ public AncestorListener[] getAncestorListeners()
+ {
+ return (AncestorListener[]) getListeners(AncestorListener.class);
+ }
+
+ /**
+ * Return all registered VetoableChangeListener objects.
+ *
+ * @return An array of the VetoableChangeListener objects
+ * registered with this component (possibly empty but never
+ * null).
+ *
+ * @since 1.4
+ */
+ public VetoableChangeListener[] getVetoableChangeListeners()
+ {
+ return vetoableChangeSupport == null ? new VetoableChangeListener[0]
+ : vetoableChangeSupport.getVetoableChangeListeners();
+ }
+
+ /**
+ * Call {@link VetoableChangeListener#vetoableChange} on all listeners
+ * registered to listen to a given property. Any method which changes
+ * the specified property of this component should call this method.
+ *
+ * @param propertyName The property which changed
+ * @param oldValue The old value of the property
+ * @param newValue The new value of the property
+ *
+ * @throws PropertyVetoException if the change was vetoed by a listener
+ *
+ * @see #addVetoableChangeListener
+ * @see #removeVetoableChangeListener
+ */
+ protected void fireVetoableChange(String propertyName, Object oldValue,
+ Object newValue)
+ throws PropertyVetoException
+ {
+ if (vetoableChangeSupport != null)
+ vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
+ }
+
+
+ /**
+ * Fires a property change for a primitive integer property.
+ *
+ * @param property the name of the property
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ *
+ * @specnote This method is implemented in
+ * {@link Component#firePropertyChange(String, int, int)}. It is
+ * only here because it is specified to be public, whereas the
+ * Component method is protected.
+ */
+ public void firePropertyChange(String property, int oldValue, int newValue)
+ {
+ super.firePropertyChange(property, oldValue, newValue);
+ }
+
+ /**
+ * Fires a property change for a primitive boolean property.
+ *
+ * @param property the name of the property
+ * @param oldValue the old value of the property
+ * @param newValue the new value of the property
+ *
+ * @specnote This method is implemented in
+ * {@link Component#firePropertyChange(String, boolean, boolean)}.
+ * It is only here because it is specified to be public, whereas
+ * the Component method is protected.
+ */
+ public void firePropertyChange(String property, boolean oldValue,
+ boolean newValue)
+ {
+ super.firePropertyChange(property, oldValue, newValue);
+ }
+
+ /**
+ * Get the value of the accessibleContext property for this component.
+ *
+ * @return the current value of the property
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return null;
+ }
+
+ /**
+ * Get the value of the {@link #alignmentX} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setAlignmentX
+ * @see #alignmentY
+ */
+ public float getAlignmentX()
+ {
+ float ret = alignmentX;
+ if (alignmentX < 0)
+ // alignment has not been set explicitly.
+ ret = super.getAlignmentX();
+
+ return ret;
+ }
+
+ /**
+ * Get the value of the {@link #alignmentY} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setAlignmentY
+ * @see #alignmentX
+ */
+ public float getAlignmentY()
+ {
+ float ret = alignmentY;
+ if (alignmentY < 0)
+ // alignment has not been set explicitly.
+ ret = super.getAlignmentY();
+
+ return ret;
+ }
+
+ /**
+ * Get the current value of the {@link #autoscrolls} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getAutoscrolls()
+ {
+ return autoscrolls;
+ }
+
+ /**
+ * Set the value of the {@link #border} property.
+ *
+ * @param newBorder The new value of the property
+ *
+ * @see #getBorder
+ */
+ public void setBorder(Border newBorder)
+ {
+ Border oldBorder = getBorder();
+ if (oldBorder == newBorder)
+ return;
+
+ border = newBorder;
+ firePropertyChange("border", oldBorder, newBorder);
+ repaint();
+ }
+
+ /**
+ * Get the value of the {@link #border} property.
+ *
+ * @return The property's current value
+ *
+ * @see #setBorder
+ */
+ public Border getBorder()
+ {
+ return border;
+ }
+
+ /**
+ * Get the component's current bounding box. If a rectangle is provided,
+ * use this as the return value (adjusting its fields in place);
+ * otherwise (of null is provided) return a new {@link
+ * Rectangle}.
+ *
+ * @param rv Optional return value to use
+ *
+ * @return A rectangle bounding the component
+ */
+ public Rectangle getBounds(Rectangle rv)
+ {
+ if (rv == null)
+ return new Rectangle(getX(), getY(), getWidth(), getHeight());
+ else
+ {
+ rv.setBounds(getX(), getY(), getWidth(), getHeight());
+ return rv;
+ }
+ }
+
+ /**
+ * Prepares a graphics context for painting this object. If {@link
+ * #debugGraphicsOptions} is not equal to {@link
+ * DebugGraphics#NONE_OPTION}, produce a new {@link DebugGraphics} object
+ * wrapping the parameter. Otherwise configure the parameter with this
+ * component's foreground color and font.
+ *
+ * @param g The graphics context to wrap or configure
+ *
+ * @return A graphics context to paint this object with
+ *
+ * @see #debugGraphicsOptions
+ * @see #paint
+ */
+ protected Graphics getComponentGraphics(Graphics g)
+ {
+ Graphics g2 = g;
+ int options = getDebugGraphicsOptions();
+ if (options != DebugGraphics.NONE_OPTION)
+ {
+ if (!(g2 instanceof DebugGraphics))
+ g2 = new DebugGraphics(g);
+ DebugGraphics dg = (DebugGraphics) g2;
+ dg.setDebugOptions(dg.getDebugOptions() | options);
+ }
+ g2.setFont(this.getFont());
+ g2.setColor(this.getForeground());
+ return g2;
+ }
+
+ /**
+ * Get the value of the {@link #debugGraphicsOptions} property.
+ *
+ * @return The current value of the property.
+ *
+ * @see #setDebugGraphicsOptions
+ * @see #debugGraphicsOptions
+ */
+ public int getDebugGraphicsOptions()
+ {
+ String option = System.getProperty("gnu.javax.swing.DebugGraphics");
+ int options = debugGraphicsOptions;
+ if (option != null && option.length() != 0)
+ {
+ if (options < 0)
+ options = 0;
+
+ if (option.equals("LOG"))
+ options |= DebugGraphics.LOG_OPTION;
+ else if (option.equals("FLASH"))
+ options |= DebugGraphics.FLASH_OPTION;
+ }
+ return options;
+ }
+
+ /**
+ * Get the component's insets, which are calculated from
+ * the {@link #border} property. If the border is null,
+ * calls {@link Container#getInsets}.
+ *
+ * @return The component's current insets
+ */
+ public Insets getInsets()
+ {
+ if (border == null)
+ return super.getInsets();
+ return getBorder().getBorderInsets(this);
+ }
+
+ /**
+ * Get the component's insets, which are calculated from the {@link
+ * #border} property. If the border is null, calls {@link
+ * Container#getInsets}. The passed-in {@link Insets} value will be
+ * used as the return value, if possible.
+ *
+ * @param insets Return value object to reuse, if possible
+ *
+ * @return The component's current insets
+ */
+ public Insets getInsets(Insets insets)
+ {
+ Insets t = getInsets();
+
+ if (insets == null)
+ return t;
+
+ insets.left = t.left;
+ insets.right = t.right;
+ insets.top = t.top;
+ insets.bottom = t.bottom;
+ return insets;
+ }
+
+ /**
+ * Get the component's location. The passed-in {@link Point} value
+ * will be used as the return value, if possible.
+ *
+ * @param rv Return value object to reuse, if possible
+ *
+ * @return The component's current location
+ */
+ public Point getLocation(Point rv)
+ {
+ if (rv == null)
+ return new Point(getX(), getY());
+
+ rv.setLocation(getX(), getY());
+ return rv;
+ }
+
+ /**
+ * Get the component's maximum size. If the maximumSize property
+ * has been explicitly set, it is returned. If the maximumSize
+ * property has not been set but the {@link #ui} property has been, the
+ * result of {@link ComponentUI#getMaximumSize} is returned. If neither
+ * property has been set, the result of {@link Container#getMaximumSize}
+ * is returned.
+ *
+ * @return the maximum size of the component
+ *
+ * @see Component#setMaximumSize
+ * @see Component#getMaximumSize()
+ * @see Component#isMaximumSizeSet()
+ * @see ComponentUI#getMaximumSize(JComponent)
+ */
+ public Dimension getMaximumSize()
+ {
+ Dimension size = null;
+ if (isMaximumSizeSet())
+ size = super.getMaximumSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getMaximumSize(this);
+ if (size == null)
+ size = super.getMaximumSize();
+ }
+ return size;
+ }
+
+ /**
+ * Get the component's minimum size. If the minimumSize property
+ * has been explicitly set, it is returned. If the minimumSize
+ * property has not been set but the {@link #ui} property has been, the
+ * result of {@link ComponentUI#getMinimumSize} is returned. If neither
+ * property has been set, the result of {@link Container#getMinimumSize}
+ * is returned.
+ *
+ * @return The minimum size of the component
+ *
+ * @see Component#setMinimumSize
+ * @see Component#getMinimumSize()
+ * @see Component#isMinimumSizeSet()
+ * @see ComponentUI#getMinimumSize(JComponent)
+ */
+ public Dimension getMinimumSize()
+ {
+ Dimension size = null;
+ if (isMinimumSizeSet())
+ size = super.getMinimumSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getMinimumSize(this);
+ if (size == null)
+ size = super.getMinimumSize();
+ }
+ return size;
+ }
+
+ /**
+ * Get the component's preferred size. If the preferredSize
+ * property has been explicitly set, it is returned. If the
+ * preferredSize property has not been set but the {@link #ui}
+ * property has been, the result of {@link ComponentUI#getPreferredSize} is
+ * returned. If neither property has been set, the result of {@link
+ * Container#getPreferredSize} is returned.
+ *
+ * @return The preferred size of the component
+ *
+ * @see Component#setPreferredSize
+ * @see Component#getPreferredSize()
+ * @see Component#isPreferredSizeSet()
+ * @see ComponentUI#getPreferredSize(JComponent)
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = null;
+ if (isPreferredSizeSet())
+ size = super.getPreferredSize();
+ else
+ {
+ if (ui != null)
+ size = ui.getPreferredSize(this);
+ if (size == null)
+ size = super.getPreferredSize();
+ }
+ return size;
+ }
+
+ /**
+ * Return the value of the nextFocusableComponent property.
+ *
+ * @return The current value of the property, or null
+ * if none has been set.
+ *
+ * @deprecated See {@link java.awt.FocusTraversalPolicy}
+ */
+ public Component getNextFocusableComponent()
+ {
+ Container focusRoot = this;
+ if (! this.isFocusCycleRoot())
+ focusRoot = getFocusCycleRootAncestor();
+
+ FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
+ return policy.getComponentAfter(focusRoot, this);
+ }
+
+ /**
+ * Return the set of {@link KeyStroke} objects which are registered
+ * to initiate actions on this component.
+ *
+ * @return An array of the registered keystrokes (possibly empty but never
+ * null).
+ */
+ public KeyStroke[] getRegisteredKeyStrokes()
+ {
+ KeyStroke[] ks0;
+ KeyStroke[] ks1;
+ KeyStroke[] ks2;
+ if (inputMap_whenFocused != null)
+ ks0 = inputMap_whenFocused.keys();
+ else
+ ks0 = new KeyStroke[0];
+ if (inputMap_whenAncestorOfFocused != null)
+ ks1 = inputMap_whenAncestorOfFocused.keys();
+ else
+ ks1 = new KeyStroke[0];
+ if (inputMap_whenInFocusedWindow != null)
+ ks2 = inputMap_whenInFocusedWindow.keys();
+ else
+ ks2 = new KeyStroke[0];
+ int count = ks0.length + ks1.length + ks2.length;
+ KeyStroke[] result = new KeyStroke[count];
+ System.arraycopy(ks0, 0, result, 0, ks0.length);
+ System.arraycopy(ks1, 0, result, ks0.length, ks1.length);
+ System.arraycopy(ks2, 0, result, ks0.length + ks1.length, ks2.length);
+ return result;
+ }
+
+ /**
+ * Returns the first ancestor of this component which is a {@link JRootPane}.
+ * Equivalent to calling SwingUtilities.getRootPane(this);.
+ *
+ * @return An ancestral JRootPane, or null if none exists.
+ */
+ public JRootPane getRootPane()
+ {
+ JRootPane p = SwingUtilities.getRootPane(this);
+ return p;
+ }
+
+ /**
+ * Get the component's size. The passed-in {@link Dimension} value
+ * will be used as the return value, if possible.
+ *
+ * @param rv Return value object to reuse, if possible
+ *
+ * @return The component's current size
+ */
+ public Dimension getSize(Dimension rv)
+ {
+ if (rv == null)
+ return new Dimension(getWidth(), getHeight());
+ else
+ {
+ rv.setSize(getWidth(), getHeight());
+ return rv;
+ }
+ }
+
+ /**
+ * Return the toolTip property of this component, creating it and
+ * setting it if it is currently null. This method can be
+ * overridden in subclasses which wish to control the exact form of
+ * tooltip created.
+ *
+ * @return The current toolTip
+ */
+ public JToolTip createToolTip()
+ {
+ JToolTip toolTip = new JToolTip();
+ toolTip.setComponent(this);
+ return toolTip;
+ }
+
+ /**
+ * Return the location at which the toolTipText property should
+ * be displayed, when triggered by a particular mouse event.
+ *
+ * @param event The event the tooltip is being presented in response to
+ *
+ * @return The point at which to display a tooltip, or null
+ * if swing is to choose a default location.
+ */
+ public Point getToolTipLocation(MouseEvent event)
+ {
+ return null;
+ }
+
+ /**
+ * Set the tooltip text for this component. If a non-null
+ * value is set, this component is registered in the
+ * ToolTipManager in order to turn on tooltips for this
+ * component. If a null value is set, tooltips are turne off
+ * for this component.
+ *
+ * @param text the tooltip text for this component
+ *
+ * @see #getToolTipText()
+ * @see #getToolTipText(MouseEvent)
+ */
+ public void setToolTipText(String text)
+ {
+ String old = getToolTipText();
+ putClientProperty(TOOL_TIP_TEXT_KEY, text);
+ ToolTipManager ttm = ToolTipManager.sharedInstance();
+ if (text == null)
+ ttm.unregisterComponent(this);
+ else if (old == null)
+ ttm.registerComponent(this);
+ }
+
+ /**
+ * Returns the current tooltip text for this component, or null
+ * if none has been set.
+ *
+ * @return the current tooltip text for this component, or null
+ * if none has been set
+ *
+ * @see #setToolTipText
+ * @see #getToolTipText(MouseEvent)
+ */
+ public String getToolTipText()
+ {
+ return (String) getClientProperty(TOOL_TIP_TEXT_KEY);
+ }
+
+ /**
+ * Returns the tooltip text for this component for a particular mouse
+ * event. This can be used to support context sensitive tooltips that can
+ * change with the mouse location. By default this returns the static
+ * tooltip text returned by {@link #getToolTipText()}.
+ *
+ * @param event the mouse event which triggered the tooltip
+ *
+ * @return the tooltip text for this component for a particular mouse
+ * event
+ *
+ * @see #setToolTipText
+ * @see #getToolTipText()
+ */
+ public String getToolTipText(MouseEvent event)
+ {
+ return getToolTipText();
+ }
+
+ /**
+ * Returns the flag that controls whether or not the component inherits its
+ * parent's popup menu when no popup menu is specified for this component.
+ *
+ * @return A boolean.
+ *
+ * @since 1.5
+ *
+ * @see #setInheritsPopupMenu(boolean)
+ */
+ public boolean getInheritsPopupMenu()
+ {
+ return inheritsPopupMenu;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the component inherits its
+ * parent's popup menu when no popup menu is specified for this component.
+ * This is a bound property with the property name 'inheritsPopupMenu'.
+ *
+ * @param inherit the new flag value.
+ *
+ * @since 1.5
+ *
+ * @see #getInheritsPopupMenu()
+ */
+ public void setInheritsPopupMenu(boolean inherit)
+ {
+ if (inheritsPopupMenu != inherit)
+ {
+ inheritsPopupMenu = inherit;
+ this.firePropertyChange("inheritsPopupMenu", ! inherit, inherit);
+ }
+ }
+
+ /**
+ * Returns the popup menu for this component. If the popup menu is
+ * null AND the {@link #getInheritsPopupMenu()} method returns
+ * true, this method will return the parent's popup menu (if it
+ * has one).
+ *
+ * @return The popup menu (possibly null.
+ *
+ * @since 1.5
+ *
+ * @see #setComponentPopupMenu(JPopupMenu)
+ * @see #getInheritsPopupMenu()
+ */
+ public JPopupMenu getComponentPopupMenu()
+ {
+ if (componentPopupMenu == null && getInheritsPopupMenu())
+ {
+ Container parent = getParent();
+ if (parent instanceof JComponent)
+ return ((JComponent) parent).getComponentPopupMenu();
+ else
+ return null;
+ }
+ else
+ return componentPopupMenu;
+ }
+
+ /**
+ * Sets the popup menu for this component (this is a bound property with
+ * the property name 'componentPopupMenu').
+ *
+ * @param popup the popup menu (null permitted).
+ *
+ * @since 1.5
+ *
+ * @see #getComponentPopupMenu()
+ */
+ public void setComponentPopupMenu(JPopupMenu popup)
+ {
+ if (componentPopupMenu != popup)
+ {
+ JPopupMenu old = componentPopupMenu;
+ componentPopupMenu = popup;
+ firePropertyChange("componentPopupMenu", old, popup);
+ }
+ }
+
+ /**
+ * Return the top level ancestral container (usually a {@link
+ * java.awt.Window} or {@link java.applet.Applet}) which this component is
+ * contained within, or null if no ancestors exist.
+ *
+ * @return The top level container, if it exists
+ */
+ public Container getTopLevelAncestor()
+ {
+ Container c = getParent();
+ for (Container peek = c; peek != null; peek = peek.getParent())
+ c = peek;
+ return c;
+ }
+
+ /**
+ * Compute the component's visible rectangle, which is defined
+ * recursively as either the component's bounds, if it has no parent, or
+ * the intersection of the component's bounds with the visible rectangle
+ * of its parent.
+ *
+ * @param rect The return value slot to place the visible rectangle in
+ */
+ public void computeVisibleRect(Rectangle rect)
+ {
+ Component c = getParent();
+ if (c != null && c instanceof JComponent)
+ {
+ ((JComponent) c).computeVisibleRect(rect);
+ rect.translate(-getX(), -getY());
+ rect = SwingUtilities.computeIntersection(0, 0, getWidth(),
+ getHeight(), rect);
+ }
+ else
+ rect.setRect(0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Return the component's visible rectangle in a new {@link Rectangle},
+ * rather than via a return slot.
+ *
+ * @return the component's visible rectangle
+ *
+ * @see #computeVisibleRect(Rectangle)
+ */
+ public Rectangle getVisibleRect()
+ {
+ Rectangle r = new Rectangle();
+ computeVisibleRect(r);
+ return r;
+ }
+
+ /**
+ *
Requests that this component receive input focus, giving window
+ * focus to the top level ancestor of this component. Only works on
+ * displayable, focusable, visible components.
+ *
+ *
This method should not be called by clients; it is intended for
+ * focus implementations. Use {@link Component#requestFocus()} instead.
+ *
+ * @see Component#requestFocus()
+ */
+ public void grabFocus()
+ {
+ requestFocus();
+ }
+
+ /**
+ * Get the value of the {@link #doubleBuffered} property.
+ *
+ * @return The property's current value
+ */
+ public boolean isDoubleBuffered()
+ {
+ return doubleBuffered;
+ }
+
+ /**
+ * Return true if the provided component has no native peer;
+ * in other words, if it is a "lightweight component".
+ *
+ * @param c The component to test for lightweight-ness
+ *
+ * @return Whether or not the component is lightweight
+ */
+ public static boolean isLightweightComponent(Component c)
+ {
+ return c.getPeer() instanceof LightweightPeer;
+ }
+
+ /**
+ * Return true if you wish this component to manage its own
+ * focus. In particular: if you want this component to be sent
+ * TAB and SHIFT+TAB key events, and to not
+ * have its children considered as focus transfer targets. If
+ * true, focus traversal around this component changes to
+ * CTRL+TAB and CTRL+SHIFT+TAB.
+ *
+ * @return true if you want this component to manage its own
+ * focus, otherwise (by default) false
+ *
+ * @deprecated 1.4 Use {@link Component#setFocusTraversalKeys(int, Set)} and
+ * {@link Container#setFocusCycleRoot(boolean)} instead
+ */
+ public boolean isManagingFocus()
+ {
+ return false;
+ }
+
+ /**
+ * Return the current value of the {@link #opaque} property.
+ *
+ * @return The current property value
+ */
+ public boolean isOpaque()
+ {
+ return opaque;
+ }
+
+ /**
+ * Return true if the component can guarantee that none of its
+ * children will overlap in Z-order. This is a hint to the painting system.
+ * The default is to return true, but some components such as
+ * {@link JLayeredPane} should override this to return false.
+ *
+ * @return Whether the component tiles its children
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ return true;
+ }
+
+ /**
+ * Return true if this component is currently painting a tile,
+ * this means that paint() is called again on another child component. This
+ * method returns false if this component does not paint a tile
+ * or if the last tile is currently painted.
+ *
+ * @return whether the component is painting a tile
+ */
+ public boolean isPaintingTile()
+ {
+ return paintingTile;
+ }
+
+ /**
+ * Get the value of the {@link #requestFocusEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean isRequestFocusEnabled()
+ {
+ return requestFocusEnabled;
+ }
+
+ /**
+ * Return true if this component is a validation root; this
+ * will cause calls to {@link #invalidate()} in this component's children
+ * to be "captured" at this component, and not propagate to its parents.
+ * For most components this should return false, but some
+ * components such as {@link JViewport} will want to return
+ * true.
+ *
+ * @return Whether this component is a validation root
+ */
+ public boolean isValidateRoot()
+ {
+ return false;
+ }
+
+ /**
+ *
Paint the component. This is a delicate process, and should only be
+ * called from the repaint thread, under control of the {@link
+ * RepaintManager}. Client code should usually call {@link #repaint()} to
+ * trigger painting.
+ *
+ *
The body of the paint call involves calling {@link
+ * #paintComponent}, {@link #paintBorder}, and {@link #paintChildren} in
+ * order. If you want to customize painting behavior, you should override
+ * one of these methods rather than paint.
+ *
+ * @param g The graphics context to paint with
+ *
+ * @see #paintImmediately(Rectangle)
+ */
+ public void paint(Graphics g)
+ {
+ RepaintManager rm = RepaintManager.currentManager(this);
+ // We do a little stunt act here to switch on double buffering if it's
+ // not already on. If we are not already doublebuffered, then we jump
+ // into the method paintDoubleBuffered, which turns on the double buffer
+ // and then calls paint(g) again. In the second call we go into the else
+ // branch of this if statement and actually paint things to the double
+ // buffer. When this method completes, the call stack unwinds back to
+ // paintDoubleBuffered, where the buffer contents is finally drawn to the
+ // screen.
+ if (!paintingDoubleBuffered && isDoubleBuffered()
+ && rm.isDoubleBufferingEnabled())
+ {
+ Rectangle clip = g.getClipBounds();
+ paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height);
+ }
+ else
+ {
+ if (getClientProperty("bufferedDragging") != null
+ && dragBuffer == null)
+ {
+ initializeDragBuffer();
+ }
+ else if (getClientProperty("bufferedDragging") == null
+ && dragBuffer != null)
+ {
+ dragBuffer = null;
+ }
+
+ Rectangle clip = g.getClipBounds();
+ int clipX, clipY, clipW, clipH;
+ if (clip == null)
+ {
+ clipX = 0;
+ clipY = 0;
+ clipW = getWidth();
+ clipH = getHeight();
+ }
+ else
+ {
+ clipX = clip.x;
+ clipY = clip.y;
+ clipW = clip.width;
+ clipH = clip.height;
+ }
+ if (dragBuffer != null && dragBufferInitialized)
+ {
+ g.drawImage(dragBuffer, 0, 0, this);
+ }
+ else
+ {
+ Graphics g2 = getComponentGraphics(g);
+ if (! isOccupiedByChild(clipX, clipY, clipW, clipH))
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ }
+ paintChildren(g2);
+ }
+ }
+ }
+
+ /**
+ * Determines if a region of this component is completely occupied by
+ * an opaque child component, in which case we don't need to bother
+ * painting this component at all.
+ *
+ * @param x the area, x coordinate
+ * @param y the area, y coordinate
+ * @param w the area, width
+ * @param h the area, height
+ *
+ * @return true if the specified area is completely covered
+ * by a child component, false otherwise
+ */
+ private boolean isOccupiedByChild(int x, int y, int w, int h)
+ {
+ boolean occupied = false;
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component child = getComponent(i);
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy
+ && y + h <= cy + ch)
+ {
+ occupied = child.isOpaque();
+ break;
+ }
+ }
+ return occupied;
+ }
+
+ /**
+ * Initializes the drag buffer by creating a new image and painting this
+ * component into it.
+ */
+ private void initializeDragBuffer()
+ {
+ dragBufferInitialized = false;
+ // Allocate new dragBuffer if the current one is too small.
+ if (dragBuffer == null || dragBuffer.getWidth(this) < getWidth()
+ || dragBuffer.getHeight(this) < getHeight())
+ {
+ dragBuffer = createImage(getWidth(), getHeight());
+ }
+ Graphics g = dragBuffer.getGraphics();
+ paint(g);
+ g.dispose();
+ dragBufferInitialized = true;
+ }
+
+ /**
+ * Paint the component's border. This usually means calling {@link
+ * Border#paintBorder} on the {@link #border} property, if it is
+ * non-null. You may override this if you wish to customize
+ * border painting behavior. The border is painted after the component's
+ * body, but before the component's children.
+ *
+ * @param g The graphics context with which to paint the border
+ *
+ * @see #paint
+ * @see #paintChildren
+ * @see #paintComponent
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (getBorder() != null)
+ getBorder().paintBorder(this, g, 0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Paint the component's children. This usually means calling {@link
+ * Container#paint}, which recursively calls {@link #paint} on any of the
+ * component's children, with appropriate changes to coordinate space and
+ * clipping region. You may override this if you wish to customize
+ * children painting behavior. The children are painted after the
+ * component's body and border.
+ *
+ * @param g The graphics context with which to paint the children
+ *
+ * @see #paint
+ * @see #paintBorder
+ * @see #paintComponent
+ */
+ protected void paintChildren(Graphics g)
+ {
+ if (getComponentCount() > 0)
+ {
+ // Need to lock the tree to avoid problems with AWT and concurrency.
+ synchronized (getTreeLock())
+ {
+ // Fast forward to the child to paint, if set by
+ // paintImmediately2()
+ int i = getComponentCount() - 1;
+ if (paintChild != null && paintChild.isOpaque())
+ {
+ for (; i >= 0 && getComponent(i) != paintChild; i--)
+ ;
+ }
+ for (; i >= 0; i--)
+ {
+ Component child = getComponent(i);
+ if (child != null && child.isLightweight()
+ && child.isVisible())
+ {
+ int cx = child.getX();
+ int cy = child.getY();
+ int cw = child.getWidth();
+ int ch = child.getHeight();
+ if (g.hitClip(cx, cy, cw, ch))
+ {
+ if ((! isOptimizedDrawingEnabled()) && i > 0)
+ {
+ // Check if the child is completely obscured.
+ Rectangle clip = g.getClipBounds(); // A copy.
+ SwingUtilities.computeIntersection(cx, cy, cw, ch,
+ clip);
+ if (isCompletelyObscured(i, clip.x, clip.y,
+ clip.width, clip.height))
+ continue; // Continues the for-loop.
+ }
+ Graphics cg = g.create(cx, cy, cw, ch);
+ cg.setColor(child.getForeground());
+ cg.setFont(child.getFont());
+ try
+ {
+ child.paint(cg);
+ }
+ finally
+ {
+ cg.dispose();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Determines if a region of a child component is completely obscured by one
+ * of its siblings.
+ *
+ * @param index the index of the child component
+ * @param x the region to check, x coordinate
+ * @param y the region to check, y coordinate
+ * @param w the region to check, width
+ * @param h the region to check, height
+ *
+ * @return true if the region is completely obscured by a
+ * sibling, false otherwise
+ */
+ private boolean isCompletelyObscured(int index, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int i = index - 1; i >= 0 && obscured == false; i--)
+ {
+ Component sib = getComponent(i);
+ if (sib.isVisible())
+ {
+ Rectangle sibRect = sib.getBounds(rectCache);
+ if (sib.isOpaque() && x >= sibRect.x
+ && (x + w) <= (sibRect.x + sibRect.width)
+ && y >= sibRect.y
+ && (y + h) <= (sibRect.y + sibRect.height))
+ {
+ obscured = true;
+ }
+ }
+ }
+ return obscured;
+ }
+
+ /**
+ * Checks if a component/rectangle is partially obscured by one of its
+ * siblings.
+ * Note that this doesn't check for completely obscured, this is
+ * done by isCompletelyObscured() and should probably also be checked.
+ *
+ * @param i the component index from which to start searching
+ * @param x the x coordinate of the rectangle to check
+ * @param y the y coordinate of the rectangle to check
+ * @param w the width of the rectangle to check
+ * @param h the height of the rectangle to check
+ *
+ * @return true if the rectangle is partially obscured
+ */
+ private boolean isPartiallyObscured(int i, int x, int y, int w, int h)
+ {
+ boolean obscured = false;
+ for (int j = i - 1; j >= 0 && ! obscured; j--)
+ {
+ Component sibl = getComponent(j);
+ if (sibl.isVisible())
+ {
+ Rectangle rect = sibl.getBounds(rectCache);
+ if (!(x + w <= rect.x)
+ || (y + h <= rect.y)
+ || (x >= rect.x + rect.width)
+ || (y >= rect.y + rect.height))
+ obscured = true;
+ }
+ }
+ return obscured;
+ }
+
+ /**
+ * Paint the component's body. This usually means calling {@link
+ * ComponentUI#update} on the {@link #ui} property of the component, if
+ * it is non-null. You may override this if you wish to
+ * customize the component's body-painting behavior. The component's body
+ * is painted first, before the border and children.
+ *
+ * @param g The graphics context with which to paint the body
+ *
+ * @see #paint
+ * @see #paintBorder
+ * @see #paintChildren
+ */
+ protected void paintComponent(Graphics g)
+ {
+ if (ui != null)
+ {
+ Graphics g2 = g.create();
+ try
+ {
+ ui.update(g2, this);
+ }
+ finally
+ {
+ g2.dispose();
+ }
+ }
+ }
+
+ /**
+ * A variant of {@link #paintImmediately(Rectangle)} which takes
+ * integer parameters.
+ *
+ * @param x The left x coordinate of the dirty region
+ * @param y The top y coordinate of the dirty region
+ * @param w The width of the dirty region
+ * @param h The height of the dirty region
+ */
+ public void paintImmediately(int x, int y, int w, int h)
+ {
+ // Find opaque parent and call paintImmediately2() on it.
+ if (isShowing())
+ {
+ Component c = this;
+ Component p;
+ while (c != null && ! c.isOpaque())
+ {
+ p = c.getParent();
+ if (p != null)
+ {
+ x += c.getX();
+ y += c.getY();
+ c = p;
+ }
+ }
+ if (c instanceof JComponent)
+ ((JComponent) c).paintImmediately2(x, y, w, h);
+ else
+ c.repaint(x, y, w, h);
+ }
+ }
+
+ /**
+ * Transform the provided dirty rectangle for this component into the
+ * appropriate ancestral {@link JRootPane} and call {@link #paint} on
+ * that root pane. This method is called from the {@link RepaintManager}
+ * and should always be called within the painting thread.
+ *
+ *
This method will acquire a double buffer from the {@link
+ * RepaintManager} if the component's {@link #doubleBuffered} property is
+ * true and the paint call is the
+ * first recursive paint call inside swing.
+ *
+ *
The method will also modify the provided {@link Graphics} context
+ * via the {@link #getComponentGraphics} method. If you want to customize
+ * the graphics object used for painting, you should override that method
+ * rather than paint.
+ *
+ * @param r The dirty rectangle to paint
+ */
+ public void paintImmediately(Rectangle r)
+ {
+ paintImmediately(r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * Performs the actual work of paintImmediatly on the repaint root.
+ *
+ * @param x the area to be repainted, X coordinate
+ * @param y the area to be repainted, Y coordinate
+ */
+ void paintImmediately2(int x, int y, int w, int h)
+ {
+ // Optimization for components that are always painted on top.
+ boolean onTop = onTop() && isOpaque();
+
+ // Fetch the RepaintManager.
+ RepaintManager rm = RepaintManager.currentManager(this);
+
+ // The painting clip;
+ int paintX = x;
+ int paintY = y;
+ int paintW = w;
+ int paintH = h;
+
+ // If we should paint buffered or not.
+ boolean haveBuffer = false;
+
+ // The component that is finally triggered for painting.
+ JComponent paintRoot = this;
+
+ // Stores the component and all its parents. This will be used to limit
+ // the actually painted components in paintChildren by setting
+ // the field paintChild.
+ int pIndex = -1;
+ int pCount = 0;
+ ArrayList components = new ArrayList();
+
+ // Offset to subtract from the paintRoot rectangle when painting.
+ int offsX = 0;
+ int offsY = 0;
+
+ // The current component and its child.
+ Component child;
+ Container c;
+
+ // Find appropriate paint root.
+ for (c = this, child = null;
+ c != null && ! (c instanceof Window) && ! (c instanceof Applet);
+ child = c, c = c.getParent())
+ {
+ JComponent jc = c instanceof JComponent ? (JComponent) c : null;
+ components.add(c);
+ if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled())
+ {
+ // Indicates whether we reset the paint root to be the current
+ // component.
+ boolean updatePaintRoot = false;
+
+ // Check obscured state of the child.
+ // Generally, we have 3 cases here:
+ // 1. Not obscured. No need to paint from the parent.
+ // 2. Partially obscured. Paint from the parent.
+ // 3. Completely obscured. No need to paint anything.
+ if (c != this)
+ {
+ if (jc.isPaintRoot())
+ updatePaintRoot = true;
+ else
+ {
+ int count = c.getComponentCount();
+ int i = 0;
+ for (; i < count && c.getComponent(i) != child; i++)
+ ;
+
+ if (jc.isCompletelyObscured(i, paintX, paintY, paintW,
+ paintH))
+ return; // No need to paint anything.
+ else if (jc.isPartiallyObscured(i, paintX, paintY, paintW,
+ paintH))
+ updatePaintRoot = true;
+
+ }
+ }
+ if (updatePaintRoot)
+ {
+ // Paint from parent.
+ paintRoot = jc;
+ pIndex = pCount;
+ offsX = 0;
+ offsY = 0;
+ haveBuffer = false;
+ }
+ }
+ pCount++;
+ // Check if component is double buffered.
+ if (rm.isDoubleBufferingEnabled() && jc != null
+ && jc.isDoubleBuffered())
+ {
+ haveBuffer = true;
+ }
+
+ // Clip the paint region with the parent.
+ if (! onTop)
+ {
+ paintX = Math.max(0, paintX);
+ paintY = Math.max(0, paintY);
+ paintW = Math.min(c.getWidth(), paintW + paintX) - paintX;
+ paintH = Math.min(c.getHeight(), paintH + paintY) - paintY;
+ int dx = c.getX();
+ int dy = c.getY();
+ paintX += dx;
+ paintY += dy;
+ offsX += dx;
+ offsY += dy;
+ }
+ }
+ if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0)
+ {
+ isRepainting = true;
+ paintX -= offsX;
+ paintY -= offsY;
+
+ // Set the painting path so that paintChildren paints only what we
+ // want.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild =
+ (Component) components.get(i - 1);
+ }
+ }
+
+ // Actually trigger painting.
+ if (haveBuffer)
+ paintRoot.paintDoubleBuffered(paintX, paintY, paintW, paintH);
+ else
+ {
+ Graphics g = paintRoot.getGraphics();
+ try
+ {
+ g.setClip(paintX, paintY, paintW, paintH);
+ paintRoot.paint(g);
+ }
+ finally
+ {
+ g.dispose();
+ }
+ }
+
+ // Reset the painting path.
+ if (paintRoot != this)
+ {
+ for (int i = pIndex; i > 0; i--)
+ {
+ Component paintParent = (Component) components.get(i);
+ if (paintParent instanceof JComponent)
+ ((JComponent) paintParent).paintChild = null;
+ }
+ }
+
+ isRepainting = false;
+ }
+ }
+
+ /**
+ * Returns true if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return true if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return false;
+ }
+
+ /**
+ * This returns true when a component needs to force itself as a paint
+ * origin. This is used for example in JViewport to make sure that it
+ * gets to update its backbuffer.
+ *
+ * @return true when a component needs to force itself as a paint
+ * origin
+ */
+ boolean isPaintRoot()
+ {
+ return false;
+ }
+
+ /**
+ * Performs double buffered repainting.
+ */
+ private void paintDoubleBuffered(int x, int y, int w, int h)
+ {
+ RepaintManager rm = RepaintManager.currentManager(this);
+
+ // Paint on the offscreen buffer.
+ Component root = SwingUtilities.getRoot(this);
+ Image buffer = rm.getVolatileOffscreenBuffer(this, root.getWidth(),
+ root.getHeight());
+
+ // The volatile offscreen buffer may be null when that's not supported
+ // by the AWT backend. Fall back to normal backbuffer in this case.
+ if (buffer == null)
+ buffer = rm.getOffscreenBuffer(this, root.getWidth(), root.getHeight());
+
+ //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root);
+ Graphics g2 = buffer.getGraphics();
+ clipAndTranslateGraphics(root, this, g2);
+ g2.clipRect(x, y, w, h);
+ g2 = getComponentGraphics(g2);
+ paintingDoubleBuffered = true;
+ try
+ {
+ if (isRepainting) // Called from paintImmediately, go through paint().
+ paint(g2);
+ else // Called from paint() (AWT refresh), don't call it again.
+ {
+ paintComponent(g2);
+ paintBorder(g2);
+ paintChildren(g2);
+ }
+ }
+ finally
+ {
+ paintingDoubleBuffered = false;
+ g2.dispose();
+ }
+
+ // Paint the buffer contents on screen.
+ rm.commitBuffer(this, x, y, w, h);
+ }
+
+ /**
+ * Clips and translates the Graphics instance for painting on the double
+ * buffer. This has to be done, so that it reflects the component clip of the
+ * target component.
+ *
+ * @param root the root component (top-level container usually)
+ * @param target the component to be painted
+ * @param g the Graphics instance
+ */
+ private void clipAndTranslateGraphics(Component root, Component target,
+ Graphics g)
+ {
+ Component parent = target;
+ int deltaX = 0;
+ int deltaY = 0;
+ while (parent != root)
+ {
+ deltaX += parent.getX();
+ deltaY += parent.getY();
+ parent = parent.getParent();
+ }
+ g.translate(deltaX, deltaY);
+ g.clipRect(0, 0, target.getWidth(), target.getHeight());
+ }
+
+ /**
+ * Performs normal painting without double buffering.
+ *
+ * @param r the area that should be repainted
+ */
+ void paintSimple(Rectangle r)
+ {
+ Graphics g = getGraphics();
+ Graphics g2 = getComponentGraphics(g);
+ g2.setClip(r);
+ paint(g2);
+ g2.dispose();
+ if (g != g2)
+ g.dispose();
+ }
+
+ /**
+ * Return a string representation for this component, for use in
+ * debugging.
+ *
+ * @return A string describing this component.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",alignmentX=").append(getAlignmentX());
+ sb.append(",alignmentY=").append(getAlignmentY());
+ sb.append(",border=");
+ if (getBorder() != null)
+ sb.append(getBorder());
+ sb.append(",maximumSize=");
+ if (getMaximumSize() != null)
+ sb.append(getMaximumSize());
+ sb.append(",minimumSize=");
+ if (getMinimumSize() != null)
+ sb.append(getMinimumSize());
+ sb.append(",preferredSize=");
+ if (getPreferredSize() != null)
+ sb.append(getPreferredSize());
+ return sb.toString();
+ }
+
+ /**
+ * A variant of {@link
+ * #registerKeyboardAction(ActionListener,String,KeyStroke,int)} which
+ * provides null for the command name.
+ *
+ * @param act the action listener to notify when the keystroke occurs.
+ * @param stroke the key stroke.
+ * @param cond the condition (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_IN_FOCUSED_WINDOW} and
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
+ */
+ public void registerKeyboardAction(ActionListener act,
+ KeyStroke stroke,
+ int cond)
+ {
+ registerKeyboardAction(act, null, stroke, cond);
+ }
+
+ /*
+ * There is some charmingly undocumented behavior sun seems to be using
+ * to simulate the old register/unregister keyboard binding API. It's not
+ * clear to me why this matters, but we shall endeavour to follow suit.
+ *
+ * Two main thing seem to be happening when you do registerKeyboardAction():
+ *
+ * - no actionMap() entry gets created, just an entry in inputMap()
+ *
+ * - the inputMap() entry is a proxy class which invokes the the
+ * binding's actionListener as a target, and which clobbers the command
+ * name sent in the ActionEvent, providing the binding command name
+ * instead.
+ *
+ * This much you can work out just by asking the input and action maps
+ * what they contain after making bindings, and watching the event which
+ * gets delivered to the recipient. Beyond that, it seems to be a
+ * sun-private solution so I will only immitate it as much as it matters
+ * to external observers.
+ */
+ private static class ActionListenerProxy
+ extends AbstractAction
+ {
+ ActionListener target;
+ String bindingCommandName;
+
+ public ActionListenerProxy(ActionListener li,
+ String cmd)
+ {
+ target = li;
+ bindingCommandName = cmd;
+ }
+
+ public void actionPerformed(ActionEvent e)
+ {
+ ActionEvent derivedEvent = new ActionEvent(e.getSource(),
+ e.getID(),
+ bindingCommandName,
+ e.getModifiers());
+ target.actionPerformed(derivedEvent);
+ }
+ }
+
+
+ /**
+ * An obsolete method to register a keyboard action on this component.
+ * You should use getInputMap and getActionMap
+ * to fetch mapping tables from keystrokes to commands, and commands to
+ * actions, respectively, and modify those mappings directly.
+ *
+ * @param act The action to be registered
+ * @param cmd The command to deliver in the delivered {@link
+ * java.awt.event.ActionEvent}
+ * @param stroke The keystroke to register on
+ * @param cond One of the values {@link #UNDEFINED_CONDITION},
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or
+ * {@link #WHEN_IN_FOCUSED_WINDOW}, indicating the condition which must
+ * be met for the action to be fired
+ *
+ * @see #unregisterKeyboardAction
+ * @see #getConditionForKeyStroke
+ * @see #resetKeyboardActions
+ */
+ public void registerKeyboardAction(ActionListener act,
+ String cmd,
+ KeyStroke stroke,
+ int cond)
+ {
+ ActionListenerProxy proxy = new ActionListenerProxy(act, cmd);
+ getInputMap(cond).put(stroke, proxy);
+ getActionMap().put(proxy, proxy);
+ }
+
+ /**
+ * Sets the input map for the given condition.
+ *
+ * @param condition the condition (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_IN_FOCUSED_WINDOW} and
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}).
+ * @param map the map.
+ *
+ * @throws IllegalArgumentException if condition is not one of
+ * the specified values.
+ */
+ public final void setInputMap(int condition, InputMap map)
+ {
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+ switch (condition)
+ {
+ case WHEN_FOCUSED:
+ inputMap_whenFocused = map;
+ break;
+
+ case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
+ inputMap_whenAncestorOfFocused = map;
+ break;
+
+ case WHEN_IN_FOCUSED_WINDOW:
+ if (map != null && !(map instanceof ComponentInputMap))
+ throw new
+ IllegalArgumentException("WHEN_IN_FOCUSED_WINDOW " +
+ "InputMap must be a ComponentInputMap");
+ inputMap_whenInFocusedWindow = (ComponentInputMap)map;
+ break;
+
+ case UNDEFINED_CONDITION:
+ default:
+ throw new IllegalArgumentException();
+ }
+ }
+
+ /**
+ * Returns the input map associated with this component for the given
+ * state/condition.
+ *
+ * @param condition the state (one of {@link #WHEN_FOCUSED},
+ * {@link #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT} and
+ * {@link #WHEN_IN_FOCUSED_WINDOW}).
+ *
+ * @return The input map.
+ * @throws IllegalArgumentException if condition is not one of
+ * the specified values.
+ * @since 1.3
+ */
+ public final InputMap getInputMap(int condition)
+ {
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+ switch (condition)
+ {
+ case WHEN_FOCUSED:
+ if (inputMap_whenFocused == null)
+ inputMap_whenFocused = new InputMap();
+ return inputMap_whenFocused;
+
+ case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
+ if (inputMap_whenAncestorOfFocused == null)
+ inputMap_whenAncestorOfFocused = new InputMap();
+ return inputMap_whenAncestorOfFocused;
+
+ case WHEN_IN_FOCUSED_WINDOW:
+ if (inputMap_whenInFocusedWindow == null)
+ inputMap_whenInFocusedWindow = new ComponentInputMap(this);
+ return inputMap_whenInFocusedWindow;
+
+ case UNDEFINED_CONDITION:
+ default:
+ throw new IllegalArgumentException("Invalid 'condition' argument: "
+ + condition);
+ }
+ }
+
+ /**
+ * Returns the input map associated with this component for the
+ * {@link #WHEN_FOCUSED} state.
+ *
+ * @return The input map.
+ *
+ * @since 1.3
+ * @see #getInputMap(int)
+ */
+ public final InputMap getInputMap()
+ {
+ return getInputMap(WHEN_FOCUSED);
+ }
+
+ public final ActionMap getActionMap()
+ {
+ if (actionMap == null)
+ actionMap = new ActionMap();
+ return actionMap;
+ }
+
+ public final void setActionMap(ActionMap map)
+ {
+ actionMap = map;
+ }
+
+ /**
+ * Return the condition that determines whether a registered action
+ * occurs in response to the specified keystroke.
+ *
+ * As of 1.3 KeyStrokes can be registered with multiple simultaneous
+ * conditions.
+ *
+ * @param ks The keystroke to return the condition of
+ *
+ * @return One of the values {@link #UNDEFINED_CONDITION}, {@link
+ * #WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}, {@link #WHEN_FOCUSED}, or {@link
+ * #WHEN_IN_FOCUSED_WINDOW}
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ * @see #unregisterKeyboardAction
+ * @see #resetKeyboardActions
+ */
+ public int getConditionForKeyStroke(KeyStroke ks)
+ {
+ if (inputMap_whenFocused != null
+ && inputMap_whenFocused.get(ks) != null)
+ return WHEN_FOCUSED;
+ else if (inputMap_whenAncestorOfFocused != null
+ && inputMap_whenAncestorOfFocused.get(ks) != null)
+ return WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
+ else if (inputMap_whenInFocusedWindow != null
+ && inputMap_whenInFocusedWindow.get(ks) != null)
+ return WHEN_IN_FOCUSED_WINDOW;
+ else
+ return UNDEFINED_CONDITION;
+ }
+
+ /**
+ * Get the ActionListener (typically an {@link Action} object) which is
+ * associated with a particular keystroke.
+ *
+ * @param ks The keystroke to retrieve the action of
+ *
+ * @return The action associated with the specified keystroke
+ */
+ public ActionListener getActionForKeyStroke(KeyStroke ks)
+ {
+ Object key = getInputMap(JComponent.WHEN_FOCUSED).get(ks);
+ if (key == null)
+ key = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).get(ks);
+ if (key == null)
+ key = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).get(ks);
+ if (key != null)
+ {
+ if (key instanceof ActionListenerProxy)
+ return ((ActionListenerProxy) key).target;
+ else
+ return getActionMap().get(key);
+ }
+ return null;
+ }
+
+ /**
+ * A hook for subclasses which want to customize event processing.
+ */
+ protected void processComponentKeyEvent(KeyEvent e)
+ {
+ // This method does nothing, it is meant to be overridden by subclasses.
+ }
+
+ /**
+ * Override the default key dispatch system from Component to hook into
+ * the swing {@link InputMap} / {@link ActionMap} system.
+ *
+ * See
+ * this report for more details, it's somewhat complex.
+ */
+ protected void processKeyEvent(KeyEvent e)
+ {
+ // let the AWT event processing send KeyEvents to registered listeners
+ super.processKeyEvent(e);
+ processComponentKeyEvent(e);
+
+ if (e.isConsumed())
+ return;
+
+ // Input maps are checked in this order:
+ // 1. The focused component's WHEN_FOCUSED map is checked.
+ // 2. The focused component's WHEN_ANCESTOR_OF_FOCUSED_COMPONENT map.
+ // 3. The WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps of the focused
+ // component's parent, then its parent's parent, and so on.
+ // Note: Input maps for disabled components are skipped.
+ // 4. The WHEN_IN_FOCUSED_WINDOW maps of all the enabled components in
+ // the focused window are searched.
+
+ KeyStroke keyStroke = KeyStroke.getKeyStrokeForEvent(e);
+ boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
+
+ if (processKeyBinding(keyStroke, e, WHEN_FOCUSED, pressed))
+ {
+ // This is step 1 from above comment.
+ e.consume();
+ return;
+ }
+ else if (processKeyBinding
+ (keyStroke, e, WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
+ {
+ // This is step 2 from above comment.
+ e.consume();
+ return;
+ }
+
+ // This is step 3 from above comment.
+ Container current = getParent();
+ while (current != null)
+ {
+ // If current is a JComponent, see if it handles the event in its
+ // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT maps.
+ if ((current instanceof JComponent) &&
+ ((JComponent)current).processKeyBinding
+ (keyStroke, e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, pressed))
+ {
+ e.consume();
+ return;
+ }
+
+ // Stop when we've tried a top-level container and it didn't handle it
+ if (current instanceof Window || current instanceof Applet)
+ break;
+
+ // Move up the hierarchy
+ current = current.getParent();
+ }
+
+ // Current being null means the JComponent does not currently have a
+ // top-level ancestor, in which case we don't need to check
+ // WHEN_IN_FOCUSED_WINDOW bindings.
+ if (current == null || e.isConsumed())
+ return;
+
+ // This is step 4 from above comment. KeyboardManager maintains mappings
+ // related to WHEN_IN_FOCUSED_WINDOW bindings so that we don't have to
+ // traverse the containment hierarchy each time.
+ if (KeyboardManager.getManager().processKeyStroke(current, keyStroke, e))
+ e.consume();
+ }
+
+ protected boolean processKeyBinding(KeyStroke ks,
+ KeyEvent e,
+ int condition,
+ boolean pressed)
+ {
+ if (isEnabled())
+ {
+ Action act = null;
+ Object cmd = null;
+ InputMap map = getInputMap(condition);
+ if (map != null)
+ {
+ cmd = map.get(ks);
+ if (cmd != null)
+ {
+ if (cmd instanceof ActionListenerProxy)
+ act = (Action) cmd;
+ else
+ act = getActionMap().get(cmd);
+ }
+ }
+ if (act != null && act.isEnabled())
+ {
+ // Need to synchronize here so we don't get in trouble with
+ // our __command__ hack.
+ synchronized (act)
+ {
+ // We add the command as value to the action, so that
+ // the action can later determine the command with which it
+ // was called. This is undocumented, but shouldn't affect
+ // compatibility. It allows us to use only one Action instance
+ // to do the work for all components of one type, instead of
+ // having loads of small Actions. This effectivly saves startup
+ // time of Swing.
+ act.putValue("__command__", cmd);
+ return SwingUtilities.notifyAction(act, ks, e, this,
+ e.getModifiers());
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Remove a keyboard action registry.
+ *
+ * @param aKeyStroke The keystroke to unregister
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ * @see #getConditionForKeyStroke
+ * @see #resetKeyboardActions
+ */
+ public void unregisterKeyboardAction(KeyStroke aKeyStroke)
+ {
+ ActionMap am = getActionMap();
+ // This loops through the conditions WHEN_FOCUSED,
+ // WHEN_ANCESTOR_OF_FOCUSED_COMPONENT and WHEN_IN_FOCUSED_WINDOW.
+ for (int cond = 0; cond < 3; cond++)
+ {
+ InputMap im = getInputMap(cond);
+ if (im != null)
+ {
+ Object action = im.get(aKeyStroke);
+ if (action != null && am != null)
+ am.remove(action);
+ im.remove(aKeyStroke);
+ }
+ }
+ }
+
+
+ /**
+ * Reset all keyboard action registries.
+ *
+ * @see #registerKeyboardAction(ActionListener, KeyStroke, int)
+ * @see #unregisterKeyboardAction
+ * @see #getConditionForKeyStroke
+ */
+ public void resetKeyboardActions()
+ {
+ if (inputMap_whenFocused != null)
+ inputMap_whenFocused.clear();
+ if (inputMap_whenAncestorOfFocused != null)
+ inputMap_whenAncestorOfFocused.clear();
+ if (inputMap_whenInFocusedWindow != null)
+ inputMap_whenInFocusedWindow.clear();
+ if (actionMap != null)
+ actionMap.clear();
+ }
+
+ /**
+ * Mark the described region of this component as dirty in the current
+ * {@link RepaintManager}. This will queue an asynchronous repaint using
+ * the system painting thread in the near future.
+ *
+ * @param tm ignored
+ * @param x coordinate of the region to mark as dirty
+ * @param y coordinate of the region to mark as dirty
+ * @param width dimension of the region to mark as dirty
+ * @param height dimension of the region to mark as dirty
+ */
+ public void repaint(long tm, int x, int y, int width, int height)
+ {
+ RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width,
+ height);
+ }
+
+ /**
+ * Mark the described region of this component as dirty in the current
+ * {@link RepaintManager}. This will queue an asynchronous repaint using
+ * the system painting thread in the near future.
+ *
+ * @param r The rectangle to mark as dirty
+ */
+ public void repaint(Rectangle r)
+ {
+ RepaintManager.currentManager(this).addDirtyRegion(this, r.x, r.y, r.width,
+ r.height);
+ }
+
+ /**
+ * Request focus on the default component of this component's {@link
+ * FocusTraversalPolicy}.
+ *
+ * @return The result of {@link #requestFocus()}
+ *
+ * @deprecated Use {@link #requestFocus()} on the default component provided
+ * from the {@link FocusTraversalPolicy} instead.
+ */
+ public boolean requestDefaultFocus()
+ {
+ return false;
+ }
+
+ /**
+ * Queue a an invalidation and revalidation of this component, using
+ * {@link RepaintManager#addInvalidComponent}.
+ */
+ public void revalidate()
+ {
+ // As long as we don't have a parent we don't need to do any layout, since
+ // this is done anyway as soon as we get connected to a parent.
+ if (getParent() == null)
+ return;
+
+ if (! EventQueue.isDispatchThread())
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ revalidate();
+ }
+ });
+ else
+ {
+ invalidate();
+ RepaintManager.currentManager(this).addInvalidComponent(this);
+ }
+ }
+
+ /**
+ * Calls scrollRectToVisible on the component's parent.
+ * Components which can service this call should override.
+ *
+ * @param r The rectangle to make visible
+ */
+ public void scrollRectToVisible(Rectangle r)
+ {
+ // Search nearest JComponent.
+ int xOffs = getX();
+ int yOffs = getY();
+ Component p;
+ for (p = getParent(); p != null && ! (p instanceof JComponent);
+ p = p.getParent())
+ {
+ xOffs += p.getX();
+ yOffs += p.getY();
+ }
+ if (p != null)
+ {
+ r.x += xOffs;
+ r.y += yOffs;
+ JComponent jParent = (JComponent) p;
+ jParent.scrollRectToVisible(r);
+ r.x -= xOffs;
+ r.y -= yOffs;
+ }
+ }
+
+ /**
+ * Set the value of the {@link #alignmentX} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAlignmentX(float a)
+ {
+ if (a < 0.0F)
+ alignmentX = 0.0F;
+ else if (a > 1.0)
+ alignmentX = 1.0F;
+ else
+ alignmentX = a;
+ }
+
+ /**
+ * Set the value of the {@link #alignmentY} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAlignmentY(float a)
+ {
+ if (a < 0.0F)
+ alignmentY = 0.0F;
+ else if (a > 1.0)
+ alignmentY = 1.0F;
+ else
+ alignmentY = a;
+ }
+
+ /**
+ * Set the value of the {@link #autoscrolls} property.
+ *
+ * @param a The new value of the property
+ */
+ public void setAutoscrolls(boolean a)
+ {
+ autoscrolls = a;
+ clientAutoscrollsSet = true;
+ }
+
+ /**
+ * Set the value of the {@link #debugGraphicsOptions} property.
+ *
+ * @param debugOptions The new value of the property
+ */
+ public void setDebugGraphicsOptions(int debugOptions)
+ {
+ debugGraphicsOptions = debugOptions;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBuffered} property.
+ *
+ * @param db The new value of the property
+ */
+ public void setDoubleBuffered(boolean db)
+ {
+ doubleBuffered = db;
+ }
+
+ /**
+ * Set the value of the enabled property.
+ *
+ * @param enable The new value of the property
+ */
+ public void setEnabled(boolean enable)
+ {
+ if (enable == isEnabled())
+ return;
+ super.setEnabled(enable);
+ firePropertyChange("enabled", !enable, enable);
+ repaint();
+ }
+
+ /**
+ * Set the value of the font property.
+ *
+ * @param f The new value of the property
+ */
+ public void setFont(Font f)
+ {
+ if (f == getFont())
+ return;
+ super.setFont(f);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the background property.
+ *
+ * @param bg The new value of the property
+ */
+ public void setBackground(Color bg)
+ {
+ if (bg == getBackground())
+ return;
+ super.setBackground(bg);
+ repaint();
+ }
+
+ /**
+ * Set the value of the foreground property.
+ *
+ * @param fg The new value of the property
+ */
+ public void setForeground(Color fg)
+ {
+ if (fg == getForeground())
+ return;
+ super.setForeground(fg);
+ repaint();
+ }
+
+ /**
+ * Set the specified component to be the next component in the
+ * focus cycle, overriding the {@link FocusTraversalPolicy} for
+ * this component.
+ *
+ * @param aComponent The component to set as the next focusable
+ *
+ * @deprecated Use FocusTraversalPolicy instead
+ */
+ public void setNextFocusableComponent(Component aComponent)
+ {
+ Container focusRoot = this;
+ if (! this.isFocusCycleRoot())
+ focusRoot = getFocusCycleRootAncestor();
+
+ FocusTraversalPolicy policy = focusRoot.getFocusTraversalPolicy();
+ if (policy instanceof CompatibilityFocusTraversalPolicy)
+ {
+ policy = new CompatibilityFocusTraversalPolicy(policy);
+ focusRoot.setFocusTraversalPolicy(policy);
+ }
+ CompatibilityFocusTraversalPolicy p =
+ (CompatibilityFocusTraversalPolicy) policy;
+
+ Component old = getNextFocusableComponent();
+ if (old != null)
+ {
+ p.removeNextFocusableComponent(this, old);
+ }
+
+ if (aComponent != null)
+ {
+ p.addNextFocusableComponent(this, aComponent);
+ }
+ }
+
+ /**
+ * Set the value of the {@link #requestFocusEnabled} property.
+ *
+ * @param e The new value of the property
+ */
+ public void setRequestFocusEnabled(boolean e)
+ {
+ requestFocusEnabled = e;
+ }
+
+ /**
+ * Get the value of the {@link #transferHandler} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setTransferHandler
+ */
+
+ public TransferHandler getTransferHandler()
+ {
+ return transferHandler;
+ }
+
+ /**
+ * Set the value of the {@link #transferHandler} property.
+ *
+ * @param newHandler The new value of the property
+ *
+ * @see #getTransferHandler
+ */
+
+ public void setTransferHandler(TransferHandler newHandler)
+ {
+ if (transferHandler == newHandler)
+ return;
+
+ TransferHandler oldHandler = transferHandler;
+ transferHandler = newHandler;
+ firePropertyChange("transferHandler", oldHandler, newHandler);
+ }
+
+ /**
+ * Set if the component should paint all pixels withing its bounds.
+ * If this property is set to false, the component expects the cleared
+ * background.
+ *
+ * @param isOpaque if true, paint all pixels. If false, expect the clean
+ * background.
+ *
+ * @see ComponentUI#update
+ */
+ public void setOpaque(boolean isOpaque)
+ {
+ boolean oldOpaque = opaque;
+ opaque = isOpaque;
+ clientOpaqueSet = true;
+ firePropertyChange("opaque", oldOpaque, opaque);
+ }
+
+ /**
+ * Set the value of the visible property.
+ *
+ * If the value is changed, then the AncestorListeners of this component
+ * and all its children (recursivly) are notified.
+ *
+ * @param v The new value of the property
+ */
+ public void setVisible(boolean v)
+ {
+ // No need to do anything if the actual value doesn't change.
+ if (isVisible() == v)
+ return;
+
+ super.setVisible(v);
+
+ // Notify AncestorListeners.
+ if (v == true)
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
+ else
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
+
+ Container parent = getParent();
+ if (parent != null)
+ parent.repaint(getX(), getY(), getWidth(), getHeight());
+ revalidate();
+ }
+
+ /**
+ * Call {@link #paint}.
+ *
+ * @param g The graphics context to paint into
+ */
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ /**
+ * Get the value of the UIClassID property. This property should be a key
+ * in the {@link UIDefaults} table managed by {@link UIManager}, the
+ * value of which is the name of a class to load for the component's
+ * {@link #ui} property.
+ *
+ * @return A "symbolic" name which will map to a class to use for the
+ * component's UI, such as "ComponentUI"
+ *
+ * @see #setUI
+ * @see #updateUI
+ */
+ public String getUIClassID()
+ {
+ return "ComponentUI";
+ }
+
+ /**
+ * Install a new UI delegate as the component's {@link #ui} property. In
+ * the process, this will call {@link ComponentUI#uninstallUI} on any
+ * existing value for the {@link #ui} property, and {@link
+ * ComponentUI#installUI} on the new UI delegate.
+ *
+ * @param newUI The new UI delegate to install
+ *
+ * @see #updateUI
+ * @see #getUIClassID
+ */
+ protected void setUI(ComponentUI newUI)
+ {
+ if (ui != null)
+ ui.uninstallUI(this);
+
+ ComponentUI oldUI = ui;
+ ui = newUI;
+
+ if (ui != null)
+ ui.installUI(this);
+
+ firePropertyChange("UI", oldUI, newUI);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * This method should be overridden in subclasses. In JComponent, the
+ * method does nothing. In subclasses, it should a UI delegate
+ * (corresponding to the symbolic name returned from {@link
+ * #getUIClassID}) from the {@link UIManager}, and calls {@link #setUI}
+ * with the new delegate.
+ */
+ public void updateUI()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the locale used as the default for all new components. The
+ * default value is {@link Locale#getDefault()} (that is, the platform
+ * default locale).
+ *
+ * @return The locale (never null).
+ *
+ * @see #setDefaultLocale(Locale)
+ */
+ public static Locale getDefaultLocale()
+ {
+ if (defaultLocale == null)
+ defaultLocale = Locale.getDefault();
+ return defaultLocale;
+ }
+
+ /**
+ * Sets the locale to be used as the default for all new components. If this
+ * is set to null, the {@link #getDefaultLocale()} method will
+ * return the platform default locale.
+ *
+ * @param l the locale (null permitted).
+ */
+ public static void setDefaultLocale(Locale l)
+ {
+ defaultLocale = l;
+ }
+
+ /**
+ * Returns the currently set input verifier for this component.
+ *
+ * @return the input verifier, or null if none
+ */
+ public InputVerifier getInputVerifier()
+ {
+ return inputVerifier;
+ }
+
+ /**
+ * Sets the input verifier to use by this component.
+ *
+ * @param verifier the input verifier, or null
+ */
+ public void setInputVerifier(InputVerifier verifier)
+ {
+ InputVerifier oldVerifier = inputVerifier;
+ inputVerifier = verifier;
+ firePropertyChange("inputVerifier", oldVerifier, verifier);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public boolean getVerifyInputWhenFocusTarget()
+ {
+ return verifyInputWhenFocusTarget;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setVerifyInputWhenFocusTarget(boolean verifyInputWhenFocusTarget)
+ {
+ if (this.verifyInputWhenFocusTarget == verifyInputWhenFocusTarget)
+ return;
+
+ this.verifyInputWhenFocusTarget = verifyInputWhenFocusTarget;
+ firePropertyChange("verifyInputWhenFocusTarget",
+ ! verifyInputWhenFocusTarget,
+ verifyInputWhenFocusTarget);
+ }
+
+ /**
+ * Requests that this component gets the input focus if the
+ * requestFocusEnabled property is set to true.
+ * This also means that this component's top-level window becomes
+ * the focused window, if that is not already the case.
+ *
+ * The preconditions that have to be met to become a focus owner is that
+ * the component must be displayable, visible and focusable.
+ *
+ * Note that this signals only a request for becoming focused. There are
+ * situations in which it is not possible to get the focus. So developers
+ * should not assume that the component has the focus until it receives
+ * a {@link java.awt.event.FocusEvent} with a value of
+ * {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
+ *
+ * @see Component#requestFocus()
+ */
+ public void requestFocus()
+ {
+ if (isRequestFocusEnabled())
+ super.requestFocus();
+ }
+
+ /**
+ * This method is overridden to make it public so that it can be used
+ * by look and feel implementations.
+ *
+ * You should not use this method directly. Instead you are strongly
+ * encouraged to call {@link #requestFocus()} or
+ * {@link #requestFocusInWindow()} instead.
+ *
+ * @param temporary if the focus change is temporary
+ *
+ * @return false if the focus change request will definitly
+ * fail, true if it will likely succeed
+ *
+ * @see Component#requestFocus(boolean)
+ *
+ * @since 1.4
+ */
+ public boolean requestFocus(boolean temporary)
+ {
+ return super.requestFocus(temporary);
+ }
+
+ /**
+ * Requests that this component gets the input focus if the top level
+ * window that contains this component has the focus and the
+ * requestFocusEnabled property is set to true.
+ *
+ * The preconditions that have to be met to become a focus owner is that
+ * the component must be displayable, visible and focusable.
+ *
+ * Note that this signals only a request for becoming focused. There are
+ * situations in which it is not possible to get the focus. So developers
+ * should not assume that the component has the focus until it receives
+ * a {@link java.awt.event.FocusEvent} with a value of
+ * {@link java.awt.event.FocusEvent#FOCUS_GAINED}.
+ *
+ * @return false if the focus change request will definitly
+ * fail, true if it will likely succeed
+ *
+ * @see Component#requestFocusInWindow()
+ */
+ public boolean requestFocusInWindow()
+ {
+ if (isRequestFocusEnabled())
+ return super.requestFocusInWindow();
+ else
+ return false;
+ }
+
+ /**
+ * This method is overridden to make it public so that it can be used
+ * by look and feel implementations.
+ *
+ * You should not use this method directly. Instead you are strongly
+ * encouraged to call {@link #requestFocus()} or
+ * {@link #requestFocusInWindow()} instead.
+ *
+ * @param temporary if the focus change is temporary
+ *
+ * @return false if the focus change request will definitly
+ * fail, true if it will likely succeed
+ *
+ * @see Component#requestFocus(boolean)
+ *
+ * @since 1.4
+ */
+ protected boolean requestFocusInWindow(boolean temporary)
+ {
+ return super.requestFocusInWindow(temporary);
+ }
+
+ /**
+ * Receives notification if this component is added to a parent component.
+ *
+ * Notification is sent to all registered AncestorListeners about the
+ * new parent.
+ *
+ * This method sets up ActionListeners for all registered KeyStrokes of
+ * this component in the chain of parent components.
+ *
+ * A PropertyChange event is fired to indicate that the ancestor property
+ * has changed.
+ *
+ * This method is used internally and should not be used in applications.
+ */
+ public void addNotify()
+ {
+ // Register the WHEN_IN_FOCUSED_WINDOW keyboard bindings
+ // Note that here we unregister all bindings associated with
+ // this component and then re-register them. This may be more than
+ // necessary if the top-level ancestor hasn't changed. Should
+ // maybe improve this.
+ KeyboardManager km = KeyboardManager.getManager();
+ km.clearBindingsForComp(this);
+ km.registerEntireMap((ComponentInputMap)
+ this.getInputMap(WHEN_IN_FOCUSED_WINDOW));
+ super.addNotify();
+
+ // Notify AncestorListeners.
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_ADDED);
+
+ // fire property change event for 'ancestor'
+ firePropertyChange("ancestor", null, getParent());
+ }
+
+ /**
+ * Receives notification that this component no longer has a parent.
+ *
+ * This method sends an AncestorEvent to all registered AncestorListeners,
+ * notifying them that the parent is gone.
+ *
+ * The keybord actions of this component are removed from the parent and
+ * its ancestors.
+ *
+ * A PropertyChangeEvent is fired to indicate that the 'ancestor' property
+ * has changed.
+ *
+ * This method is called before the component is actually removed from
+ * its parent, so the parent is still visible through
+ * {@link Component#getParent}.
+ */
+ public void removeNotify()
+ {
+ super.removeNotify();
+
+ KeyboardManager.getManager().clearBindingsForComp(this);
+
+ // Notify ancestor listeners.
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_REMOVED);
+
+ // fire property change event for 'ancestor'
+ firePropertyChange("ancestor", getParent(), null);
+ }
+
+ /**
+ * Returns true if the coordinates (x, y) lie within
+ * the bounds of this component and false otherwise.
+ * x and y are relative to the coordinate space of the component.
+ *
+ * @param x the X coordinate of the point to check
+ * @param y the Y coordinate of the point to check
+ *
+ * @return true if the specified point lies within the bounds
+ * of this component, false otherwise
+ */
+ public boolean contains(int x, int y)
+ {
+ if (ui == null)
+ return super.contains(x, y);
+ else
+ return ui.contains(this, x, y);
+ }
+
+ /**
+ * Disables this component.
+ *
+ * @deprecated replaced by {@link #setEnabled(boolean)}
+ */
+ public void disable()
+ {
+ super.disable();
+ }
+
+ /**
+ * Enables this component.
+ *
+ * @deprecated replaced by {@link #setEnabled(boolean)}
+ */
+ public void enable()
+ {
+ super.enable();
+ }
+
+ /**
+ * Returns the Graphics context for this component. This can be used
+ * to draw on a component.
+ *
+ * @return the Graphics context for this component
+ */
+ public Graphics getGraphics()
+ {
+ return super.getGraphics();
+ }
+
+ /**
+ * Returns the X coordinate of the upper left corner of this component.
+ * Prefer this method over {@link #getBounds} or {@link #getLocation}
+ * because it does not cause any heap allocation.
+ *
+ * @return the X coordinate of the upper left corner of the component
+ */
+ public int getX()
+ {
+ return super.getX();
+ }
+
+ /**
+ * Returns the Y coordinate of the upper left corner of this component.
+ * Prefer this method over {@link #getBounds} or {@link #getLocation}
+ * because it does not cause any heap allocation.
+ *
+ * @return the Y coordinate of the upper left corner of the component
+ */
+ public int getY()
+ {
+ return super.getY();
+ }
+
+ /**
+ * Returns the height of this component. Prefer this method over
+ * {@link #getBounds} or {@link #getSize} because it does not cause
+ * any heap allocation.
+ *
+ * @return the height of the component
+ */
+ public int getHeight()
+ {
+ return super.getHeight();
+ }
+
+ /**
+ * Returns the width of this component. Prefer this method over
+ * {@link #getBounds} or {@link #getSize} because it does not cause
+ * any heap allocation.
+ *
+ * @return the width of the component
+ */
+ public int getWidth()
+ {
+ return super.getWidth();
+ }
+
+ /**
+ * Prints this component to the given Graphics context. A call to this
+ * method results in calls to the methods {@link #printComponent},
+ * {@link #printBorder} and {@link #printChildren} in this order.
+ *
+ * Double buffering is temporarily turned off so the painting goes directly
+ * to the supplied Graphics context.
+ *
+ * @param g the Graphics context to print onto
+ */
+ public void print(Graphics g)
+ {
+ boolean doubleBufferState = isDoubleBuffered();
+ setDoubleBuffered(false);
+ printComponent(g);
+ printBorder(g);
+ printChildren(g);
+ setDoubleBuffered(doubleBufferState);
+ }
+
+ /**
+ * Prints this component to the given Graphics context. This invokes
+ * {@link #print}.
+ *
+ * @param g the Graphics context to print onto
+ */
+ public void printAll(Graphics g)
+ {
+ print(g);
+ }
+
+ /**
+ * Prints this component to the specified Graphics context. The default
+ * behaviour is to invoke {@link #paintComponent}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printComponent(Graphics g)
+ {
+ paintComponent(g);
+ }
+
+ /**
+ * Print this component's children to the specified Graphics context.
+ * The default behaviour is to invoke {@link #paintChildren}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printChildren(Graphics g)
+ {
+ paintChildren(g);
+ }
+
+ /**
+ * Print this component's border to the specified Graphics context.
+ * The default behaviour is to invoke {@link #paintBorder}. Override this
+ * if you want special behaviour for printing.
+ *
+ * @param g the Graphics context to print onto
+ *
+ * @since 1.3
+ */
+ protected void printBorder(Graphics g)
+ {
+ paintBorder(g);
+ }
+
+ /**
+ * Processes mouse motion event, like dragging and moving.
+ *
+ * @param ev the MouseEvent describing the mouse motion
+ */
+ protected void processMouseMotionEvent(MouseEvent ev)
+ {
+ super.processMouseMotionEvent(ev);
+ }
+
+ /**
+ * Moves and resizes the component.
+ *
+ * @param x the new horizontal location
+ * @param y the new vertial location
+ * @param w the new width
+ * @param h the new height
+ */
+ public void reshape(int x, int y, int w, int h)
+ {
+ int oldX = getX();
+ int oldY = getY();
+ super.reshape(x, y, w, h);
+ // Notify AncestorListeners.
+ if (oldX != getX() || oldY != getY())
+ fireAncestorEvent(this, AncestorEvent.ANCESTOR_MOVED);
+ }
+
+ /**
+ * Fires an AncestorEvent to this component's and all of its child
+ * component's AncestorListeners.
+ *
+ * @param ancestor the component that triggered the event
+ * @param id the kind of ancestor event that should be fired
+ */
+ void fireAncestorEvent(JComponent ancestor, int id)
+ {
+ // Fire event for registered ancestor listeners of this component.
+ AncestorListener[] listeners = getAncestorListeners();
+ if (listeners.length > 0)
+ {
+ AncestorEvent ev = new AncestorEvent(this, id,
+ ancestor, ancestor.getParent());
+ for (int i = 0; i < listeners.length; i++)
+ {
+ switch (id)
+ {
+ case AncestorEvent.ANCESTOR_MOVED:
+ listeners[i].ancestorMoved(ev);
+ break;
+ case AncestorEvent.ANCESTOR_ADDED:
+ listeners[i].ancestorAdded(ev);
+ break;
+ case AncestorEvent.ANCESTOR_REMOVED:
+ listeners[i].ancestorRemoved(ev);
+ break;
+ }
+ }
+ }
+ // Dispatch event to all children.
+ int numChildren = getComponentCount();
+ for (int i = 0; i < numChildren; i++)
+ {
+ Component child = getComponent(i);
+ if (! (child instanceof JComponent))
+ continue;
+ JComponent jc = (JComponent) child;
+ jc.fireAncestorEvent(ancestor, id);
+ }
+ }
+
+ /**
+ * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map
+ * is changed.
+ *
+ * @param changed the JComponent associated with the WHEN_IN_FOCUSED_WINDOW
+ * map
+ */
+ void updateComponentInputMap(ComponentInputMap changed)
+ {
+ // Since you can change a component's input map via
+ // setInputMap, we have to check if changed
+ // is still in our WHEN_IN_FOCUSED_WINDOW map hierarchy
+ InputMap curr = getInputMap(WHEN_IN_FOCUSED_WINDOW);
+ while (curr != null && curr != changed)
+ curr = curr.getParent();
+
+ // If curr is null then changed is not in the hierarchy
+ if (curr == null)
+ return;
+
+ // Now we have to update the keyboard manager's hashtable
+ KeyboardManager km = KeyboardManager.getManager();
+
+ // This is a poor strategy, should be improved. We currently
+ // delete all the old bindings for the component and then register
+ // the current bindings.
+ km.clearBindingsForComp(changed.getComponent());
+ km.registerEntireMap((ComponentInputMap)
+ getInputMap(WHEN_IN_FOCUSED_WINDOW));
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("opaque"))
+ {
+ if (! clientOpaqueSet)
+ {
+ setOpaque(((Boolean) value).booleanValue());
+ clientOpaqueSet = false;
+ }
+ }
+ else if (propertyName.equals("autoscrolls"))
+ {
+ if (! clientAutoscrollsSet)
+ {
+ setAutoscrolls(((Boolean) value).booleanValue());
+ clientAutoscrollsSet = false;
+ }
+ }
+ else
+ {
+ throw new IllegalArgumentException
+ ("Unsupported property for LookAndFeel.installProperty(): "
+ + propertyName);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JDesktopPane.java b/libjava/classpath/javax/swing/JDesktopPane.java
new file mode 100644
index 000000000..6d8b21276
--- /dev/null
+++ b/libjava/classpath/javax/swing/JDesktopPane.java
@@ -0,0 +1,386 @@
+/* JDesktopPane.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.beans.PropertyVetoException;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.DesktopPaneUI;
+
+/**
+ * JDesktopPane is a container (usually for JInternalFrames) that simulates a
+ * desktop. Typically, the user will create JInternalFrames and place them in
+ * a JDesktopPane. The user can then interact with JInternalFrames like they
+ * usually would with JFrames. The actions (minimize, maximize, close, etc)
+ * are done by using a DesktopManager that is associated with the
+ * JDesktopPane.
+ */
+public class JDesktopPane extends JLayeredPane implements Accessible
+{
+ private static final long serialVersionUID = 766333777224038726L;
+
+ /**
+ * This specifies that when dragged, a JInternalFrame should be completely
+ * visible.
+ *
+ * @specnote final since 1.5.0.
+ */
+ public static final int LIVE_DRAG_MODE = 0;
+
+ /**
+ * This specifies that when dragged, a JInternalFrame should only be visible
+ * as an outline.
+ *
+ * @specnote final since 1.5.0.
+ */
+ public static final int OUTLINE_DRAG_MODE = 1;
+
+ /** The selected frame in the JDesktopPane. */
+ private transient JInternalFrame selectedFrame;
+
+ /** The JDesktopManager to use for acting on JInternalFrames. */
+ transient DesktopManager desktopManager;
+
+ /** The drag mode used by the JDesktopPane. */
+ private transient int dragMode = LIVE_DRAG_MODE;
+
+ /**
+ * Indicates if the dragMode property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientDragModeSet = false;
+
+ /**
+ * Provides the accessibility features for the JDesktopPane
+ * component.
+ */
+ protected class AccessibleJDesktopPane extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 6079388927946077570L;
+
+ /**
+ * Creates a new AccessibleJDesktopPane instance.
+ */
+ protected AccessibleJDesktopPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the JSlider component.
+ *
+ * @return {@link AccessibleRole#DESKTOP_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.DESKTOP_PANE;
+ }
+ }
+
+ /**
+ * Creates a new JDesktopPane object.
+ */
+ public JDesktopPane()
+ {
+ setLayout(null);
+ updateUI();
+ }
+
+ /**
+ * This method returns the UI used with the JDesktopPane.
+ *
+ * @return The UI used with the JDesktopPane.
+ */
+ public DesktopPaneUI getUI()
+ {
+ return (DesktopPaneUI) ui;
+ }
+
+ /**
+ * This method sets the UI used with the JDesktopPane.
+ *
+ * @param ui The UI to use with the JDesktopPane.
+ */
+ public void setUI(DesktopPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets the drag mode to use with the JDesktopPane.
+ *
+ * @param mode The drag mode to use.
+ *
+ * @throws IllegalArgumentException If the drag mode given is not
+ * LIVE_DRAG_MODE or OUTLINE_DRAG_MODE.
+ */
+ public void setDragMode(int mode)
+ {
+ if ((mode != LIVE_DRAG_MODE) && (mode != OUTLINE_DRAG_MODE))
+ throw new IllegalArgumentException("Drag mode not valid.");
+
+ clientDragModeSet = true;
+
+ // FIXME: Unsupported mode.
+ if (mode == OUTLINE_DRAG_MODE)
+ // throw new IllegalArgumentException("Outline drag modes are
+ // unsupported.");
+ mode = LIVE_DRAG_MODE;
+
+ dragMode = mode;
+ }
+
+ /**
+ * This method returns the drag mode used with the JDesktopPane.
+ *
+ * @return The drag mode used with the JDesktopPane.
+ */
+ public int getDragMode()
+ {
+ return dragMode;
+ }
+
+ /**
+ * This method returns the DesktopManager used with the JDesktopPane.
+ *
+ * @return The DesktopManager to use with the JDesktopPane.
+ */
+ public DesktopManager getDesktopManager()
+ {
+ return desktopManager;
+ }
+
+ /**
+ * This method sets the DesktopManager to use with the JDesktopPane.
+ *
+ * @param manager The DesktopManager to use with the JDesktopPane.
+ */
+ public void setDesktopManager(DesktopManager manager)
+ {
+ desktopManager = manager;
+ }
+
+ /**
+ * This method restores the UI used with the JDesktopPane to the default.
+ */
+ public void updateUI()
+ {
+ setUI((DesktopPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a String identifier that allows the UIManager to know
+ * which class will act as JDesktopPane's UI.
+ *
+ * @return A String identifier for the UI class to use.
+ */
+ public String getUIClassID()
+ {
+ return "DesktopPaneUI";
+ }
+
+ /**
+ * This method returns all JInternalFrames that are in the JDesktopPane.
+ *
+ * @return All JInternalFrames that are in the JDesktopPane.
+ */
+ public JInternalFrame[] getAllFrames()
+ {
+ return getFramesFromComponents(getComponents());
+ }
+
+ /**
+ * This method returns the currently selected frame in the JDesktopPane.
+ *
+ * @return The currently selected frame in the JDesktopPane.
+ */
+ public JInternalFrame getSelectedFrame()
+ {
+ return selectedFrame;
+ }
+
+ /**
+ * This method sets the selected frame in the JDesktopPane.
+ *
+ * @param frame The selected frame in the JDesktopPane.
+ */
+ public void setSelectedFrame(JInternalFrame frame)
+ {
+ if (selectedFrame != null)
+ {
+ try
+ {
+ selectedFrame.setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // We do nothing when the attempt is vetoed.
+ }
+ }
+ selectedFrame = null;
+
+ try
+ {
+ if (frame != null)
+ frame.setSelected(true);
+
+ selectedFrame = frame;
+ }
+ catch (PropertyVetoException e)
+ {
+ // We do nothing when the attempt is vetoed.
+ }
+ }
+
+ /**
+ * This method returns all the JInternalFrames in the given layer.
+ *
+ * @param layer The layer to grab frames in.
+ *
+ * @return All JInternalFrames in the given layer.
+ */
+ public JInternalFrame[] getAllFramesInLayer(int layer)
+ {
+ return getFramesFromComponents(getComponentsInLayer(layer));
+ }
+
+ /**
+ * This method always returns true to indicate that it is not transparent.
+ *
+ * @return true.
+ */
+ public boolean isOpaque()
+ {
+ return true;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JDesktopPane.
+ *
+ * @return A string describing the attributes of this JDesktopPane
+ * (never null).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isOptimizedDrawingPossible=");
+ sb.append(isOptimizedDrawingEnabled());
+ sb.append(",desktopManager=");
+ if (desktopManager != null)
+ sb.append(desktopManager);
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * This method returns all the JInternalFrames in the given Component array.
+ *
+ * @param components An array to search for JInternalFrames in.
+ *
+ * @return An array of JInternalFrames found in the Component array.
+ */
+ private static JInternalFrame[] getFramesFromComponents(Component[] components)
+ {
+ int count = 0;
+
+ for (int i = 0; i < components.length; i++)
+ if (components[i] instanceof JInternalFrame)
+ count++;
+
+ JInternalFrame[] value = new JInternalFrame[count];
+ for (int i = 0, j = 0; i < components.length && j != count; i++)
+ if (components[i] instanceof JInternalFrame)
+ value[j++] = (JInternalFrame) components[i];
+ return value;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JDesktopPane component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJDesktopPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDesktopPane();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("dragMode"))
+ {
+ if (! clientDragModeSet)
+ {
+ setDragMode(((Integer) value).intValue());
+ clientDragModeSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JDialog.java b/libjava/classpath/javax/swing/JDialog.java
new file mode 100644
index 000000000..04ec825bc
--- /dev/null
+++ b/libjava/classpath/javax/swing/JDialog.java
@@ -0,0 +1,583 @@
+/* JDialog.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTEvent;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dialog;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.IllegalComponentStateException;
+import java.awt.LayoutManager;
+import java.awt.event.WindowEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A dialog window. This is an extension of {@link java.awt.Dialog} that
+ * provides support for the Swing architecture. Most importantly it contains a
+ * {@link JRootPane} as it's only top-level child, that manages the content
+ * pane, the menu and a glass pane.
+ *
+ * Also, unlike java.awt.Dialogs, JDialogs support the
+ * Swing Pluggable Look & Feel architecture.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JDialog extends Dialog implements Accessible, WindowConstants,
+ RootPaneContainer
+{
+ /**
+ * Provides accessibility support for JDialogs.
+ */
+ protected class AccessibleJDialog extends Dialog.AccessibleAWTDialog
+ {
+ /**
+ * Creates a new instance of AccessibleJDialog.
+ */
+ protected AccessibleJDialog()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ private static final long serialVersionUID = -864070866424508218L;
+
+ /** DOCUMENT ME! */
+ protected AccessibleContext accessibleContext;
+
+ /** The single RootPane in the Dialog. */
+ protected JRootPane rootPane;
+
+ /**
+ * Whether checking is enabled on the RootPane.
+ *
+ * @specnote Should be false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /** The default action taken when closed. */
+ private int closeAction = HIDE_ON_CLOSE;
+
+ /** Whether JDialogs are decorated by the Look and Feel. */
+ private static boolean decorated;
+
+ /* Creates a new non-modal JDialog with no title
+ * using a shared Frame as the owner.
+ */
+ public JDialog()
+ {
+ this((Frame) SwingUtilities.getOwnerFrame(null), "", false, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog with no title
+ * using the given owner.
+ *
+ * @param owner The owner of the JDialog.
+ */
+ public JDialog(Dialog owner)
+ {
+ this(owner, "", false, null);
+ }
+
+ /**
+ * Creates a new JDialog with no title using the
+ * given modal setting and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Dialog owner, boolean modal)
+ {
+ this(owner, "", modal, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog using the
+ * given title and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ */
+ public JDialog(Dialog owner, String title)
+ {
+ this(owner, title, false, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Dialog owner, String title, boolean modal)
+ {
+ this(owner, title, modal, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, owner and graphics configuration.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ * @param gc The Graphics Configuration to use.
+ */
+ public JDialog(Dialog owner, String title, boolean modal,
+ GraphicsConfiguration gc)
+ {
+ super(owner, title, modal, gc);
+ dialogInit();
+ }
+
+ /**
+ * Creates a new non-modal JDialog with no title
+ * using the given owner.
+ *
+ * @param owner The owner of the JDialog.
+ */
+ public JDialog(Frame owner)
+ {
+ this(owner, "", false, null);
+ }
+
+ /**
+ * Creates a new JDialog with no title using the
+ * given modal setting and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Frame owner, boolean modal)
+ {
+ this(owner, "", modal, null);
+ }
+
+ /**
+ * Creates a new non-modal JDialog using the
+ * given title and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ */
+ public JDialog(Frame owner, String title)
+ {
+ this(owner, title, false, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, and owner.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ */
+ public JDialog(Frame owner, String title, boolean modal)
+ {
+ this(owner, title, modal, null);
+ }
+
+ /**
+ * Creates a new JDialog using the given modal
+ * settings, title, owner and graphics configuration.
+ *
+ * @param owner The owner of the JDialog.
+ * @param title The title of the JDialog.
+ * @param modal Whether the JDialog is modal.
+ * @param gc The Graphics Configuration to use.
+ */
+ public JDialog(Frame owner, String title, boolean modal,
+ GraphicsConfiguration gc)
+ {
+ super((Frame) SwingUtilities.getOwnerFrame(owner), title, modal, gc);
+ dialogInit();
+ }
+
+ /**
+ * This method is called to initialize the
+ * JDialog. It sets the layout used, the locale,
+ * and creates the RootPane.
+ */
+ protected void dialogInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+
+ // FIXME: Do a check on GraphicsEnvironment.isHeadless()
+ setLocale(JComponent.getDefaultLocale());
+ getRootPane(); // Will do set/create.
+ invalidate();
+ // Now that initStageDone is true, adds and layouts apply to contentPane,
+ // not top-level.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ /**
+ * This method returns whether JDialogs will have their
+ * window decorations provided by the Look and Feel.
+ *
+ * @return Whether the window decorations are Look and Feel provided.
+ */
+ public static boolean isDefaultLookAndFeelDecorated()
+ {
+ return decorated;
+ }
+
+ /**
+ * This method sets whether JDialogs will have their
+ * window decorations provided by the Look and Feel.
+ *
+ * @param defaultLookAndFeelDecorated Whether the window
+ * decorations are Look and Feel provided.
+ */
+ public static void setDefaultLookAndFeelDecorated(boolean defaultLookAndFeelDecorated)
+ {
+ decorated = defaultLookAndFeelDecorated;
+ }
+
+ /**
+ * This method returns the preferred size of
+ * the JDialog.
+ *
+ * @return The preferred size.
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension d = super.getPreferredSize();
+ return d;
+ }
+
+ /**
+ * This method returns the JMenuBar used
+ * in this JDialog.
+ *
+ * @return The JMenuBar in the JDialog.
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ /**
+ * This method sets the JMenuBar used
+ * in this JDialog.
+ *
+ * @param menubar The JMenuBar to use.
+ */
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ /**
+ * This method sets the LayoutManager used in the JDialog.
+ * This method will throw an Error if rootPaneChecking is
+ * enabled.
+ *
+ * @param manager The LayoutManager to use.
+ */
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ /**
+ * This method sets the JLayeredPane used in the JDialog.
+ * If the given JLayeredPane is null, then this method
+ * will throw an Error.
+ *
+ * @param layeredPane The JLayeredPane to use.
+ */
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ if (layeredPane == null)
+ throw new IllegalComponentStateException("layeredPane cannot be null.");
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ /**
+ * This method returns the JLayeredPane used with this JDialog.
+ *
+ * @return The JLayeredPane used with this JDialog.
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ /**
+ * This method returns the JRootPane used with this JDialog.
+ *
+ * @return The JRootPane used with this JDialog.
+ */
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ /**
+ * This method sets the JRootPane used with this JDialog.
+ *
+ * @param root The JRootPane to use.
+ */
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ rootPane.show();
+ add(rootPane);
+ }
+
+ /**
+ * This method creates a new JRootPane.
+ *
+ * @return A new JRootPane.
+ */
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ /**
+ * This method returns the ContentPane
+ * in the JRootPane.
+ *
+ * @return The ContentPane in the JRootPane.
+ */
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ /**
+ * This method sets the ContentPane to use with this
+ * JDialog. If the ContentPane given is null, this method
+ * will throw an exception.
+ *
+ * @param contentPane The ContentPane to use with the JDialog.
+ */
+ public void setContentPane(Container contentPane)
+ {
+ if (contentPane == null)
+ throw new IllegalComponentStateException("contentPane cannot be null.");
+ getRootPane().setContentPane(contentPane);
+ }
+
+ /**
+ * This method returns the GlassPane for this JDialog.
+ *
+ * @return The GlassPane for this JDialog.
+ */
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ /**
+ * This method sets the GlassPane for this JDialog.
+ *
+ * @param glassPane The GlassPane for this JDialog.
+ */
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ /**
+ * This method is called when a component is added to the
+ * the JDialog. Calling this method with rootPaneCheckingEnabled
+ * will cause an Error to be thrown.
+ *
+ * @param comp The component to add.
+ * @param constraints The constraints.
+ * @param index The position of the component.
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * This method removes a component from the JDialog.
+ *
+ * @param comp The component to remove.
+ */
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ /**
+ * This method returns whether rootPane checking is enabled.
+ *
+ * @return Whether rootPane checking is enabled.
+ */
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ /**
+ * This method sets whether rootPane checking is enabled.
+ *
+ * @param enabled Whether rootPane checking is enabled.
+ */
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ /**
+ * This method simply calls paint and returns.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+
+ /**
+ * This method handles window events. This allows the JDialog
+ * to honour its default close operation.
+ *
+ * @param e The WindowEvent.
+ */
+ protected void processWindowEvent(WindowEvent e)
+ {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ switch (closeAction)
+ {
+ case EXIT_ON_CLOSE:
+ System.exit(0);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DO_NOTHING_ON_CLOSE:
+ break;
+ }
+ }
+ }
+
+ /**
+ * This method sets the action to take
+ * when the JDialog is closed.
+ *
+ * @param operation The action to take.
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ /* Reference implementation allows invalid operations
+ to be specified. If so, getDefaultCloseOperation
+ must return the invalid code, and the behaviour
+ defaults to DO_NOTHING_ON_CLOSE. processWindowEvent
+ above handles this */
+ closeAction = operation;
+ }
+
+ /**
+ * This method returns the action taken when
+ * the JDialog is closed.
+ *
+ * @return The action to take.
+ */
+ public int getDefaultCloseOperation()
+ {
+ return closeAction;
+ }
+
+ /**
+ * This method returns a String describing the JDialog.
+ *
+ * @return A String describing the JDialog.
+ */
+ protected String paramString()
+ {
+ return "JDialog";
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDialog();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JEditorPane.java b/libjava/classpath/javax/swing/JEditorPane.java
new file mode 100644
index 000000000..8ad1095ee
--- /dev/null
+++ b/libjava/classpath/javax/swing/JEditorPane.java
@@ -0,0 +1,1222 @@
+/* JEditorPane.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Container;
+import java.awt.Dimension;
+import java.io.BufferedInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.HashMap;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleHyperlink;
+import javax.accessibility.AccessibleHypertext;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.event.HyperlinkListener;
+import javax.swing.plaf.TextUI;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.WrappedPlainView;
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.HTMLEditorKit;
+
+/**
+ * A powerful text editor component that can handle different types of
+ * content.
+ *
+ * The JEditorPane text component is driven by an instance of
+ * {@link EditorKit}. The editor kit is responsible for providing
+ * a default {@link Document} implementation, a mechanism for loading
+ * and saving documents of its supported content type and providing
+ * a set of {@link Action}s for manipulating the content.
+ *
+ * By default the following content types are supported:
+ *
+ *
text/plain: Plain text, handled by
+ * {@link javax.swing.text.DefaultEditorKit}.
+ *
text/html: HTML 4.0 styled text, handled by
+ * {@link javax.swing.text.html.HTMLEditorKit}.
+ *
text/rtf: RTF text, handled by
+ * {@link javax.swing.text.rtf.RTFEditorKit}.
+ *
+ *
+ * @author original author unknown
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ */
+public class JEditorPane extends JTextComponent
+{
+ /**
+ * Provides accessibility support for JEditorPane.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJEditorPane extends AccessibleJTextComponent
+ {
+
+ /**
+ * Creates a new AccessibleJEditorPane object.
+ */
+ protected AccessibleJEditorPane()
+ {
+ super();
+ }
+
+ /**
+ * Returns a description of this AccessibleJEditorPane. If
+ * this property is not set, then this returns the content-type of the
+ * editor pane.
+ *
+ * @return a description of this AccessibleJEditorPane
+ */
+ public String getAccessibleDescription()
+ {
+ String descr = super.getAccessibleDescription();
+ if (descr == null)
+ return getContentType();
+ else
+ return descr;
+ }
+
+ /**
+ * Returns the accessible state of this AccessibleJEditorPane.
+ *
+ * @return the accessible state of this AccessibleJEditorPane
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ /**
+ * Provides accessibility support for JEditorPanes, when the
+ * editor kit is an instance of {@link HTMLEditorKit}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJEditorPaneHTML extends AccessibleJEditorPane
+ {
+ /**
+ * Returns the accessible text of the JEditorPane. This will
+ * be an instance of
+ * {@link JEditorPaneAccessibleHypertextSupport}.
+ *
+ * @return the accessible text of the JEditorPane
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return new JEditorPaneAccessibleHypertextSupport();
+ }
+ }
+
+ /**
+ * This is the accessible text that is returned by
+ * {@link AccessibleJEditorPaneHTML#getAccessibleText()}.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class JEditorPaneAccessibleHypertextSupport
+ extends AccessibleJEditorPane implements AccessibleHypertext
+ {
+
+ /**
+ * Creates a new JEditorPaneAccessibleHypertextSupport object.
+ */
+ public JEditorPaneAccessibleHypertextSupport()
+ {
+ super();
+ }
+
+ /**
+ * The accessible representation of a HTML link.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public class HTMLLink extends AccessibleHyperlink
+ {
+
+ /**
+ * The element in the document that represents the link.
+ */
+ Element element;
+
+ /**
+ * Creates a new HTMLLink.
+ *
+ * @param el the link element
+ */
+ public HTMLLink(Element el)
+ {
+ this.element = el;
+ }
+
+ /**
+ * Returns true if this HTMLLink is still
+ * valid. A HTMLLink can become invalid when the document
+ * changes.
+ *
+ * @return true if this HTMLLink is still
+ * valid
+ */
+ public boolean isValid()
+ {
+ // I test here if the element at our element's start offset is the
+ // same as the element in the document at this offset. If this is true,
+ // I consider the link valid, if not, then this link no longer
+ // represented by this HTMLLink and therefor invalid.
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ return doc.getCharacterElement(element.getStartOffset()) == element;
+ }
+
+ /**
+ * Returns the number of AccessibleActions in this link object. In
+ * general, link have 1 AccessibleAction associated with them. There are
+ * special cases where links can have multiple actions associated, like
+ * in image maps.
+ *
+ * @return the number of AccessibleActions in this link object
+ */
+ public int getAccessibleActionCount()
+ {
+ // TODO: Implement the special cases.
+ return 1;
+ }
+
+ /**
+ * Performs the specified action on the link object. This ususally means
+ * activating the link.
+ *
+ * @return true if the action has been performed
+ * successfully, false otherwise
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ String href = (String) element.getAttributes().getAttribute("href");
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ URL url = new URL(doc.getBase(), href);
+ setPage(url);
+ String desc = doc.getText(element.getStartOffset(),
+ element.getEndOffset() - element.getStartOffset());
+ HyperlinkEvent ev =
+ new HyperlinkEvent(JEditorPane.this,
+ HyperlinkEvent.EventType.ACTIVATED, url, desc,
+ element);
+ fireHyperlinkUpdate(ev);
+ return true;
+ }
+ catch (Exception ex)
+ {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the description of the action at action index i.
+ * This method returns the text within the element associated with this
+ * link.
+ *
+ * @param i the action index
+ *
+ * @return the description of the action at action index i
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ return doc.getText(element.getStartOffset(),
+ element.getEndOffset() - element.getStartOffset());
+ }
+ catch (BadLocationException ex)
+ {
+ throw (AssertionError)
+ new AssertionError("BadLocationException must not be thrown "
+ + "here.")
+ .initCause(ex);
+ }
+ }
+
+ /**
+ * Returns an {@link URL} object, that represents the action at action
+ * index i.
+ *
+ * @param i the action index
+ *
+ * @return an {@link URL} object, that represents the action at action
+ * index i
+ */
+ public Object getAccessibleActionObject(int i)
+ {
+ String href = (String) element.getAttributes().getAttribute("href");
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ try
+ {
+ URL url = new URL(doc.getBase(), href);
+ return url;
+ }
+ catch (MalformedURLException ex)
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Returns an object that represents the link anchor. For examples, if
+ * the link encloses a string, then a String object is
+ * returned, if the link encloses an <img> tag, then an
+ * ImageIcon object is returned.
+ *
+ * @return an object that represents the link anchor
+ */
+ public Object getAccessibleActionAnchor(int i)
+ {
+ // TODO: This is only the String case. Implement all cases.
+ return getAccessibleActionDescription(i);
+ }
+
+ /**
+ * Returns the start index of the hyperlink element.
+ *
+ * @return the start index of the hyperlink element
+ */
+ public int getStartIndex()
+ {
+ return element.getStartOffset();
+ }
+
+ /**
+ * Returns the end index of the hyperlink element.
+ *
+ * @return the end index of the hyperlink element
+ */
+ public int getEndIndex()
+ {
+ return element.getEndOffset();
+ }
+
+ }
+
+ /**
+ * Returns the number of hyperlinks in the document.
+ *
+ * @return the number of hyperlinks in the document
+ */
+ public int getLinkCount()
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ linkIter.next();
+ }
+ return count;
+ }
+
+ /**
+ * Returns the i-th hyperlink in the document or
+ * null if there is no hyperlink with the specified index.
+ *
+ * @param i the index of the hyperlink to return
+ *
+ * @return the i-th hyperlink in the document or
+ * null if there is no hyperlink with the specified
+ * index
+ */
+ public AccessibleHyperlink getLink(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ if (count == i)
+ break;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ {
+ int offset = linkIter.getStartOffset();
+ // TODO: I fetch the element for the link via getCharacterElement().
+ // I am not sure that this is correct, maybe we must use
+ // getParagraphElement()?
+ Element el = doc.getCharacterElement(offset);
+ HTMLLink link = new HTMLLink(el);
+ return link;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Returns the index of the link element at the character position
+ * c within the document, or -1 if there is no
+ * link at the specified position.
+ *
+ * @param c the character index from which to fetch the link index
+ *
+ * @return the index of the link element at the character position
+ * c within the document, or -1 if there
+ * is no link at the specified position
+ */
+ public int getLinkIndex(int c)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ if (linkIter.getStartOffset() <= c && linkIter.getEndOffset() > c)
+ break;
+ count++;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ return count;
+ else
+ return -1;
+ }
+
+ /**
+ * Returns the link text of the link at index i, or
+ * null, if there is no link at the specified position.
+ *
+ * @param i the index of the link
+ *
+ * @return the link text of the link at index i, or
+ * null, if there is no link at the specified
+ * position
+ */
+ public String getLinkText(int i)
+ {
+ HTMLDocument doc = (HTMLDocument) getDocument();
+ HTMLDocument.Iterator linkIter = doc.getIterator(HTML.Tag.A);
+ int count = 0;
+ while (linkIter.isValid())
+ {
+ count++;
+ if (count == i)
+ break;
+ linkIter.next();
+ }
+ if (linkIter.isValid())
+ {
+ int offset = linkIter.getStartOffset();
+ // TODO: I fetch the element for the link via getCharacterElement().
+ // I am not sure that this is correct, maybe we must use
+ // getParagraphElement()?
+ Element el = doc.getCharacterElement(offset);
+ try
+ {
+ String text = doc.getText(el.getStartOffset(),
+ el.getEndOffset() - el.getStartOffset());
+ return text;
+ }
+ catch (BadLocationException ex)
+ {
+ throw (AssertionError)
+ new AssertionError("BadLocationException must not be thrown "
+ + "here.")
+ .initCause(ex);
+ }
+ }
+ else
+ return null;
+ }
+ }
+
+ /**
+ * Used to store a mapping for content-type to editor kit class.
+ */
+ private static class EditorKitMapping
+ {
+ /**
+ * The classname of the editor kit.
+ */
+ String className;
+
+ /**
+ * The classloader with which the kit is to be loaded.
+ */
+ ClassLoader classLoader;
+
+ /**
+ * Creates a new EditorKitMapping object.
+ *
+ * @param cn the classname
+ * @param cl the classloader
+ */
+ EditorKitMapping(String cn, ClassLoader cl)
+ {
+ className = cn;
+ classLoader = cl;
+ }
+ }
+
+ /**
+ * An EditorKit used for plain text. This is the default editor kit for
+ * JEditorPanes.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ private static class PlainEditorKit extends DefaultEditorKit
+ {
+
+ /**
+ * Returns a ViewFactory that supplies WrappedPlainViews.
+ */
+ public ViewFactory getViewFactory()
+ {
+ return new ViewFactory()
+ {
+ public View create(Element el)
+ {
+ return new WrappedPlainView(el);
+ }
+ };
+ }
+ }
+
+ /**
+ * A special stream that can be cancelled.
+ */
+ private class PageStream
+ extends FilterInputStream
+ {
+ /**
+ * True when the stream has been cancelled, false otherwise.
+ */
+ private boolean cancelled;
+
+ protected PageStream(InputStream in)
+ {
+ super(in);
+ cancelled = false;
+ }
+
+ private void checkCancelled()
+ throws IOException
+ {
+ if (cancelled)
+ throw new IOException("Stream has been cancelled");
+ }
+
+ void cancel()
+ {
+ cancelled = true;
+ }
+
+ public int read()
+ throws IOException
+ {
+ checkCancelled();
+ return super.read();
+ }
+
+ public int read(byte[] b, int off, int len)
+ throws IOException
+ {
+ checkCancelled();
+ return super.read(b, off, len);
+ }
+
+ public long skip(long n)
+ throws IOException
+ {
+ checkCancelled();
+ return super.skip(n);
+ }
+
+ public int available()
+ throws IOException
+ {
+ checkCancelled();
+ return super.available();
+ }
+
+ public void reset()
+ throws IOException
+ {
+ checkCancelled();
+ super.reset();
+ }
+ }
+
+ /**
+ * The thread that loads documents asynchronously.
+ */
+ private class PageLoader
+ implements Runnable
+ {
+ private Document doc;
+ private PageStream in;
+ private URL old;
+ URL page;
+ PageLoader(Document doc, InputStream in, URL old, URL page)
+ {
+ this.doc = doc;
+ this.in = new PageStream(in);
+ this.old = old;
+ this.page = page;
+ }
+
+ public void run()
+ {
+ try
+ {
+ read(in, doc);
+ }
+ catch (IOException ex)
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(JEditorPane.this);
+ }
+ finally
+ {
+ if (SwingUtilities.isEventDispatchThread())
+ firePropertyChange("page", old, page);
+ else
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ firePropertyChange("page", old, page);
+ }
+ });
+ }
+ }
+ }
+
+ void cancel()
+ {
+ in.cancel();
+ }
+ }
+
+ private static final long serialVersionUID = 3140472492599046285L;
+
+ private EditorKit editorKit;
+
+ boolean focus_root;
+
+ /**
+ * Maps content-types to editor kit instances.
+ */
+ static HashMap editorKits;
+
+ // A mapping between content types and registered EditorKit types
+ static HashMap registerMap;
+
+ static
+ {
+ registerMap = new HashMap();
+ editorKits = new HashMap();
+ registerEditorKitForContentType("application/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+ registerEditorKitForContentType("text/plain",
+ "javax.swing.JEditorPane$PlainEditorKit");
+ registerEditorKitForContentType("text/html",
+ "javax.swing.text.html.HTMLEditorKit");
+ registerEditorKitForContentType("text/rtf",
+ "javax.swing.text.rtf.RTFEditorKit");
+
+ }
+
+ // A mapping between content types and used EditorKits
+ HashMap editorMap;
+
+ /**
+ * The currently loading stream, if any.
+ */
+ private PageLoader loader;
+
+ public JEditorPane()
+ {
+ init();
+ setEditorKit(createDefaultEditorKit());
+ }
+
+ public JEditorPane(String url) throws IOException
+ {
+ this(new URL(url));
+ }
+
+ public JEditorPane(String type, String text)
+ {
+ init();
+ setEditorKit(createEditorKitForContentType(type));
+ setText(text);
+ }
+
+ public JEditorPane(URL url) throws IOException
+ {
+ init();
+ setEditorKit(createEditorKitForContentType("text/html"));
+ setPage(url);
+ }
+
+ /**
+ * Called by the constructors to set up the default bindings for content
+ * types and EditorKits.
+ */
+ void init()
+ {
+ editorMap = new HashMap();
+ }
+
+ protected EditorKit createDefaultEditorKit()
+ {
+ return new PlainEditorKit();
+ }
+
+ /**
+ * Creates and returns an EditorKit that is appropriate for the given
+ * content type. This is created using the default recognized types
+ * plus any EditorKit types that have been registered.
+ *
+ * @see #registerEditorKitForContentType(String, String)
+ * @see #registerEditorKitForContentType(String, String, ClassLoader)
+ * @param type the content type
+ * @return an EditorKit for use with the given content type
+ */
+ public static EditorKit createEditorKitForContentType(String type)
+ {
+ // Try cached instance.
+ EditorKit e = (EditorKit) editorKits.get(type);
+ if (e == null)
+ {
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ if (m != null)
+ {
+ String className = m.className;
+ ClassLoader loader = m.classLoader;
+ try
+ {
+ e = (EditorKit) loader.loadClass(className).newInstance();
+ }
+ catch (Exception e2)
+ {
+ // The reference implementation returns null when class is not
+ // loadable or instantiatable.
+ }
+ }
+ // Cache this for later retrieval.
+ if (e != null)
+ editorKits.put(type, e);
+ }
+ return e;
+ }
+
+ /**
+ * Sends a given HyperlinkEvent to all registered listeners.
+ *
+ * @param event the event to send
+ */
+ public void fireHyperlinkUpdate(HyperlinkEvent event)
+ {
+ HyperlinkListener[] listeners = getHyperlinkListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].hyperlinkUpdate(event);
+ }
+
+ /**
+ * Returns the accessible context associated with this editor pane.
+ *
+ * @return the accessible context associated with this editor pane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ if (getEditorKit() instanceof HTMLEditorKit)
+ accessibleContext = new AccessibleJEditorPaneHTML();
+ else
+ accessibleContext = new AccessibleJEditorPane();
+ }
+ return accessibleContext;
+ }
+
+ public final String getContentType()
+ {
+ return getEditorKit().getContentType();
+ }
+
+ /**
+ * Returns the EditorKit. If there is no EditorKit set this method
+ * calls createDefaultEditorKit() and setEditorKit() first.
+ */
+ public EditorKit getEditorKit()
+ {
+ if (editorKit == null)
+ setEditorKit(createDefaultEditorKit());
+ return editorKit;
+ }
+
+ /**
+ * Returns the class name of the EditorKit associated with the given
+ * content type.
+ *
+ * @since 1.3
+ * @param type the content type
+ * @return the class name of the EditorKit associated with this content type
+ */
+ public static String getEditorKitClassNameForContentType(String type)
+ {
+ EditorKitMapping m = (EditorKitMapping) registerMap.get(type);
+ String kitName = m != null ? m.className : null;
+ return kitName;
+ }
+
+ /**
+ * Returns the EditorKit to use for the given content type. If an
+ * EditorKit has been explicitly set via
+ * setEditorKitForContentType
+ * then it will be returned. Otherwise an attempt will be made to create
+ * an EditorKit from the default recognzied content types or any
+ * EditorKits that have been registered. If none can be created, a
+ * PlainEditorKit is created.
+ *
+ * @see #registerEditorKitForContentType(String, String)
+ * @see #registerEditorKitForContentType(String, String, ClassLoader)
+ * @param type the content type
+ * @return an appropriate EditorKit for the given content type
+ */
+ public EditorKit getEditorKitForContentType(String type)
+ {
+ // First check if an EditorKit has been explicitly set.
+ EditorKit e = (EditorKit) editorMap.get(type);
+ // Then check to see if we can create one.
+ if (e == null)
+ {
+ e = createEditorKitForContentType(type);
+ if (e != null)
+ setEditorKitForContentType(type, e);
+ }
+ // Otherwise default to PlainEditorKit.
+ if (e == null)
+ e = createDefaultEditorKit();
+ return e;
+ }
+
+ /**
+ * Returns the preferred size for the JEditorPane. This is implemented to
+ * return the super's preferred size, unless one of
+ * {@link #getScrollableTracksViewportHeight()} or
+ * {@link #getScrollableTracksViewportWidth()} returns true,
+ * in which case the preferred width and/or height is replaced by the UI's
+ * minimum size.
+ *
+ * @return the preferred size for the JEditorPane
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension pref = super.getPreferredSize();
+ Container parent = getParent();
+ if (parent instanceof JViewport)
+ {
+ JViewport vp = (JViewport) getParent();
+ TextUI ui = getUI();
+ Dimension min = null;
+ if (! getScrollableTracksViewportWidth())
+ {
+ min = ui.getMinimumSize(this);
+ int vpWidth = vp.getWidth();
+ if (vpWidth != 0 && vpWidth < min.width)
+ pref.width = min.width;
+ }
+ if (! getScrollableTracksViewportHeight())
+ {
+ if (min == null)
+ min = ui.getMinimumSize(this);
+ int vpHeight = vp.getHeight();
+ if (vpHeight != 0 && vpHeight < min.height)
+ pref.height = min.height;
+ }
+ }
+ return pref;
+ }
+
+ /**
+ * Returns true when a Viewport should force the height of
+ * this component to match the viewport height. This is implemented to return
+ * true when the parent is an instance of JViewport and
+ * the viewport height > the UI's minimum height.
+ *
+ * @return true when a Viewport should force the height of
+ * this component to match the viewport height
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ // Tests show that this returns true when the parent is a JViewport
+ // and has a height > minimum UI height.
+ Container parent = getParent();
+ int height = parent.getHeight();
+ TextUI ui = getUI();
+ return parent instanceof JViewport
+ && height >= ui.getMinimumSize(this).height
+ && height <= ui.getMaximumSize(this).height;
+ }
+
+ /**
+ * Returns true when a Viewport should force the width of
+ * this component to match the viewport width. This is implemented to return
+ * true when the parent is an instance of JViewport and
+ * the viewport width > the UI's minimum width.
+ *
+ * @return true when a Viewport should force the width of
+ * this component to match the viewport width
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ // Tests show that this returns true when the parent is a JViewport
+ // and has a width > minimum UI width.
+ Container parent = getParent();
+ return parent != null && parent instanceof JViewport
+ && parent.getWidth() > getUI().getMinimumSize(this).width;
+ }
+
+ public URL getPage()
+ {
+ return loader != null ? loader.page : null;
+ }
+
+ protected InputStream getStream(URL page)
+ throws IOException
+ {
+ URLConnection conn = page.openConnection();
+ // Try to detect the content type of the stream data.
+ String type = conn.getContentType();
+ if (type != null)
+ setContentType(type);
+ InputStream stream = conn.getInputStream();
+ return new BufferedInputStream(stream);
+ }
+
+ public String getText()
+ {
+ return super.getText();
+ }
+
+ public String getUIClassID()
+ {
+ return "EditorPaneUI";
+ }
+
+ public boolean isFocusCycleRoot()
+ {
+ return focus_root;
+ }
+
+ protected String paramString()
+ {
+ return "JEditorPane";
+ }
+
+ /**
+ * This method initializes from a stream.
+ */
+ public void read(InputStream in, Object desc) throws IOException
+ {
+ EditorKit kit = getEditorKit();
+ if (kit instanceof HTMLEditorKit && desc instanceof HTMLDocument)
+ {
+ HTMLDocument doc = (HTMLDocument) desc;
+ setDocument(doc);
+ try
+ {
+ InputStreamReader reader = new InputStreamReader(in);
+ kit.read(reader, doc, 0);
+ }
+ catch (BadLocationException ex)
+ {
+ assert false : "BadLocationException must not be thrown here.";
+ }
+ }
+ else
+ {
+ Reader inRead = new InputStreamReader(in);
+ super.read(inRead, desc);
+ }
+ }
+
+ /**
+ * Establishes a binding between type and classname. This enables
+ * us to create an EditorKit later for the given content type.
+ *
+ * @param type the content type
+ * @param classname the name of the class that is associated with this
+ * content type
+ */
+ public static void registerEditorKitForContentType(String type,
+ String classname)
+ {
+ registerEditorKitForContentType(type, classname,
+ Thread.currentThread().getContextClassLoader());
+ }
+
+ /**
+ * Establishes the default bindings of type to classname.
+ */
+ public static void registerEditorKitForContentType(String type,
+ String classname,
+ ClassLoader loader)
+ {
+ registerMap.put(type, new EditorKitMapping(classname, loader));
+ }
+
+ /**
+ * Replaces the currently selected content with new content represented
+ * by the given string.
+ */
+ public void replaceSelection(String content)
+ {
+ // TODO: Implement this properly.
+ super.replaceSelection(content);
+ }
+
+ /**
+ * Scrolls the view to the given reference location (that is, the value
+ * returned by the UL.getRef method for the URL being displayed).
+ */
+ public void scrollToReference(String reference)
+ {
+ // TODO: Implement this properly.
+ }
+
+ public final void setContentType(String type)
+ {
+ // Strip off content type parameters.
+ int paramIndex = type.indexOf(';');
+ if (paramIndex > -1)
+ {
+ // TODO: Handle character encoding.
+ type = type.substring(0, paramIndex).trim();
+ }
+ if (editorKit != null
+ && editorKit.getContentType().equals(type))
+ return;
+
+ EditorKit kit = getEditorKitForContentType(type);
+
+ if (kit != null)
+ setEditorKit(kit);
+ }
+
+ public void setEditorKit(EditorKit newValue)
+ {
+ if (editorKit == newValue)
+ return;
+
+ if (editorKit != null)
+ editorKit.deinstall(this);
+
+ EditorKit oldValue = editorKit;
+ editorKit = newValue;
+
+ if (editorKit != null)
+ {
+ editorKit.install(this);
+ setDocument(editorKit.createDefaultDocument());
+ }
+
+ firePropertyChange("editorKit", oldValue, newValue);
+ invalidate();
+ repaint();
+ // Reset the accessibleContext since this depends on the editorKit.
+ accessibleContext = null;
+ }
+
+ /**
+ * Explicitly sets an EditorKit to be used for the given content type.
+ * @param type the content type
+ * @param k the EditorKit to use for the given content type
+ */
+ public void setEditorKitForContentType(String type, EditorKit k)
+ {
+ editorMap.put(type, k);
+ }
+
+ /**
+ * Sets the current URL being displayed.
+ */
+ public void setPage(String url) throws IOException
+ {
+ setPage(new URL(url));
+ }
+
+ /**
+ * Sets the current URL being displayed.
+ */
+ public void setPage(URL page) throws IOException
+ {
+ if (page == null)
+ throw new IOException("invalid url");
+
+ URL old = getPage();
+ // Only reload if the URL doesn't point to the same file.
+ // This is not the same as equals because there might be different
+ // URLs on the same file with different anchors.
+ if (old == null || ! old.sameFile(page))
+ {
+ InputStream in = getStream(page);
+ if (editorKit != null)
+ {
+ Document doc = editorKit.createDefaultDocument();
+ doc.putProperty(Document.StreamDescriptionProperty, page);
+
+ if (loader != null)
+ loader.cancel();
+ loader = new PageLoader(doc, in, old, page);
+
+ int prio = -1;
+ if (doc instanceof AbstractDocument)
+ {
+ AbstractDocument aDoc = (AbstractDocument) doc;
+ prio = aDoc.getAsynchronousLoadPriority();
+ }
+ if (prio >= 0)
+ {
+ // Load asynchronously.
+ setDocument(doc);
+ Thread loadThread = new Thread(loader,
+ "JEditorPane.PageLoader");
+ loadThread.setDaemon(true);
+ loadThread.setPriority(prio);
+ loadThread.start();
+ }
+ else
+ {
+ // Load synchronously.
+ loader.run();
+ setDocument(doc);
+ }
+ }
+ }
+ }
+
+ /**
+ * Sets the text of the JEditorPane. The argument t
+ * is expected to be in the format of the current EditorKit. This removes
+ * the content of the current document and uses the EditorKit to read in the
+ * new text. This allows the EditorKit to handle the String rather than just
+ * inserting in plain text.
+ *
+ * @param t the text to display in this JEditorPane
+ */
+ public void setText(String t)
+ {
+ try
+ {
+ // Remove the current content.
+ Document doc = getDocument();
+ doc.remove(0, doc.getLength());
+ if (t == null || t.equals(""))
+ return;
+
+ // Let the EditorKit read the text into the Document.
+ getEditorKit().read(new StringReader(t), doc, 0);
+ }
+ catch (BadLocationException ble)
+ {
+ // TODO: Don't know what to do here.
+ }
+ catch (IOException ioe)
+ {
+ // TODO: Don't know what to do here.
+ }
+ }
+
+ /**
+ * Add a HyperlinkListener object to this editor pane.
+ *
+ * @param listener the listener to add
+ */
+ public void addHyperlinkListener(HyperlinkListener listener)
+ {
+ listenerList.add(HyperlinkListener.class, listener);
+ }
+
+ /**
+ * Removes a HyperlinkListener object to this editor pane.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeHyperlinkListener(HyperlinkListener listener)
+ {
+ listenerList.remove(HyperlinkListener.class, listener);
+ }
+
+ /**
+ * Returns all added HyperlinkListener objects.
+ *
+ * @return array of listeners
+ *
+ * @since 1.4
+ */
+ public HyperlinkListener[] getHyperlinkListeners()
+ {
+ return (HyperlinkListener[]) getListeners(HyperlinkListener.class);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFileChooser.java b/libjava/classpath/javax/swing/JFileChooser.java
new file mode 100644
index 000000000..61b2fde73
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFileChooser.java
@@ -0,0 +1,1626 @@
+/* JFileChooser.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Frame;
+import java.awt.GraphicsEnvironment;
+import java.awt.HeadlessException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowAdapter;
+import java.beans.PropertyChangeEvent;
+import java.io.File;
+import java.util.ArrayList;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.filechooser.FileSystemView;
+import javax.swing.filechooser.FileView;
+import javax.swing.plaf.FileChooserUI;
+
+
+/**
+ * A component that provides the user a dialog box to browse through a
+ * filesystem and choose one or more files or directories.
+ *
+ * A JFileChooser can be configured to filter the displayed file list
+ * by adding a {@link FileFilter} instance using
+ * {@link #addChoosableFileFilter(FileFilter)}. Additional components can
+ * be embedded in the file chooser using {@link #setAccessory(JComponent)}.
+ * The JFileChooser properties also provide mechanisms to customize the
+ * behaviour of the file chooser.
+ *
+ * @author Kim Ho (kho@luxsci.net)
+ */
+public class JFileChooser extends JComponent implements Accessible
+{
+ private static final long serialVersionUID = 3162921138695327837L;
+
+ /**
+ * A dialog type for selecting a file to open.
+ * @see #setDialogType(int)
+ */
+ public static final int OPEN_DIALOG = 0;
+
+ /**
+ * A dialog type for selecting a file to save.
+ * @see #setDialogType(int)
+ */
+ public static final int SAVE_DIALOG = 1;
+
+ /**
+ * A dialog type for some custom purpose.
+ * @see #setDialogType(int)
+ */
+ public static final int CUSTOM_DIALOG = 2;
+
+ /**
+ * A return value indicating the file chooser has been closed by cancelling.
+ *
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int CANCEL_OPTION = 1;
+
+ /**
+ * A return value indicating the file chooser has been closed by approving
+ * the selection.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int APPROVE_OPTION = 0;
+
+ /**
+ * A return value indicating the file chooser has been closed by some error.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ public static final int ERROR_OPTION = -1;
+
+ /**
+ * A selection mode constant indicating acceptance of files only.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int FILES_ONLY = 0;
+
+ /**
+ * A selection mode constant indicating acceptance of directories only.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int DIRECTORIES_ONLY = 1;
+
+ /**
+ * A selection mode constant indicating acceptance of files and directories.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final int FILES_AND_DIRECTORIES = 2;
+
+ /**
+ * Action command string for cancelling the current selection.
+ * @see #cancelSelection()
+ */
+ public static final String CANCEL_SELECTION = "CancelSelection";
+
+ /**
+ * Action command string for approving the current selection.
+ * @see #cancelSelection()
+ */
+ public static final String APPROVE_SELECTION = "ApproveSelection";
+
+ /**
+ * The name of the property for the approve button text.
+ * @see #setApproveButtonText(String)
+ */
+ public static final String APPROVE_BUTTON_TEXT_CHANGED_PROPERTY =
+ "ApproveButtonTextChangedProperty";
+
+ /**
+ * The name of the property for the approve button tool tip text.
+ * @see #setApproveButtonToolTipText(String)
+ */
+ public static final String APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY =
+ "ApproveButtonToolTipTextChangedProperty";
+
+ /**
+ * The name of the property for the approve button mnemonic.
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public static final String APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY =
+ "ApproveButtonMnemonicChangedProperty";
+
+ /**
+ * The name of the property for control button visibility.
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ public static final String CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY =
+ "ControlButtonsAreShownChangedProperty";
+
+ /**
+ * The name of the property for the current directory.
+ * @see #setCurrentDirectory(File)
+ */
+ public static final String DIRECTORY_CHANGED_PROPERTY = "directoryChanged";
+
+ /**
+ * The name of the property for the selected file.
+ * @see #setSelectedFile(File)
+ */
+ public static final String SELECTED_FILE_CHANGED_PROPERTY =
+ "SelectedFileChangedProperty";
+
+ /**
+ * The name of the property for the selected files.
+ * @see #setSelectedFiles(File[])
+ */
+ public static final String SELECTED_FILES_CHANGED_PROPERTY =
+ "SelectedFilesChangedProperty";
+
+ /**
+ * The name of the property for multi-selection.
+ * @see #setMultiSelectionEnabled(boolean)
+ */
+ public static final String MULTI_SELECTION_ENABLED_CHANGED_PROPERTY =
+ "MultiSelectionEnabledChangedProperty";
+
+ /**
+ * The name of the 'file system view' property.
+ * @see #setFileSystemView(FileSystemView)
+ */
+ public static final String FILE_SYSTEM_VIEW_CHANGED_PROPERTY =
+ "FileSystemViewChanged";
+
+ /**
+ * The name of the 'file view' property.
+ * @see #setFileView(FileView)
+ */
+ public static final String FILE_VIEW_CHANGED_PROPERTY = "fileViewChanged";
+
+ /**
+ * The name of the 'file hiding enabled' property.
+ * @see #setFileHidingEnabled(boolean)
+ */
+ public static final String FILE_HIDING_CHANGED_PROPERTY =
+ "FileHidingChanged";
+
+ /**
+ * The name of the 'file filter' property.
+ * @see #setFileFilter(FileFilter)
+ */
+ public static final String FILE_FILTER_CHANGED_PROPERTY =
+ "fileFilterChanged";
+
+ /**
+ * The name of the 'file selection mode' property.
+ * @see #setFileSelectionMode(int)
+ */
+ public static final String FILE_SELECTION_MODE_CHANGED_PROPERTY =
+ "fileSelectionChanged";
+
+ /**
+ * The name of the 'accessory' property.
+ * @see #setAccessory(JComponent)
+ */
+ public static final String ACCESSORY_CHANGED_PROPERTY =
+ "AccessoryChangedProperty";
+
+ /**
+ * The name of the 'accept all file filter used' property.
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ public static final String ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY =
+ "acceptAllFileFilterUsedChanged";
+
+ /**
+ * The name of the 'dialog title' property.
+ * @see #setDialogTitle(String)
+ */
+ public static final String DIALOG_TITLE_CHANGED_PROPERTY =
+ "DialogTitleChangedProperty";
+
+ /**
+ * The name of the 'dialog type' property.
+ * @see #setDialogType(int)
+ */
+ public static final String DIALOG_TYPE_CHANGED_PROPERTY =
+ "DialogTypeChangedProperty";
+
+ /**
+ * The name of the 'choosable file filters' property.
+ * @see #addChoosableFileFilter(FileFilter)
+ */
+ public static final String CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY =
+ "ChoosableFileFilterChangedProperty";
+
+ /**
+ * The accessible context.
+ * @see #getAccessibleContext()
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * The file system view.
+ * @see #setFileSystemView(FileSystemView)
+ */
+ private FileSystemView fsv;
+
+ /**
+ * The accessory component.
+ * @see #setAccessory(JComponent)
+ */
+ private JComponent accessory;
+
+ /**
+ * The approve button mnemonic.
+ * @see #setApproveButtonMnemonic(int)
+ */
+ private int approveButtonMnemonic = 0;
+
+ /**
+ * The approve button text.
+ * @see #setApproveButtonText(String)
+ */
+ private String approveButtonText;
+
+ /**
+ * The approve button tool tip text.
+ * @see #setApproveButtonToolTipText(String)
+ */
+ private String approveButtonToolTipText;
+
+ /**
+ * The choosable file filters.
+ * @see #addChoosableFileFilter(FileFilter)
+ */
+ private ArrayList choosableFilters = new ArrayList();
+
+ /**
+ * A flag controlling whether the accept all file filter is used.
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ private boolean isAcceptAll = true;
+
+ /**
+ * The dialog title.
+ * @see #setDialogTitle(String)
+ */
+ private String dialogTitle;
+
+ /**
+ * The dialog type.
+ * @see #setDialogType(int)
+ */
+ private int dialogType = OPEN_DIALOG;
+
+ /**
+ * The return value for the dialog.
+ * @see #showOpenDialog(Component)
+ * @see #showSaveDialog(Component)
+ */
+ private int retval = ERROR_OPTION;
+
+ /**
+ * A flag indicating whether the file chooser allows multiple selection.
+ * @see #isMultiSelectionEnabled()
+ */
+ private boolean multiSelection = false;
+
+ /**
+ * A flag indicating whether file hiding is enabled.
+ * @see #isFileHidingEnabled()
+ */
+ private boolean fileHiding = true;
+
+ /**
+ * The file selection mode.
+ * @see #setFileSelectionMode(int)
+ */
+ private int fileSelectionMode = FILES_ONLY;
+
+ /**
+ * The file view.
+ * @see #setFileView(FileView)
+ */
+ private FileView fv = null;
+
+ /**
+ * A flag controlling whether or not the control buttons are visible.
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ private boolean controlButtonsShown = true;
+
+ /**
+ * The current directory.
+ * @see #setCurrentDirectory(File)
+ */
+ private File currentDir = null;
+
+ /**
+ * The current file filter.
+ * @see #setFileFilter(FileFilter)
+ */
+ private FileFilter currentFilter = null;
+
+ /**
+ * An array of selected files.
+ * @see #setSelectedFiles(File[])
+ */
+ private File[] selectedFiles;
+
+ /**
+ * The selected file.
+ * @see #setSelectedFile(File)
+ */
+ private File selectedFile;
+
+ /**
+ * The drag enabled property.
+ * @see #setDragEnabled(boolean)
+ * @see #getDragEnabled()
+ */
+ private boolean dragEnabled;
+
+ /**
+ * Creates a new JFileChooser object.
+ */
+ public JFileChooser()
+ {
+ setup(null);
+ setCurrentDirectory(null);
+ }
+
+ /**
+ * Creates a new JFileChooser object.
+ *
+ * @param currentDirectoryPath the directory that should initially be
+ * shown in the filechooser (if null, the user's home
+ * directory is used).
+ */
+ public JFileChooser(String currentDirectoryPath)
+ {
+ this(currentDirectoryPath, null);
+ }
+
+ /**
+ * Creates a new JFileChooser object with the specified
+ * directory and {@link FileSystemView}.
+ *
+ * @param currentDirectoryPath the directory that should initially be
+ * shown in the filechooser (if null, the user's home
+ * directory is used).
+ * @param fsv the file system view (if null, the default file
+ * system view is used).
+ */
+ public JFileChooser(String currentDirectoryPath, FileSystemView fsv)
+ {
+ setup(fsv);
+ File dir = null;
+ if (currentDirectoryPath != null)
+ dir = getFileSystemView().createFileObject(currentDirectoryPath);
+ setCurrentDirectory(dir);
+ }
+
+ /**
+ * Creates a new JFileChooser object.
+ *
+ * @param currentDirectory the directory that should initially be
+ * shown in the filechooser (if null, the user's home
+ * directory is used).
+ */
+ public JFileChooser(File currentDirectory)
+ {
+ setup(null);
+ setCurrentDirectory(currentDirectory);
+ }
+
+ /**
+ * Creates a new JFileChooser object.
+ *
+ * @param fsv the file system view (if null, the default file
+ * system view is used).
+ */
+ public JFileChooser(FileSystemView fsv)
+ {
+ setup(fsv);
+ setCurrentDirectory(null);
+ }
+
+ /**
+ * Creates a new JFileChooser object.
+ *
+ * @param currentDirectory the directory that should initially be
+ * shown in the filechooser (if null, the user's home
+ * directory is used).
+ * @param fsv the file system view (if null, the default file
+ * system view is used).
+ */
+ public JFileChooser(File currentDirectory, FileSystemView fsv)
+ {
+ setup(fsv);
+ setCurrentDirectory(currentDirectory);
+ }
+
+ /**
+ * Sets up the file chooser. This method is called by all the constructors.
+ *
+ * @param view the file system view (if null, the default file
+ * system view is used).
+ *
+ * @see FileSystemView#getFileSystemView()
+ */
+ protected void setup(FileSystemView view)
+ {
+ if (view == null)
+ view = FileSystemView.getFileSystemView();
+ setFileSystemView(view);
+ updateUI();
+ }
+
+ /**
+ * Sets the dragEnabled property, this disables/enables automatic drag
+ * handling (drag and drop) on this component. The default value of the
+ * dragEnabled property is false.
+ *
+ * Some look and feels might not support automatic drag and drop; they
+ * will ignore this property.
+ *
+ * @param b - the new dragEnabled value
+ */
+ public void setDragEnabled(boolean b)
+ {
+ if (b && GraphicsEnvironment.isHeadless())
+ throw new HeadlessException();
+
+ dragEnabled = b;
+ }
+
+ /**
+ * Returns true if dragging is enabled.
+ *
+ * @return true if dragging is enabled.
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Returns the selected file, if there is one.
+ *
+ * @return The selected file (possibly null).
+ *
+ * @see #setSelectedFile(File)
+ */
+ public File getSelectedFile()
+ {
+ return selectedFile;
+ }
+
+ /**
+ * Sets the selected file and sends a {@link PropertyChangeEvent} to all
+ * registered listeners. The property name is
+ * {@link #SELECTED_FILE_CHANGED_PROPERTY}.
+ *
+ * @param file the file (null permitted).
+ */
+ public void setSelectedFile(File file)
+ {
+ if (selectedFile == null || !selectedFile.equals(file))
+ {
+ File old = selectedFile;
+ selectedFile = file;
+ firePropertyChange(SELECTED_FILE_CHANGED_PROPERTY, old, selectedFile);
+ }
+ }
+
+ /**
+ * Returns the selected file or files in an array. If no files are selected,
+ * an empty array is returned.
+ *
+ * @return An array of the selected files (possibly empty).
+ */
+ public File[] getSelectedFiles()
+ {
+ if (selectedFiles != null)
+ return selectedFiles;
+ if (selectedFile != null)
+ return new File[] { selectedFile };
+ return new File[0];
+ }
+
+ /**
+ * Sets the selected files and sends a {@link PropertyChangeEvent} (with the
+ * name {@link #SELECTED_FILES_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param selectedFiles the selected files (null permitted).
+ */
+ public void setSelectedFiles(File[] selectedFiles)
+ {
+ if (selectedFiles == null)
+ selectedFiles = new File[0];
+ if (selectedFiles.length > 0)
+ setSelectedFile(selectedFiles[0]);
+ else
+ setSelectedFile(null);
+ if (this.selectedFiles != selectedFiles)
+ {
+ File[] old = this.selectedFiles;
+ this.selectedFiles = selectedFiles;
+ firePropertyChange(SELECTED_FILES_CHANGED_PROPERTY, old, selectedFiles);
+ }
+
+ }
+
+ /**
+ * Returns the current directory.
+ *
+ * @return The current directory.
+ */
+ public File getCurrentDirectory()
+ {
+ return currentDir;
+ }
+
+ /**
+ * Sets the current directory and fires a {@link PropertyChangeEvent} (with
+ * the property name {@link #DIRECTORY_CHANGED_PROPERTY}) to all registered
+ * listeners. If dir is null, the current
+ * directory is set to the default directory returned by the file system
+ * view.
+ *
+ * @param dir the new directory (null permitted).
+ *
+ * @see FileSystemView#getDefaultDirectory()
+ */
+ public void setCurrentDirectory(File dir)
+ {
+ if (currentDir != dir || dir == null)
+ {
+ if (dir == null)
+ dir = fsv.getDefaultDirectory();
+
+ File old = currentDir;
+ currentDir = dir;
+ firePropertyChange(DIRECTORY_CHANGED_PROPERTY, old, currentDir);
+ }
+ }
+
+ /**
+ * Called by the UI delegate when the parent directory is changed.
+ */
+ public void changeToParentDirectory()
+ {
+ setCurrentDirectory(fsv.getParentDirectory(currentDir));
+ }
+
+ /**
+ * Rescans the current directory (this is handled by the UI delegate).
+ */
+ public void rescanCurrentDirectory()
+ {
+ getUI().rescanCurrentDirectory(this);
+ }
+
+ /**
+ * Ensures the the specified file is visible (this is handled by the
+ * UI delegate).
+ *
+ * @param f the file.
+ */
+ public void ensureFileIsVisible(File f)
+ {
+ getUI().ensureFileIsVisible(this, f);
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #OPEN_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showOpenDialog(Component parent) throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+
+ // FIXME: Remove when we get ancestor property
+ d.setTitle("Open");
+ setDialogType(OPEN_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #SAVE_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showSaveDialog(Component parent) throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+ setDialogType(SAVE_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Displays the file chooser in a modal dialog using the
+ * {@link #CUSTOM_DIALOG} type.
+ *
+ * @param parent the parent component.
+ *
+ * @return A return value indicating how the dialog was closed (one of
+ * {@link #APPROVE_OPTION}, {@link #CANCEL_OPTION} and
+ * {@link #ERROR_OPTION}).
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ public int showDialog(Component parent, String approveButtonText)
+ throws HeadlessException
+ {
+ JDialog d = createDialog(parent);
+ setApproveButtonText(approveButtonText);
+ setDialogType(CUSTOM_DIALOG);
+
+ retval = ERROR_OPTION;
+
+ d.pack();
+ d.show();
+ return retval;
+ }
+
+ /**
+ * Creates a modal dialog in which to display the file chooser.
+ *
+ * @param parent the parent component.
+ *
+ * @return The dialog.
+ *
+ * @throws HeadlessException DOCUMENT ME!
+ */
+ protected JDialog createDialog(Component parent) throws HeadlessException
+ {
+ Frame toUse = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
+ if (toUse == null)
+ toUse = (Frame) SwingUtilities.getOwnerFrame(null);
+
+ JDialog dialog = new JDialog(toUse);
+ setSelectedFile(null);
+ dialog.getContentPane().add(this);
+ dialog.addWindowListener( new WindowAdapter()
+ {
+ public void windowClosing(WindowEvent e)
+ {
+ cancelSelection();
+ }
+ });
+ dialog.setModal(true);
+ dialog.invalidate();
+ dialog.repaint();
+ return dialog;
+ }
+
+ /**
+ * Returns the flag that controls whether or not the control buttons are
+ * shown on the file chooser.
+ *
+ * @return A boolean.
+ *
+ * @see #setControlButtonsAreShown(boolean)
+ */
+ public boolean getControlButtonsAreShown()
+ {
+ return controlButtonsShown;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the control buttons are
+ * shown and, if it changes, sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY}) to
+ * all registered listeners.
+ *
+ * @param b the new value for the flag.
+ */
+ public void setControlButtonsAreShown(boolean b)
+ {
+ if (controlButtonsShown != b)
+ {
+ controlButtonsShown = b;
+ firePropertyChange(CONTROL_BUTTONS_ARE_SHOWN_CHANGED_PROPERTY,
+ ! controlButtonsShown, controlButtonsShown);
+ }
+ }
+
+ /**
+ * Returns the type of file chooser.
+ *
+ * @return {@link #OPEN_DIALOG}, {@link #SAVE_DIALOG} or
+ * {@link #CUSTOM_DIALOG}.
+ *
+ * @see #setDialogType(int)
+ */
+ public int getDialogType()
+ {
+ return dialogType;
+ }
+
+ /**
+ * Sets the dialog type and fires a {@link PropertyChangeEvent} (with the
+ * property name {@link #DIALOG_TYPE_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param dialogType the dialog type (one of: {@link #OPEN_DIALOG},
+ * {@link #SAVE_DIALOG}, {@link #CUSTOM_DIALOG}).
+ *
+ * @throws IllegalArgumentException if dialogType is not valid.
+ */
+ public void setDialogType(int dialogType)
+ {
+ if (dialogType != OPEN_DIALOG && dialogType != SAVE_DIALOG
+ && dialogType != CUSTOM_DIALOG)
+ throw new IllegalArgumentException("Choose allowable dialogType.");
+
+ if (this.dialogType != dialogType)
+ {
+ int old = this.dialogType;
+ this.dialogType = dialogType;
+ firePropertyChange(DIALOG_TYPE_CHANGED_PROPERTY, old, this.dialogType);
+ }
+ }
+
+ /**
+ * Sets the dialog title and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #DIALOG_TITLE_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param dialogTitle the dialog title (null permitted).
+ *
+ * @see #getDialogTitle()
+ */
+ public void setDialogTitle(String dialogTitle)
+ {
+ if (this.dialogTitle != dialogTitle)
+ {
+ String old = this.dialogTitle;
+ this.dialogTitle = dialogTitle;
+ firePropertyChange(DIALOG_TITLE_CHANGED_PROPERTY, old, this.dialogTitle);
+ }
+ }
+
+ /**
+ * Returns the dialog title.
+ *
+ * @return The dialog title (possibly null).
+ *
+ * @see #setDialogTitle(String)
+ */
+ public String getDialogTitle()
+ {
+ return dialogTitle;
+ }
+
+ /**
+ * Sets the tool tip text for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param toolTipText the text.
+ */
+ public void setApproveButtonToolTipText(String toolTipText)
+ {
+ if (approveButtonToolTipText != toolTipText)
+ {
+ String oldText = approveButtonToolTipText;
+ approveButtonToolTipText = toolTipText;
+ firePropertyChange(APPROVE_BUTTON_TOOL_TIP_TEXT_CHANGED_PROPERTY,
+ oldText, approveButtonToolTipText);
+ }
+ }
+
+ /**
+ * Returns the tool tip text for the approve button.
+ *
+ * @return The tool tip text for the approve button.
+ *
+ * @see #setApproveButtonToolTipText(String)
+ */
+ public String getApproveButtonToolTipText()
+ {
+ return approveButtonToolTipText;
+ }
+
+ /**
+ * Returns the approve button mnemonic, or zero if no mnemonic has been set.
+ *
+ * @return The approve button mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public int getApproveButtonMnemonic()
+ {
+ return approveButtonMnemonic;
+ }
+
+ /**
+ * Sets the mnemonic for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param mnemonic the mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(char)
+ */
+ public void setApproveButtonMnemonic(int mnemonic)
+ {
+ if (approveButtonMnemonic != mnemonic)
+ {
+ int oldMnemonic = approveButtonMnemonic;
+ approveButtonMnemonic = mnemonic;
+ firePropertyChange(APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY,
+ oldMnemonic, approveButtonMnemonic);
+ }
+ }
+
+ /**
+ * Sets the mnemonic for the approve button and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #APPROVE_BUTTON_MNEMONIC_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param mnemonic the mnemonic.
+ *
+ * @see #setApproveButtonMnemonic(int)
+ */
+ public void setApproveButtonMnemonic(char mnemonic)
+ {
+ setApproveButtonMnemonic((int) Character.toUpperCase(mnemonic));
+ }
+
+ /**
+ * Sets the approve button text and fires a {@link PropertyChangeEvent}
+ * (with the property name {@link #APPROVE_BUTTON_TEXT_CHANGED_PROPERTY}) to
+ * all registered listeners.
+ *
+ * @param approveButtonText the text (null permitted).
+ *
+ * @see #getApproveButtonText()
+ */
+ public void setApproveButtonText(String approveButtonText)
+ {
+ if (this.approveButtonText != approveButtonText)
+ {
+ String oldText = this.approveButtonText;
+ this.approveButtonText = approveButtonText;
+ firePropertyChange(APPROVE_BUTTON_TEXT_CHANGED_PROPERTY, oldText,
+ this.approveButtonText);
+ }
+ }
+
+ /**
+ * Returns the approve button text.
+ *
+ * @return The approve button text (possibly null).
+ *
+ * @see #setApproveButtonText(String)
+ */
+ public String getApproveButtonText()
+ {
+ return approveButtonText;
+ }
+
+ /**
+ * Returns the available file filters for this file chooser.
+ *
+ * @return The available file filters.
+ */
+ public FileFilter[] getChoosableFileFilters()
+ {
+ return (FileFilter[]) choosableFilters.toArray(new FileFilter[choosableFilters.size()]);
+ }
+
+ /**
+ * Adds a file filter to the list of available filters and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param filter the filter (null permitted).
+ */
+ public void addChoosableFileFilter(FileFilter filter)
+ {
+ if (filter != null)
+ {
+ FileFilter[] old = getChoosableFileFilters();
+ choosableFilters.add(filter);
+ FileFilter[] newFilters = getChoosableFileFilters();
+ firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old,
+ newFilters);
+ }
+ setFileFilter(filter);
+ }
+
+ /**
+ * Removes a file filter from the list of available filters and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param f the file filter.
+ *
+ * @return true if the filter was removed and
+ * false otherwise.
+ */
+ public boolean removeChoosableFileFilter(FileFilter f)
+ {
+ if (f == currentFilter)
+ setFileFilter(null);
+ FileFilter[] old = getChoosableFileFilters();
+ if (! choosableFilters.remove(f))
+ return false;
+ FileFilter[] newFilters = getChoosableFileFilters();
+ firePropertyChange(CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY, old, newFilters);
+ return true;
+ }
+
+ /**
+ * Clears the list of choosable file filters and installs the 'accept all'
+ * filter from the UI delegate.
+ */
+ public void resetChoosableFileFilters()
+ {
+ choosableFilters.clear();
+ choosableFilters.add(getUI().getAcceptAllFileFilter(this));
+ setFileFilter((FileFilter) choosableFilters.get(0));
+ }
+
+ /**
+ * Returns the 'accept all' file filter from the UI delegate.
+ *
+ * @return The 'accept all' file filter.
+ */
+ public FileFilter getAcceptAllFileFilter()
+ {
+ return getUI().getAcceptAllFileFilter(this);
+ }
+
+ /**
+ * Returns the flag that controls whether or not the 'accept all' file
+ * filter is included in the list of filters.
+ *
+ * @return A boolean.
+ *
+ * @see #setAcceptAllFileFilterUsed(boolean)
+ */
+ public boolean isAcceptAllFileFilterUsed()
+ {
+ return isAcceptAll;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the 'accept all' file filter
+ * is included in the list of filters, and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setAcceptAllFileFilterUsed(boolean b)
+ {
+ if (isAcceptAll != b)
+ {
+ isAcceptAll = b;
+ if (b)
+ addChoosableFileFilter(getAcceptAllFileFilter());
+ else
+ removeChoosableFileFilter(getAcceptAllFileFilter());
+ firePropertyChange(ACCEPT_ALL_FILE_FILTER_USED_CHANGED_PROPERTY,
+ ! isAcceptAll, isAcceptAll);
+ }
+ }
+
+ /**
+ * Returns the accessory component for the file chooser. The default
+ * value is null.
+ *
+ * @return The accessory component (possibly null).
+ *
+ * @see #setAccessory(JComponent)
+ */
+ public JComponent getAccessory()
+ {
+ return accessory;
+ }
+
+ /**
+ * Sets the accessory component for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners. The property
+ * name is {@link #ACCESSORY_CHANGED_PROPERTY}.
+ *
+ * @param newAccessory the accessory component.
+ */
+ public void setAccessory(JComponent newAccessory)
+ {
+ if (accessory != newAccessory)
+ {
+ JComponent old = accessory;
+ accessory = newAccessory;
+ firePropertyChange(ACCESSORY_CHANGED_PROPERTY, old, accessory);
+ }
+ }
+
+ /**
+ * Sets the file selection mode and sends a {@link PropertyChangeEvent}
+ * to all registered listeners. The property name is
+ * {@link #FILE_SELECTION_MODE_CHANGED_PROPERTY}.
+ *
+ * @param mode the mode ({@link #FILES_ONLY}, {@link #DIRECTORIES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}).
+ *
+ * @throws IllegalArgumentException if the mode is invalid.
+ */
+ public void setFileSelectionMode(int mode)
+ {
+ if (mode != FILES_ONLY && mode != DIRECTORIES_ONLY
+ && mode != FILES_AND_DIRECTORIES)
+ throw new IllegalArgumentException("Choose a correct file selection mode.");
+ if (fileSelectionMode != mode)
+ {
+ int old = fileSelectionMode;
+ fileSelectionMode = mode;
+ firePropertyChange(FILE_SELECTION_MODE_CHANGED_PROPERTY, old,
+ fileSelectionMode);
+ }
+ }
+
+ /**
+ * Returns the file selection mode, one of: {@link #FILES_ONLY},
+ * {@link #DIRECTORIES_ONLY} or {@link #FILES_AND_DIRECTORIES}. The
+ * default is {@link #FILES_ONLY}.
+ *
+ * @return The file selection mode.
+ *
+ * @see #setFileSelectionMode(int)
+ */
+ public int getFileSelectionMode()
+ {
+ return fileSelectionMode;
+ }
+
+ /**
+ * Returns true if file selection is enabled, and
+ * false otherwise. File selection is enabled when the
+ * file selection mode is {@link #FILES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}.
+ *
+ * @return true if file selection is enabled.
+ *
+ * @see #getFileSelectionMode()
+ */
+ public boolean isFileSelectionEnabled()
+ {
+ return (fileSelectionMode == FILES_ONLY
+ || fileSelectionMode == FILES_AND_DIRECTORIES);
+ }
+
+ /**
+ * Returns true if directory selection is enabled, and
+ * false otherwise. Directory selection is enabled when the
+ * file selection mode is {@link #DIRECTORIES_ONLY} or
+ * {@link #FILES_AND_DIRECTORIES}.
+ *
+ * @return true if file selection is enabled.
+ *
+ * @see #getFileSelectionMode()
+ */
+ public boolean isDirectorySelectionEnabled()
+ {
+ return (fileSelectionMode == DIRECTORIES_ONLY
+ || fileSelectionMode == FILES_AND_DIRECTORIES);
+ }
+
+ /**
+ * Sets the flag that controls whether multiple selections are allowed in
+ * this filechooser and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #MULTI_SELECTION_ENABLED_CHANGED_PROPERTY}) to all
+ * registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setMultiSelectionEnabled(boolean b)
+ {
+ if (multiSelection != b)
+ {
+ multiSelection = b;
+ firePropertyChange(MULTI_SELECTION_ENABLED_CHANGED_PROPERTY,
+ ! multiSelection, multiSelection);
+ }
+ }
+
+ /**
+ * Returns true if multiple selections are allowed within this
+ * file chooser, and false otherwise.
+ *
+ * @return A boolean.
+ *
+ * @see #setMultiSelectionEnabled(boolean)
+ */
+ public boolean isMultiSelectionEnabled()
+ {
+ return multiSelection;
+ }
+
+ /**
+ * Returns true if hidden files are to be hidden, and
+ * false otherwise.
+ *
+ * @return A boolean.
+ *
+ * @see #setFileHidingEnabled(boolean)
+ */
+ public boolean isFileHidingEnabled()
+ {
+ return fileHiding;
+ }
+
+ /**
+ * Sets the flag that controls whether or not hidden files are displayed,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * {@link #FILE_HIDING_CHANGED_PROPERTY}) to all registered listeners.
+ *
+ * @param b the new value of the flag.
+ */
+ public void setFileHidingEnabled(boolean b)
+ {
+ if (fileHiding != b)
+ {
+ fileHiding = b;
+ firePropertyChange(FILE_HIDING_CHANGED_PROPERTY, ! fileHiding,
+ fileHiding);
+ }
+ }
+
+ /**
+ * Sets the file filter and sends a {@link PropertyChangeEvent} (with the
+ * property name {@link #FILE_FILTER_CHANGED_PROPERTY}) to all registered
+ * listeners.
+ *
+ * @param filter the filter (null permitted).
+ */
+ public void setFileFilter(FileFilter filter)
+ {
+ if (currentFilter != filter)
+ {
+ if (filter != null && !choosableFilters.contains(filter))
+ addChoosableFileFilter(filter);
+ FileFilter old = currentFilter;
+ currentFilter = filter;
+ firePropertyChange(FILE_FILTER_CHANGED_PROPERTY, old, currentFilter);
+ }
+ }
+
+ /**
+ * Returns the file filter.
+ *
+ * @return The file filter.
+ *
+ * @see #setFileFilter(FileFilter)
+ */
+ public FileFilter getFileFilter()
+ {
+ return currentFilter;
+ }
+
+ /**
+ * Sets a custom {@link FileView} for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners. The property
+ * name is {@link #FILE_VIEW_CHANGED_PROPERTY}.
+ *
+ * @param fileView the file view (null permitted).
+ *
+ * @see #getFileView()
+ */
+ public void setFileView(FileView fileView)
+ {
+ if (fv != fileView)
+ {
+ FileView old = fv;
+ fv = fileView;
+ firePropertyChange(FILE_VIEW_CHANGED_PROPERTY, old, fv);
+ }
+ }
+
+ /**
+ * Returns the custom {@link FileView} for the file chooser.
+ *
+ * @return The file view (possibly null).
+ */
+ public FileView getFileView()
+ {
+ return fv;
+ }
+
+ /**
+ * Returns the name of the file, generated by the current (or default)
+ * {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file name.
+ */
+ public String getName(File f)
+ {
+ String name = null;
+ if (fv != null)
+ name = fv.getName(f);
+ if (name == null)
+ name = getUI().getFileView(this).getName(f);
+ return name;
+ }
+
+ /**
+ * Returns the description of the file, generated by the current (or default)
+ * {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file description.
+ */
+ public String getDescription(File f)
+ {
+ String result = null;
+ if (fv != null)
+ result = fv.getDescription(f);
+ if (result == null)
+ result = getUI().getFileView(this).getDescription(f);
+ return result;
+ }
+
+ /**
+ * Returns the type description for the file, generated by the current (or
+ * default) {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return The file type description.
+ */
+ public String getTypeDescription(File f)
+ {
+ String result = null;
+ if (fv != null)
+ result = getFileView().getTypeDescription(f);
+ if (result == null)
+ result = getUI().getFileView(this).getTypeDescription(f);
+ return result;
+ }
+
+ /**
+ * Returns the icon provided by the current (or default) {@link FileView}.
+ *
+ * @param f the file.
+ *
+ * @return An icon representing the file.
+ */
+ public Icon getIcon(File f)
+ {
+ Icon result = null;
+ if (fv != null)
+ result = fv.getIcon(f);
+ if (result == null)
+ result = getUI().getFileView(this).getIcon(f);
+ return result;
+ }
+
+ /**
+ * Returns true if the file is traversable, and
+ * false otherwise.
+ *
+ * @param f the file or directory.
+ *
+ * @return A boolean.
+ */
+ public boolean isTraversable(File f)
+ {
+ return getFileSystemView().isTraversable(f).booleanValue();
+ }
+
+ /**
+ * Returns true if the file is accepted by the current
+ * file filter.
+ *
+ * @param f the file.
+ *
+ * @return A boolean.
+ */
+ public boolean accept(File f)
+ {
+ if (f == null)
+ return true;
+ FileFilter ff = getFileFilter();
+ if (ff != null)
+ return ff.accept(f);
+ else
+ return true;
+ }
+
+ /**
+ * Sets the file system view for the file chooser and sends a
+ * {@link PropertyChangeEvent} to all registered listeners.
+ *
+ * @param fsv the file system view.
+ */
+ public void setFileSystemView(FileSystemView fsv)
+ {
+ if (this.fsv != fsv)
+ {
+ FileSystemView old = this.fsv;
+ this.fsv = fsv;
+ firePropertyChange(FILE_SYSTEM_VIEW_CHANGED_PROPERTY, old, this.fsv);
+ }
+ }
+
+ /**
+ * Returns the file system view being used by this file chooser.
+ *
+ * @return The file system view.
+ *
+ * @see #setFileSystemView(FileSystemView)
+ */
+ public FileSystemView getFileSystemView()
+ {
+ return fsv;
+ }
+
+ /**
+ * Approves the selection. An {@link ActionEvent} is sent to all registered
+ * listeners.
+ */
+ public void approveSelection()
+ {
+ retval = APPROVE_OPTION;
+ fireActionPerformed(APPROVE_SELECTION);
+ }
+
+ /**
+ * Cancels the selection. An {@link ActionEvent} is sent to all registered
+ * listeners.
+ */
+ public void cancelSelection()
+ {
+ retval = CANCEL_OPTION;
+ fireActionPerformed(CANCEL_SELECTION);
+ }
+
+ /**
+ * Adds an {@link ActionListener} to the file chooser.
+ *
+ * @param l the listener.
+ */
+ public void addActionListener(ActionListener l)
+ {
+ listenerList.add(ActionListener.class, l);
+ }
+
+ /**
+ * Removes an {@link ActionListener} from this file chooser.
+ *
+ * @param l the listener.
+ */
+ public void removeActionListener(ActionListener l)
+ {
+ try
+ {
+ listenerList.remove(ActionListener.class, l);
+ }
+ catch (IllegalArgumentException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns the action listeners registered with this file chooser.
+ *
+ * @return An array of listeners.
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sends an @link {ActionEvent} to all registered listeners.
+ *
+ * @param command the action command.
+ */
+ protected void fireActionPerformed(String command)
+ {
+ ActionListener[] list = getActionListeners();
+ ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
+ command);
+
+ for (int i = 0; i < list.length; i++)
+ list[i].actionPerformed(event);
+ }
+
+ /**
+ * Installs the UI delegate for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((FileChooserUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the UI delegate class identifier.
+ *
+ * @return FileChooserUI.
+ */
+ public String getUIClassID()
+ {
+ return "FileChooserUI";
+ }
+
+ /**
+ * Returns the UI delegate for the component.
+ *
+ * @return The UI delegate.
+ */
+ public FileChooserUI getUI()
+ {
+ return (FileChooserUI) ui;
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * JFileChooser component, for use in debugging. The return
+ * value is guaranteed to be non-null, but the format of the
+ * string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * JFileChooser.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",approveButtonText=");
+ if (approveButtonText != null)
+ sb.append(approveButtonText);
+ sb.append(",currentDirectory=");
+ if (currentDir != null)
+ sb.append(currentDir);
+ sb.append(",dialogTitle=");
+ if (dialogTitle != null)
+ sb.append(dialogTitle);
+ sb.append(",dialogType=");
+ if (dialogType == OPEN_DIALOG)
+ sb.append("OPEN_DIALOG");
+ if (dialogType == SAVE_DIALOG)
+ sb.append("SAVE_DIALOG");
+ if (dialogType == CUSTOM_DIALOG)
+ sb.append("CUSTOM_DIALOG");
+ sb.append(",fileSelectionMode=");
+ if (fileSelectionMode == FILES_ONLY)
+ sb.append("FILES_ONLY");
+ if (fileSelectionMode == DIRECTORIES_ONLY)
+ sb.append("DIRECTORIES_ONLY");
+ if (fileSelectionMode == FILES_AND_DIRECTORIES)
+ sb.append("FILES_AND_DIRECTORIES");
+ sb.append(",returnValue=");
+ if (retval == APPROVE_OPTION)
+ sb.append("APPROVE_OPTION");
+ if (retval == CANCEL_OPTION)
+ sb.append("CANCEL_OPTION");
+ if (retval == ERROR_OPTION)
+ sb.append("ERROR_OPTION");
+ sb.append(",selectedFile=");
+ if (selectedFile != null)
+ sb.append(selectedFile);
+ sb.append(",useFileHiding=").append(fileHiding);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JFileChooser component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJFileChooser}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJFileChooser();
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the JFileChooser
+ * component.
+ */
+ protected class AccessibleJFileChooser
+ extends JComponent.AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of AccessibleJFileChooser.
+ */
+ protected AccessibleJFileChooser()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the JFileChooser
+ * component.
+ *
+ * @return {@link AccessibleRole#FILE_CHOOSER}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.FILE_CHOOSER;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFormattedTextField.java b/libjava/classpath/javax/swing/JFormattedTextField.java
new file mode 100644
index 000000000..e4b6fec79
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFormattedTextField.java
@@ -0,0 +1,648 @@
+/* JFormattedTextField.java --
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.event.FocusEvent;
+import java.io.Serializable;
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Date;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.DateFormatter;
+import javax.swing.text.DefaultFormatter;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
+import javax.swing.text.InternationalFormatter;
+import javax.swing.text.NavigationFilter;
+import javax.swing.text.NumberFormatter;
+
+/**
+ * A text field that makes use of a formatter to display and edit a specific
+ * type of data. The value that is displayed can be an arbitrary object. The
+ * formatter is responsible for displaying the value in a textual form and
+ * it may allow editing of the value.
+ *
+ * Formatters are usually obtained using an instance of
+ * {@link AbstractFormatterFactory}. This factory is responsible for providing
+ * an instance of {@link AbstractFormatter} that is able to handle the
+ * formatting of the value of the JFormattedTextField.
+ *
+ * @author Michael Koch
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ * @since 1.4
+ */
+public class JFormattedTextField extends JTextField
+{
+ private static final long serialVersionUID = 5464657870110180632L;
+
+ /**
+ * An abstract base implementation for a formatter that can be used by
+ * a JTextField. A formatter can display a specific type of object and
+ * may provide a way to edit this value.
+ */
+ public abstract static class AbstractFormatter implements Serializable
+ {
+ private static final long serialVersionUID = -5193212041738979680L;
+
+ private JFormattedTextField textField;
+
+ public AbstractFormatter ()
+ {
+ //Do nothing here.
+ }
+
+ /**
+ * Clones the AbstractFormatter and removes the association to any
+ * particular JFormattedTextField.
+ *
+ * @return a clone of this formatter with no association to any particular
+ * JFormattedTextField
+ * @throws CloneNotSupportedException if the Object's class doesn't support
+ * the {@link Cloneable} interface
+ */
+ protected Object clone()
+ throws CloneNotSupportedException
+ {
+ // Clone this formatter.
+ AbstractFormatter newFormatter = (AbstractFormatter) super.clone();
+
+ // And remove the association to the JFormattedTextField.
+ newFormatter.textField = null;
+ return newFormatter;
+ }
+
+ /**
+ * Returns a custom set of Actions that this formatter supports. Should
+ * be subclassed by formatters that have a custom set of Actions.
+ *
+ * @return null. Should be subclassed by formatters that want
+ * to install custom Actions on the JFormattedTextField.
+ */
+ protected Action[] getActions()
+ {
+ return null;
+ }
+
+ /**
+ * Gets the DocumentFilter for this formatter. Should be subclassed
+ * by formatters wishing to install a filter that oversees Document
+ * mutations.
+ *
+ * @return null. Should be subclassed by formatters
+ * that want to restrict Document mutations.
+ */
+ protected DocumentFilter getDocumentFilter()
+ {
+ // Subclasses should override this if they want to install a
+ // DocumentFilter.
+ return null;
+ }
+
+ /**
+ * Returns the JFormattedTextField on which this formatter is
+ * currently installed.
+ *
+ * @return the JFormattedTextField on which this formatter is currently
+ * installed
+ */
+ protected JFormattedTextField getFormattedTextField()
+ {
+ return textField;
+ }
+
+ /**
+ * Gets the NavigationFilter for this formatter. Should be subclassed
+ * by formatters (such as {@link DefaultFormatter}) that wish to
+ * restrict where the cursor can be placed within the text field.
+ *
+ * @return null. Subclassed by formatters that want to restrict
+ * cursor location within the JFormattedTextField.
+ */
+ protected NavigationFilter getNavigationFilter()
+ {
+ // This should be subclassed if the formatter wants to install
+ // a NavigationFilter on the JFormattedTextField.
+ return null;
+ }
+
+ /**
+ * Installs this formatter on the specified JFormattedTextField. This
+ * converts the current value to a displayable String and displays it,
+ * and installs formatter specific Actions from getActions.
+ * It also installs a DocumentFilter and NavigationFilter on the
+ * JFormattedTextField.
+ *
+ * If there is a ParseException this sets the text to an
+ * empty String and marks the text field in an invalid state.
+ *
+ * @param textField the JFormattedTextField on which to install this
+ * formatter
+ */
+ public void install(JFormattedTextField textField)
+ {
+ // Uninstall the current textfield.
+ if (this.textField != null)
+ uninstall();
+
+ this.textField = textField;
+
+ // Install some state on the text field, including display text,
+ // DocumentFilter, NavigationFilter, and formatter specific Actions.
+ if (textField != null)
+ {
+ try
+ {
+ // Set the text of the field.
+ textField.setText(valueToString(textField.getValue()));
+ Document doc = textField.getDocument();
+
+ // Set the DocumentFilter for the field's Document.
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).setDocumentFilter(getDocumentFilter());
+
+ // Set the NavigationFilter.
+ textField.setNavigationFilter(getNavigationFilter());
+
+ // Set the Formatter Actions
+ // FIXME: Have to add the actions from getActions()
+ }
+ catch (ParseException pe)
+ {
+ // Set the text to an empty String and mark the field as invalid.
+ textField.setText("");
+ setEditValid(false);
+ }
+ }
+ }
+
+ /**
+ * Clears the state installed on the JFormattedTextField by the formatter.
+ * This resets the DocumentFilter, NavigationFilter, and any additional
+ * Actions (returned by getActions()).
+ */
+ public void uninstall()
+ {
+ // Set the DocumentFilter for the field's Document.
+ Document doc = textField.getDocument();
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument) doc).setDocumentFilter(null);
+ textField.setNavigationFilter(null);
+ // FIXME: Have to remove the Actions from getActions()
+ this.textField = null;
+ }
+
+ /**
+ * Invoke this method when invalid values are entered. This forwards the
+ * call to the JFormattedTextField.
+ */
+ protected void invalidEdit()
+ {
+ textField.invalidEdit();
+ }
+
+ /**
+ * This method updates the editValid property of
+ * JFormattedTextField.
+ *
+ * @param valid the new state for the editValid property
+ */
+ protected void setEditValid(boolean valid)
+ {
+ textField.editValid = valid;
+ }
+
+ /**
+ * Parses text to return a corresponding Object.
+ *
+ * @param text the String to parse
+ * @return an Object that text represented
+ * @throws ParseException if there is an error in the conversion
+ */
+ public abstract Object stringToValue(String text)
+ throws ParseException;
+
+ /**
+ * Returns a String to be displayed, based on the Object
+ * value.
+ *
+ * @param value the Object from which to generate a String
+ * @return a String to be displayed
+ * @throws ParseException if there is an error in the conversion
+ */
+ public abstract String valueToString(Object value)
+ throws ParseException;
+ }
+
+ /**
+ * Delivers instances of an {@link AbstractFormatter} for
+ * a specific value type for a JFormattedTextField.
+ */
+ public abstract static class AbstractFormatterFactory
+ {
+ public AbstractFormatterFactory()
+ {
+ // Do nothing here.
+ }
+
+ public abstract AbstractFormatter getFormatter(JFormattedTextField tf);
+ }
+
+ /** The possible focusLostBehavior options **/
+ public static final int COMMIT = 0;
+ public static final int COMMIT_OR_REVERT = 1;
+ public static final int REVERT = 2;
+ public static final int PERSIST = 3;
+
+ /** The most recent valid and committed value **/
+ private Object value;
+
+ /** The behaviour for when this text field loses focus **/
+ private int focusLostBehavior = COMMIT_OR_REVERT;
+
+ /** The formatter factory currently being used **/
+ private AbstractFormatterFactory formatterFactory;
+
+ /** The formatter currently being used **/
+ private AbstractFormatter formatter;
+
+ // Package-private to avoid an accessor method.
+ boolean editValid = true;
+
+ /**
+ * Creates a JFormattedTextField with no formatter factory.
+ * setValue or setFormatterFactory will
+ * properly configure this text field to edit a particular type
+ * of value.
+ */
+ public JFormattedTextField()
+ {
+ this((AbstractFormatterFactory) null, null);
+ }
+
+ /**
+ * Creates a JFormattedTextField that can handle the specified Format.
+ * An appopriate AbstractFormatter and AbstractFormatterFactory will
+ * be created for the specified Format.
+ *
+ * @param format the Format that this JFormattedTextField should be able
+ * to handle
+ */
+ public JFormattedTextField(Format format)
+ {
+ this ();
+ setFormatterFactory(getAppropriateFormatterFactory(format));
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter. This will
+ * create a {@link DefaultFormatterFactory} with this formatter as the default
+ * formatter.
+ *
+ * @param formatter the formatter to use for this JFormattedTextField
+ */
+ public JFormattedTextField(AbstractFormatter formatter)
+ {
+ this(new DefaultFormatterFactory(formatter));
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory.
+ *
+ * @param factory the formatter factory to use for this JFormattedTextField
+ */
+ public JFormattedTextField(AbstractFormatterFactory factory)
+ {
+ setFormatterFactory(factory);
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified formatter factory and
+ * initial value.
+ *
+ * @param factory the initial formatter factory for this JFormattedTextField
+ * @param value the initial value for the text field
+ */
+ public JFormattedTextField(AbstractFormatterFactory factory, Object value)
+ {
+ setFormatterFactory(factory);
+ setValue(value);
+ }
+
+ /**
+ * Creates a JFormattedTextField with the specified value. This creates a
+ * formatter and formatterFactory that are appropriate for the value.
+ *
+ * @param value the initial value for this JFormattedTextField
+ */
+ public JFormattedTextField(Object value)
+ {
+ setValue(value);
+ }
+
+ /**
+ * Returns an AbstractFormatterFactory that will give an appropriate
+ * AbstractFormatter for the given Format.
+ * @param format the Format to match with an AbstractFormatter.
+ * @return a DefaultFormatterFactory whose defaultFormatter is appropriate
+ * for the given Format.
+ */
+ private AbstractFormatterFactory getAppropriateFormatterFactory(Format format)
+ {
+ AbstractFormatter newFormatter;
+ if (format instanceof DateFormat)
+ newFormatter = new DateFormatter((DateFormat) format);
+ else if (format instanceof NumberFormat)
+ newFormatter = new NumberFormatter ((NumberFormat) format);
+ else
+ newFormatter = new InternationalFormatter(format);
+
+ return new DefaultFormatterFactory(newFormatter);
+ }
+
+ /**
+ * Forces the current value from the editor to be set as the current
+ * value. If there is no current formatted this has no effect.
+ *
+ * @throws ParseException if the formatter cannot format the current value
+ */
+ public void commitEdit()
+ throws ParseException
+ {
+ if (formatter == null)
+ return;
+ // Note: this code is a lot like setValue except that we don't want
+ // to create a new formatter.
+ Object oldValue = this.value;
+
+ this.value = formatter.stringToValue(getText());
+ editValid = true;
+
+ firePropertyChange("value", oldValue, this.value);
+ }
+
+ /**
+ * Gets the command list supplied by the UI augmented by the specific
+ * Actions for JFormattedTextField.
+ *
+ * @return an array of Actions that this text field supports
+ */
+ public Action[] getActions()
+ {
+ // FIXME: Add JFormattedTextField specific actions
+ // These are related to committing or cancelling edits.
+ return super.getActions();
+ }
+
+ /**
+ * Returns the behaviour of this JFormattedTextField upon losing focus. This
+ * is one of COMMIT, COMMIT_OR_REVERT,
+ * PERSIST, or REVERT.
+ * @return the behaviour upon losing focus
+ */
+ public int getFocusLostBehavior()
+ {
+ return focusLostBehavior;
+ }
+
+ /**
+ * Returns the current formatter used for this JFormattedTextField.
+ * @return the current formatter used for this JFormattedTextField
+ */
+ public AbstractFormatter getFormatter()
+ {
+ return formatter;
+ }
+
+ /**
+ * Returns the factory currently used to generate formatters for this
+ * JFormattedTextField.
+ * @return the factory currently used to generate formatters
+ */
+ public AbstractFormatterFactory getFormatterFactory()
+ {
+ return formatterFactory;
+ }
+
+ public String getUIClassID()
+ {
+ return "FormattedTextFieldUI";
+ }
+
+ /**
+ * Returns the last valid value. This may not be the value currently shown
+ * in the text field depending on whether or not the formatter commits on
+ * valid edits and allows invalid input to be temporarily displayed.
+ * @return the last committed valid value
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This method is used to provide feedback to the user when an invalid value
+ * is input during editing.
+ */
+ protected void invalidEdit()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Returns true if the current value being edited is valid. This property is
+ * managed by the current formatted.
+ * @return true if the value being edited is valid.
+ */
+ public boolean isEditValid()
+ {
+ return editValid;
+ }
+
+ /**
+ * Processes focus events. This is overridden because we may want to
+ * change the formatted depending on whether or not this field has
+ * focus.
+ *
+ * @param evt the FocusEvent
+ */
+ protected void processFocusEvent(FocusEvent evt)
+ {
+ super.processFocusEvent(evt);
+ // Let the formatterFactory change the formatter for this text field
+ // based on whether or not it has focus.
+ setFormatter (formatterFactory.getFormatter(this));
+ }
+
+ /**
+ * Associates this JFormattedTextField with a Document and propagates
+ * a PropertyChange event to each listener.
+ *
+ * @param newDocument the Document to associate with this text field
+ */
+ public void setDocument(Document newDocument)
+ {
+ // FIXME: This method should do more than this. Must do some handling
+ // of the DocumentListeners.
+ Document oldDocument = getDocument();
+
+ if (oldDocument == newDocument)
+ return;
+
+ super.setDocument(newDocument);
+ }
+
+ /**
+ * Sets the behaviour of this JFormattedTextField upon losing focus.
+ * This must be COMMIT, COMMIT_OR_REVERT,
+ * PERSIST, or REVERT or an
+ * IllegalArgumentException will be thrown.
+ *
+ * @param behavior
+ * @throws IllegalArgumentException if behaviour is not
+ * one of the above
+ */
+ public void setFocusLostBehavior(int behavior)
+ {
+ if (behavior != COMMIT
+ && behavior != COMMIT_OR_REVERT
+ && behavior != PERSIST
+ && behavior != REVERT)
+ throw new IllegalArgumentException("invalid behavior");
+
+ this.focusLostBehavior = behavior;
+ }
+
+ /**
+ * Sets the formatter for this JFormattedTextField. Normally the formatter
+ * factory will take care of this, or calls to setValue will also make sure
+ * that the formatter is set appropriately.
+ *
+ * @param formatter the AbstractFormatter to use for formatting the value for
+ * this JFormattedTextField
+ */
+ protected void setFormatter(AbstractFormatter formatter)
+ {
+ AbstractFormatter oldFormatter = null;
+
+ oldFormatter = this.formatter;
+
+ if (oldFormatter != null)
+ oldFormatter.uninstall();
+
+ this.formatter = formatter;
+
+ if (formatter != null)
+ formatter.install(this);
+
+ firePropertyChange("formatter", oldFormatter, formatter);
+ }
+
+ /**
+ * Sets the factory from which this JFormattedTextField should obtain
+ * its formatters.
+ *
+ * @param factory the AbstractFormatterFactory that will be used to generate
+ * formatters for this JFormattedTextField
+ */
+ public void setFormatterFactory(AbstractFormatterFactory factory)
+ {
+ if (formatterFactory == factory)
+ return;
+
+ AbstractFormatterFactory oldFactory = formatterFactory;
+ formatterFactory = factory;
+ firePropertyChange("formatterFactory", oldFactory, factory);
+
+ // Now set the formatter according to our new factory.
+ if (formatterFactory != null)
+ setFormatter(formatterFactory.getFormatter(this));
+ else
+ setFormatter(null);
+ }
+
+ /**
+ * Sets the value that will be formatted and displayed.
+ *
+ * @param newValue the value to be formatted and displayed
+ */
+ public void setValue(Object newValue)
+ {
+ if (value == newValue)
+ return;
+
+ Object oldValue = value;
+ value = newValue;
+
+ // If there is no formatterFactory then make one.
+ if (formatterFactory == null)
+ setFormatterFactory(createFormatterFactory(newValue));
+
+ // Set the formatter appropriately. This is because there may be a new
+ // formatterFactory from the line above, or we may want a new formatter
+ // depending on the type of newValue (or if newValue is null).
+ setFormatter (formatterFactory.getFormatter(this));
+ firePropertyChange("value", oldValue, newValue);
+ }
+
+ /**
+ * A helper method that attempts to create a formatter factory that is
+ * suitable to format objects of the type like value.
+ *
+ * @param value an object which should be formatted by the formatter factory.
+ *
+ * @return a formatter factory able to format objects of the class of
+ * value
+ */
+ AbstractFormatterFactory createFormatterFactory(Object value)
+ {
+ AbstractFormatter formatter = null;
+ if (value instanceof Date)
+ formatter = new DateFormatter();
+ else if (value instanceof Number)
+ formatter = new NumberFormatter();
+ else
+ formatter = new DefaultFormatter();
+ return new DefaultFormatterFactory(formatter);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JFrame.java b/libjava/classpath/javax/swing/JFrame.java
new file mode 100644
index 000000000..074d1c7d3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JFrame.java
@@ -0,0 +1,410 @@
+/* JFrame.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.LayoutManager;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * A window that supports window decorations (titlebar and borders).
+ * This is an extension of {@link java.awt.Frame} that provides support
+ * for the Swing architecture. Most importantly it contains a {@link JRootPane}
+ * as it's only top-level child, that manages the content pane, the menu and
+ * a glass pane.
+ *
+ * Also, unlike java.awt.Frames, JFrames support the
+ * Swing Pluggable Look & Feel architecture.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JFrame extends Frame
+ implements WindowConstants, RootPaneContainer, Accessible
+{
+ /**
+ * Provides accessibility support for JFrames.
+ */
+ protected class AccessibleJFrame extends Frame.AccessibleAWTFrame
+ {
+ /**
+ * Creates a new instance of AccessibleJFrame.
+ */
+ protected AccessibleJFrame()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A flag for {@link #setDefaultCloseOperation(int)}, indicating that the
+ * application should be exited, when this JFrame is closed.
+ * Note that in version 1.4, the equivalent constant has been added to
+ * {@link WindowConstants}.
+ *
+ * @since 1.3
+ */
+ public static final int EXIT_ON_CLOSE = 3;
+
+ private static final long serialVersionUID = -3362141868504252139L;
+ private static boolean defaultLookAndFeelDecorated;
+ private int closeAction = HIDE_ON_CLOSE;
+ protected AccessibleContext accessibleContext;
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /**
+ * Creates a new frame with an empty string for the title.
+ */
+ public JFrame()
+ {
+ super("");
+ frameInit();
+ }
+
+ /**
+ * Creates a new JFrame with the specified title.
+ *
+ * @param title the frame title (null permitted).
+ */
+ public JFrame(String title)
+ {
+ super(title);
+ frameInit();
+ }
+
+ /**
+ * Creates a new JFrame in the specified {@link GraphicsConfiguration}
+ * and with an empty title.
+ *
+ * @param gc the GraphicsConfiguration that is used for
+ * the new JFrame
+ *
+ * @see Frame#Frame(GraphicsConfiguration)
+ */
+ public JFrame(GraphicsConfiguration gc)
+ {
+ super(gc);
+ frameInit();
+ }
+
+ /**
+ * Creates a new JFrame in the specified {@link GraphicsConfiguration}
+ * and with the specified title.
+ *
+ * @param title the title for the new JFrame
+ * @param gc the GraphicsConfiguration that is used for
+ * the new JFrame
+ *
+ * @see Frame#Frame(String, GraphicsConfiguration)
+ */
+ public JFrame(String title, GraphicsConfiguration gc)
+ {
+ super(title, gc);
+ frameInit();
+ }
+
+ protected void frameInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
+
+ super.setLayout(new BorderLayout());
+ setBackground(UIManager.getDefaults().getColor("control"));
+ enableEvents(AWTEvent.WINDOW_EVENT_MASK);
+ getRootPane(); // will do set/create
+
+ // Setup the defaultLookAndFeelDecoration if requested.
+ if (isDefaultLookAndFeelDecorated()
+ && UIManager.getLookAndFeel().getSupportsWindowDecorations())
+ {
+ setUndecorated(true);
+ getRootPane().setWindowDecorationStyle(JRootPane.FRAME);
+ }
+
+ // We're now done the init stage.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ public void setJMenuBar(JMenuBar menubar)
+ {
+ getRootPane().setJMenuBar(menubar);
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled() && comp != rootPane)
+ getContentPane().add(comp,constraints,index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp==rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public static void setDefaultLookAndFeelDecorated(boolean decorated)
+ {
+ defaultLookAndFeelDecorated = decorated;
+ }
+
+ public static boolean isDefaultLookAndFeelDecorated()
+ {
+ return defaultLookAndFeelDecorated;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JFrame.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJFrame}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJFrame();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a code for the default operation when the frame is closed. The
+ * default value is {@link WindowConstants#HIDE_ON_CLOSE}.
+ *
+ * @return One of: {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE},
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}, {@link #EXIT_ON_CLOSE}.
+ *
+ * @see #setDefaultCloseOperation(int)
+ */
+ public int getDefaultCloseOperation()
+ {
+ return closeAction;
+ }
+
+ /**
+ * Returns a string describing the attributes for the JFrame,
+ * for use in debugging. The return value is guaranteed to be
+ * non-null, but the format may vary between implementations.
+ *
+ * @return A string describing the attributes of the JFrame.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",defaultCloseOperation=");
+ sb.append(SwingUtilities.convertWindowConstantToString(
+ getDefaultCloseOperation()));
+ sb.append(",rootPane=");
+ if (rootPane != null)
+ sb.append(rootPane);
+ sb.append(",rootPaneCheckingEnabled=").append(rootPaneCheckingEnabled);
+ return sb.toString();
+ }
+
+ protected void processWindowEvent(WindowEvent e)
+ {
+ super.processWindowEvent(e);
+ if (e.getID() == WindowEvent.WINDOW_CLOSING)
+ {
+ switch (closeAction)
+ {
+ case EXIT_ON_CLOSE:
+ System.exit(0);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DO_NOTHING_ON_CLOSE:
+ break;
+ }
+ }
+ }
+
+ /**
+ * Sets the default operation that is performed when this frame is closed.
+ * The default is HIDE_ON_CLOSE. When
+ * EXIT_ON_CLOSE is specified this method calls
+ * SecurityManager.checkExit(0) which might throw a
+ * SecurityException.
+ *
+ * @param operation a code for the operation (one of:
+ * {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE},
+ * {@link WindowConstants#DISPOSE_ON_CLOSE} and
+ * {@link WindowConstants#EXIT_ON_CLOSE}).
+ *
+ * @throws IllegalArgumentException if operation is not one of
+ * the specified codes.
+ *
+ * @see #getDefaultCloseOperation()
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null && operation == EXIT_ON_CLOSE)
+ sm.checkExit(0);
+
+ if (operation != EXIT_ON_CLOSE && operation != DISPOSE_ON_CLOSE
+ && operation != HIDE_ON_CLOSE && operation != DO_NOTHING_ON_CLOSE)
+ throw new IllegalArgumentException("operation must be EXIT_ON_CLOSE, "
+ + "HIDE_ON_CLOSE, DISPOSE_ON_CLOSE, or DO_NOTHING_ON_CLOSE");
+
+ closeAction = operation;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JInternalFrame.java b/libjava/classpath/javax/swing/JInternalFrame.java
new file mode 100644
index 000000000..511bc6ed4
--- /dev/null
+++ b/libjava/classpath/javax/swing/JInternalFrame.java
@@ -0,0 +1,1820 @@
+/* JInternalFrame.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.IllegalComponentStateException;
+import java.awt.KeyboardFocusManager;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyVetoException;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.InternalFrameEvent;
+import javax.swing.event.InternalFrameListener;
+import javax.swing.plaf.DesktopIconUI;
+import javax.swing.plaf.InternalFrameUI;
+
+/**
+ * This class implements a Swing widget that looks and acts like a native
+ * frame. The frame can be dragged, resized, closed, etc. Typically,
+ * JInternalFrames are placed in JDesktopPanes. The actions that the
+ * JInternalFrame performs (maximizing, minimizing, etc.) are performed by a
+ * DesktopManager. As with regular frames, components are added by calling
+ * frame.getContentPane().add.
+ */
+public class JInternalFrame extends JComponent implements Accessible,
+ WindowConstants,
+ RootPaneContainer
+{
+
+ private static final long serialVersionUID = -5425177187760785402L;
+
+ /**
+ * Provides the accessibility features for the JInternalFrame
+ * component.
+ */
+ protected class AccessibleJInternalFrame extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = 5931936924175476797L;
+
+ /**
+ * Creates a new AccessibleJInternalFrame instance.
+ */
+ protected AccessibleJInternalFrame()
+ {
+ super();
+ }
+
+ /**
+ * Returns the frame title.
+ *
+ * @return The frame title.
+ */
+ public String getAccessibleName()
+ {
+ return getTitle();
+ }
+
+ /**
+ * Returns the accessible role for the JInternalFrame
+ * component.
+ *
+ * @return {@link AccessibleRole#INTERNAL_FRAME}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.INTERNAL_FRAME;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JInternalFrame}. Since this class
+ * implements {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current layer for the {@link JInternalFrame} component,
+ * as an {@link Integer}.
+ *
+ * @return The layer for the {@link JInternalFrame} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getLayer());
+ }
+
+ /**
+ * Returns the maximum permitted accessible value.
+ *
+ * @return Integer(Integer.MAX_VALUE).
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the minimum permitted accessible value.
+ *
+ * @return Integer(Integer.MIN_VALUE).
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(Integer.MIN_VALUE);
+ }
+
+ /**
+ * Sets the layer for the internal frame.
+ *
+ * @param n the layer (see the constants defined in {@link JLayeredPane}).
+ *
+ * @return true if the value is set, and false
+ * if it was not set.
+ */
+ public boolean setCurrentAccessibleValue(Number n)
+ {
+ if (n == null)
+ return false;
+ setLayer(n.intValue());
+ return true;
+ }
+ }
+
+ /**
+ * This class represents the JInternalFrame while it is iconified.
+ */
+ public static class JDesktopIcon extends JComponent implements Accessible
+ {
+ /**
+ * Provides the accessibility features for the JDesktopIcon
+ * component.
+ */
+ protected class AccessibleJDesktopIcon extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = 5035560458941637802L;
+
+ /**
+ * Creates a new AccessibleJDesktopIcon instance.
+ */
+ protected AccessibleJDesktopIcon()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible role for the JDesktopIcon
+ * component.
+ *
+ * @return {@link AccessibleRole#DESKTOP_ICON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.DESKTOP_ICON;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JDesktopIcon}. Since this class
+ * implements {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current layer for the {@link JInternalFrame} component
+ * represented by this JDesktopIcon, as an {@link Integer}.
+ *
+ * @return The layer.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(frame.getLayer());
+ }
+
+ /**
+ * Returns the maximum permitted accessible value.
+ *
+ * @return Integer(Integer.MAX_VALUE).
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(Integer.MAX_VALUE);
+ }
+
+ /**
+ * Returns the minimum permitted accessible value.
+ *
+ * @return Integer(Integer.MIN_VALUE).
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(Integer.MIN_VALUE);
+ }
+
+ /**
+ * Sets the layer for the internal frame represented by this
+ * JDesktopIcon component.
+ *
+ * @param n the layer (see the constants defined in
+ * {@link JLayeredPane}).
+ *
+ * @return true if the value is set, and false
+ * if it was not set.
+ */
+ public boolean setCurrentAccessibleValue(Number n)
+ {
+ if (n == null)
+ return false;
+ frame.setLayer(n.intValue());
+ return true;
+ }
+ }
+
+ private static final long serialVersionUID = 4672973344731387687L;
+
+ /** The JInternalFrame this DesktopIcon represents. */
+ JInternalFrame frame;
+
+ /**
+ * Creates a new JDesktopIcon object for representing the given frame.
+ *
+ * @param f The JInternalFrame to represent.
+ */
+ public JDesktopIcon(JInternalFrame f)
+ {
+ frame = f;
+ updateUI();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JDesktopIcon component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJDesktopIcon}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJDesktopIcon();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the JDesktopPane this JDesktopIcon is in.
+ *
+ * @return The JDesktopPane this JDesktopIcon is in.
+ */
+ public JDesktopPane getDesktopPane()
+ {
+ JDesktopPane p = (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ this);
+ return p;
+ }
+
+ /**
+ * This method returns the JInternalFrame this JDesktopIcon represents.
+ *
+ * @return The JInternalFrame this JDesktopIcon represents.
+ */
+ public JInternalFrame getInternalFrame()
+ {
+ return frame;
+ }
+
+ /**
+ * This method returns the UI that is responsible for the JDesktopIcon.
+ *
+ * @return The UI that is responsible for the JDesktopIcon.
+ */
+ public DesktopIconUI getUI()
+ {
+ return (DesktopIconUI) ui;
+ }
+
+ /**
+ * This method returns the String identifier that is used to determine
+ * which class is used for JDesktopIcon's UI.
+ *
+ * @return A String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "DesktopIconUI";
+ }
+
+ /**
+ * This method sets the JInternalFrame that this JDesktopIcon represents.
+ *
+ * @param f The JInternalFrame that this JDesktopIcon represents.
+ */
+ public void setInternalFrame(JInternalFrame f)
+ {
+ frame = f;
+ }
+
+ /**
+ * This method sets the UI used for this JDesktopIcon.
+ *
+ * @param ui The UI to use.
+ */
+ public void setUI(DesktopIconUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method restores the UI property to the defaults.
+ */
+ public void updateUI()
+ {
+ setUI((DesktopIconUI) UIManager.getUI(this));
+ }
+ }
+
+ /**
+ * The property fired in a PropertyChangeEvent when the contentPane property
+ * changes.
+ */
+ public static final String CONTENT_PANE_PROPERTY = "contentPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the frameIcon property
+ * changes.
+ */
+ public static final String FRAME_ICON_PROPERTY = "frameIcon";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the glassPane property
+ * changes.
+ */
+ public static final String GLASS_PANE_PROPERTY = "glassPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the closed property
+ * changes.
+ */
+ public static final String IS_CLOSED_PROPERTY = "closed";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the icon property
+ * changes.
+ */
+ public static final String IS_ICON_PROPERTY = "icon";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the maximum property
+ * changes.
+ */
+ public static final String IS_MAXIMUM_PROPERTY = "maximum";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the selected property
+ * changes.
+ */
+ public static final String IS_SELECTED_PROPERTY = "selected";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the layeredPane property
+ * changes.
+ */
+ public static final String LAYERED_PANE_PROPERTY = "layeredPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the jMenuBar property
+ * changes.
+ */
+ public static final String MENU_BAR_PROPERTY = "JMenuBar";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the rootPane property
+ * changes.
+ */
+ public static final String ROOT_PANE_PROPERTY = "rootPane";
+
+ /**
+ * The property fired in a PropertyChangeEvent when the title property
+ * changes.
+ */
+ public static final String TITLE_PROPERTY = "title";
+
+ /** Whether the JInternalFrame is closable. */
+ protected boolean closable;
+
+ /** Whether the JInternalFrame can be iconified. */
+ protected boolean iconable;
+
+ /** Whether the JInternalFrame is closed. */
+ protected boolean isClosed;
+
+ /** Whether the JInternalFrame has been iconified. */
+ protected boolean isIcon;
+
+ /** Whether the JInternalFrame has been maximized. */
+ protected boolean isMaximum;
+
+ /** Whether the JInternalFrame is the active frame. */
+ protected boolean isSelected;
+
+ /** Whether the JInternalFrame can be maximized. */
+ protected boolean maximizable;
+
+ /**
+ * Whether the JInternalFrame has rootPaneChecking enabled.
+ *
+ * @specnote Should be false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ /** Whether the JInternalFrame is resizable. */
+ protected boolean resizable;
+
+ /**
+ * The JDesktopIcon that represents the JInternalFrame while it is
+ * iconified.
+ */
+ protected JDesktopIcon desktopIcon;
+
+ /** The icon used in the JMenuBar in the TitlePane. */
+ protected Icon frameIcon;
+
+ /** The rootPane of the JInternalFrame. */
+ protected JRootPane rootPane;
+
+ /** The title on the TitlePane of the JInternalFrame. */
+ protected String title;
+
+ /** The bounds of the JInternalFrame before it was maximized. */
+ private transient Rectangle storedBounds;
+
+ /** The Component that receives focus by default. */
+ private transient Component defaultFocus;
+
+ /** The default close action taken, */
+ private transient int defaultCloseOperation = DISPOSE_ON_CLOSE;
+
+ /** Whether the JInternalFrame has become visible for the very first time. */
+ private transient boolean isFirstTimeVisible = true;
+
+ /** DOCUMENT ME! */
+ private transient boolean wasIcon = false;
+
+ /**
+ * Creates a new JInternalFrame object that has an empty string for its
+ * title, and is non-resizable, non-maximizable, non-iconifiable, and
+ * non-closable.
+ */
+ public JInternalFrame()
+ {
+ this("", false, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title and is
+ * non-resizable, non-maximizable, non-iconifiable, and non-closable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ */
+ public JInternalFrame(String title)
+ {
+ this(title, false, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title and resizable
+ * properties. The JInternalFrame is non-maximizable, non-iconifiable, and
+ * non-closable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ */
+ public JInternalFrame(String title, boolean resizable)
+ {
+ this(title, resizable, false, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable, and
+ * closable properties. The JInternalFrame is non-maximizable and
+ * non-iconifiable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable)
+ {
+ this(title, resizable, closable, false, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable,
+ * closable and maximizable properties. The JInternalFrame is
+ * non-iconifiable.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ * @param maximizable Whether the JInternalFrame is maximizable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable,
+ boolean maximizable)
+ {
+ this(title, resizable, closable, maximizable, false);
+ }
+
+ /**
+ * Creates a new JInternalFrame object with the given title, resizable,
+ * closable, maximizable and iconifiable properties.
+ *
+ * @param title The title displayed in the JInternalFrame.
+ * @param resizable Whether the JInternalFrame is resizable.
+ * @param closable Whether the JInternalFrame is closable.
+ * @param maximizable Whether the JInternalFrame is maximizable.
+ * @param iconifiable Whether the JInternalFrame is iconifiable.
+ */
+ public JInternalFrame(String title, boolean resizable, boolean closable,
+ boolean maximizable, boolean iconifiable)
+ {
+ this.title = title;
+ this.resizable = resizable;
+ this.closable = closable;
+ this.maximizable = maximizable;
+ this.iconable = iconifiable;
+ isMaximum = false;
+ setRootPane(createRootPane());
+ // JInternalFrames are invisible and opaque by default.
+ setVisible(false);
+ setOpaque(true);
+ desktopIcon = new JDesktopIcon(this);
+ updateUI();
+ setRootPaneCheckingEnabled(true); // Done the init stage, now adds go to content pane.
+ }
+
+ /**
+ * This method adds Components to this Container. For JInternalFrames,
+ * instead of calling add directly on the JInternalFrame, it should be
+ * called with JInternalFrame.getContentPane().add. If root pane checking
+ * is enabled, calling this method will cause an exception to be thrown.
+ *
+ * @param comp The Component to add.
+ * @param constraints The constraints on the Component added.
+ * @param index The position to place the Component.
+ *
+ * @throws Error DOCUMENT ME!
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're in the initialization stage use super.add. Here we add the
+ // rootPane as well as the title bar and other stuff.
+ // Otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp,constraints, index);
+ }
+
+ /**
+ * This method adds an InternalFrameListener to this JInternalFrame.
+ *
+ * @param l The listener to add.
+ */
+ public void addInternalFrameListener(InternalFrameListener l)
+ {
+ listenerList.add(InternalFrameListener.class, l);
+ }
+
+ /**
+ * This method is used to create a root pane for the JInternalFrame. This
+ * method is called by the constructors.
+ *
+ * @return A root pane for the JInternalFrame to use.
+ */
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ /**
+ * This method makes this JInternalFrame invisible, unselected and closed.
+ * If this JInternalFrame is not closed already, it will fire an
+ * INTERNAL_FRAME_CLoSED event. This method is similar to setClosed but it
+ * doesn't give vetoable listeners a chance to veto and it will not fire an
+ * INTERNAL_FRAME_CLOSING event.
+ */
+ public void dispose()
+ {
+ if (isVisible())
+ setVisible(false);
+ if (isSelected())
+ {
+ try
+ {
+ setSelected(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if they don't want to be unselected.
+ }
+ }
+ if (! isClosed)
+ {
+ firePropertyChange(IS_CLOSED_PROPERTY, Boolean.FALSE, Boolean.TRUE);
+ isClosed = true;
+ }
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSED);
+ }
+
+ /**
+ * This method is used for closing this JInternalFrame. It fires an
+ * INTERNAL_FRAME_CLOSING event and then performs the action specified by
+ * the default close operation.
+ */
+ public void doDefaultCloseAction()
+ {
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING);
+ switch (getDefaultCloseOperation())
+ {
+ case HIDE_ON_CLOSE:
+ setVisible(false);
+ break;
+ case DISPOSE_ON_CLOSE:
+ dispose();
+ break;
+ }
+ }
+
+ /**
+ * This method fires an InternalFrameEvent to the listeners.
+ *
+ * @param id The type of event being fired. See InternalFrameEvent.
+ */
+ protected void fireInternalFrameEvent(int id)
+ {
+ Object[] ifListeners = listenerList.getListenerList();
+ InternalFrameEvent evt = new InternalFrameEvent(this, id);
+ switch (id)
+ {
+ case InternalFrameEvent.INTERNAL_FRAME_CLOSING:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameClosing(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_ACTIVATED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameActivated(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_CLOSED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1]).internalFrameClosed(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameDeactivated(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_DEICONIFIED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameDeiconified(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_ICONIFIED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1])
+ .internalFrameIconified(evt);
+ }
+ break;
+ case InternalFrameEvent.INTERNAL_FRAME_OPENED:
+ for (int i = ifListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (ifListeners[i] == InternalFrameListener.class)
+ ((InternalFrameListener) ifListeners[i + 1]).internalFrameOpened(evt);
+ }
+ break;
+ }
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JInternalFrame component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJInternalFrame}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJInternalFrame();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the Content Pane for this JInternalFrame.
+ *
+ * @return The Content Pane for this JInternalFrame.
+ */
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ /**
+ * Returns a code for the default action taken when this
+ * JInternalFrame is closed.
+ *
+ * @return The action code (usually one of
+ * {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE}, or
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}).
+ *
+ * @see #setDefaultCloseOperation(int)
+ * @see #doDefaultCloseAction()
+ */
+ public int getDefaultCloseOperation()
+ {
+ return defaultCloseOperation;
+ }
+
+ /**
+ * Returns the JDesktopIcon that represents this
+ * JInternalFrame while it is iconified.
+ *
+ * @return The desktop icon component.
+ */
+ public JDesktopIcon getDesktopIcon()
+ {
+ return desktopIcon;
+ }
+
+ /**
+ * This method searches this JInternalFrame ancestors for an instance of
+ * JDesktopPane. If one is found, it is returned. If none is found, then it
+ * will search the JDesktopIcon for a JDesktopPane.
+ *
+ * @return The JDesktopPane that this JInternalFrame belongs to.
+ */
+ public JDesktopPane getDesktopPane()
+ {
+ JDesktopPane value = (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ this);
+ if (value == null && desktopIcon != null)
+ value = desktopIcon.getDesktopPane();
+ return value;
+ }
+
+ /**
+ * This method returns null because this must always be the root of a focus
+ * traversal.
+ *
+ * @return always null
+ *
+ * @since 1.4
+ */
+ public final Container getFocusCycleRootAncestor()
+ {
+ // as defined.
+ return null;
+ }
+
+ /**
+ * This method returns the child Component that will receive focus if this
+ * JInternalFrame is selected.
+ *
+ * @return The child Component that will receive focus.
+ */
+ public Component getFocusOwner()
+ {
+ if (isSelected())
+ {
+ Component focus = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
+ if (SwingUtilities.isDescendingFrom(focus, this))
+ {
+ defaultFocus = focus;
+ return focus;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the Frame Icon (the icon used in the JInternalFrame
+ * TitlePane and iconified frame).
+ *
+ * @return The Frame Icon.
+ */
+ public Icon getFrameIcon()
+ {
+ return frameIcon;
+ }
+
+ /**
+ * This method returns the Glass Pane used with this JInternalFrame.
+ *
+ * @return The Glass Pane used with this JInternalFrame.
+ */
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ /**
+ * This method returns an array of InternalFrameListeners that are listening
+ * to this JInternalFrame.
+ *
+ * @return An array of InternalFrameListeners that are listening to this
+ * JInternalFrame.
+ */
+ public InternalFrameListener[] getInternalFrameListeners()
+ {
+ return (InternalFrameListener[]) listenerList.getListeners(InternalFrameListener.class);
+ }
+
+ /**
+ * This method returns the JMenuBar for this JInternalFrame.
+ *
+ * @return The JMenuBar for this JInternalFrame.
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return getRootPane().getJMenuBar();
+ }
+
+ /**
+ * This method returns the layer that this JInternalFrame resides in.
+ *
+ * @return The layer that this JInternalFrame resides in.
+ */
+ public int getLayer()
+ {
+ return JLayeredPane.getLayer(this);
+ }
+
+ /**
+ * This method returns the LayeredPane for this JInternalFrame.
+ *
+ * @return The LayeredPane for this JInternalFrame.
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ /**
+ * This method is deprecated. This method returns the JMenuBar for this
+ * JInternalFrame.
+ *
+ * @return The JMenuBar for this JInternalFrame.
+ *
+ * @deprecated 1.0.3
+ */
+ public JMenuBar getMenuBar()
+ {
+ return getJMenuBar();
+ }
+
+ /**
+ * This method returns the child Component that will receive focus when the
+ * JInternalFrame is selected. If the JInternalFrame is selected, this
+ * method returns getFocusOwner(). Otherwise, it will return the child
+ * Component that most recently requested focus. If that is null, then the
+ * initial focus Component is returned. If that is null, then the default
+ * focus component is returned.
+ *
+ * @return The most recent focus owner.
+ */
+ public Component getMostRecentFocusOwner()
+ {
+ if (isSelected())
+ return getFocusOwner();
+ else
+ return defaultFocus;
+ }
+
+ /**
+ * This method returns the bounds of the JInternalFrame if it is not
+ * maximized. If it is maximized, it returns the bounds of the
+ * JInternalFrame before it was maximized (the bounds that it will be
+ * restored to).
+ *
+ * @return A Rectangle that contains this JInternalFrame's normal bounds (or
+ * just its bounds if it is not maximized).
+ */
+ public Rectangle getNormalBounds()
+ {
+ if (storedBounds == null)
+ return getBounds();
+ else
+ return storedBounds;
+ }
+
+ /**
+ * This method returns the Root Pane for this JInternalFrame.
+ *
+ * @return The Root Pane for this JInternalFrame.
+ */
+ public JRootPane getRootPane()
+ {
+ return rootPane;
+ }
+
+ /**
+ * Returns the frame's title.
+ *
+ * @return The frame's title (can be null).
+ *
+ * @see #setTitle(String)
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ /**
+ * This method returns the UI used to represent the JInternalFrame.
+ *
+ * @return The UI used to represent the JInternalFrame.
+ */
+ public InternalFrameUI getUI()
+ {
+ return (InternalFrameUI) ui;
+ }
+
+ /**
+ * This method returns a String identifier that is used to determine which
+ * class acts as the JInternalFrame's UI.
+ *
+ * @return A String identifier to determine a UI class.
+ */
+ public String getUIClassID()
+ {
+ return "InternalFrameUI";
+ }
+
+ /**
+ * This method returns null.
+ *
+ * @return null.
+ */
+ public final String getWarningString()
+ {
+ // as defined.
+ return null;
+ }
+
+ /**
+ * This method deselects this JInternalFrame and hides it.
+ */
+ public void hide()
+ {
+ if (isIcon())
+ getDesktopIcon().hide();
+ super.hide();
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is closable.
+ *
+ * @return Whether this JInternalFrame is closable.
+ */
+ public boolean isClosable()
+ {
+ return closable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame has been closed.
+ *
+ * @return Whether this JInternalFrame is closed.
+ */
+ public boolean isClosed()
+ {
+ return isClosed;
+ }
+
+ /**
+ * This must always return true.
+ *
+ * @return always true
+ *
+ * @since 1.4
+ */
+ public final boolean isFocusCycleRoot()
+ {
+ return true;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is currently iconified.
+ *
+ * @return Whether this JInternalFrame is currently iconified.
+ */
+ public boolean isIcon()
+ {
+ return isIcon;
+ }
+
+ /**
+ * This method returns whether the JInternalFrame can be iconified.
+ *
+ * @return Whether the JInternalFrame can be iconified.
+ */
+ public boolean isIconifiable()
+ {
+ return iconable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame can be maximized.
+ *
+ * @return Whether this JInternalFrame can be maximized.
+ */
+ public boolean isMaximizable()
+ {
+ return maximizable;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is currently maximized.
+ *
+ * @return Whether this JInternalFrame is maximized.
+ */
+ public boolean isMaximum()
+ {
+ return isMaximum;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is resizable.
+ *
+ * @return Whether this JInternalFrame is resizable.
+ */
+ public boolean isResizable()
+ {
+ return resizable;
+ }
+
+ /**
+ * This method returns whether root pane checking is enabled. If root pane
+ * checking is enabled, then calls to addImpl and setLayout will throw
+ * exceptions.
+ *
+ * @return Whether root pane checking is enabled.
+ */
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ /**
+ * This method returns whether this JInternalFrame is selected.
+ *
+ * @return Whether this JInternalFrame is selected.
+ */
+ public boolean isSelected()
+ {
+ return isSelected;
+ }
+
+ /**
+ * A helper method that moves this JInternalFrame to the back if the parent
+ * is a JLayeredPane.
+ */
+ public void moveToBack()
+ {
+ Container p = getParent();
+ if (p instanceof JLayeredPane)
+ ((JLayeredPane) p).moveToBack(this);
+ }
+
+ /**
+ * A helper method that moves this JInternalFrame to the front if the parent
+ * is a JLayeredPane.
+ */
+ public void moveToFront()
+ {
+ Container p = getParent();
+ if (p != null && p instanceof JLayeredPane)
+ ((JLayeredPane) p).moveToFront(this);
+ }
+
+ /**
+ * This method causes the children of this JInternalFrame to be laid out.
+ * Before it begins, if this JInternalFrame is an icon, then it will be
+ * deiconified. If it is maximized, then it will be restored. If either
+ * operation fails, then this method will return.
+ */
+ public void pack()
+ {
+ try
+ {
+ if (isIcon())
+ setIcon(false);
+ else if (isMaximum())
+ setMaximum(false);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing if they don't want to be restored first.
+ }
+ setSize(getPreferredSize());
+ validate();
+ }
+
+ /**
+ * This method is overridden to allow for speedier painting while this
+ * JInternalFramme is being dragged.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintComponent(Graphics g)
+ {
+ super.paintComponent(g);
+ }
+
+ /**
+ * An implementation dependent string describing the current state of this
+ * JInternalFrame instance.
+ *
+ * @return A string describing the current state of this
+ * JInternalFrame instance.
+ */
+ protected String paramString()
+ {
+ return super.paramString() + ",title=" + getTitle();
+ }
+
+ /**
+ * This method removes the given Component from the Container.
+ *
+ * @param comp The Component to remove.
+ */
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp==rootPane || ! isRootPaneCheckingEnabled())
+ super.remove(comp);
+ else
+ getContentPane().remove(comp);
+ }
+
+ /**
+ * This method removes an InternalFrameListener from this JInternalFrame.
+ *
+ * @param l The listener to remove.
+ */
+ public void removeInternalFrameListener(InternalFrameListener l)
+ {
+ listenerList.remove(InternalFrameListener.class, l);
+ }
+
+ /**
+ * This method resizes and positions this JInternalFrame. It also forces a
+ * relayout of the Container.
+ *
+ * @param x The x position of this JInternalFrame.
+ * @param y The y position of this JInternalFrame.
+ * @param width The width of this JInternalFrame.
+ * @param height The height of this JInternalFrame.
+ */
+ public void reshape(int x, int y, int width, int height)
+ {
+ super.reshape(x, y, width, height);
+ revalidate();
+ }
+
+ /**
+ * This method gives focus to the last child Component that had focus. This
+ * is used by the UI when this JInternalFrame is activated.
+ */
+ public void restoreSubcomponentFocus()
+ {
+ Component c = getMostRecentFocusOwner();
+ if (c != null)
+ c.requestFocus();
+ }
+
+ /**
+ * This method sets whether this JInternalFrame can be closed.
+ *
+ * @param b Whether this JInternalFrame can be closed.
+ */
+ public void setClosable(boolean b)
+ {
+ if (closable != b)
+ {
+ closable = b;
+ firePropertyChange("closable", ! closable, closable);
+ }
+ }
+
+ /**
+ * This method closes the JInternalFrame if the given boolean is true. If it
+ * is false, then the result of this method is unspecified. If the
+ * JInternalFrame is closed, this method does nothing. This method will
+ * first fire an INTERNAL_FRAME_CLOSING event and give a chance for veto
+ * listeners to cancel the close. If no listener vetoes the change, the
+ * closed property is set to true and the JInternalFrame is hidden and
+ * unselected. The method will finish by firing an INTERNAL_FRAME_CLOSED
+ * event.
+ *
+ * @param b Whether the JInternalFrame will be closed.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setClosed(boolean b) throws PropertyVetoException
+ {
+ if (b && ! isClosed())
+ {
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_CLOSING);
+ fireVetoableChange(IS_CLOSED_PROPERTY, false, true);
+
+ isClosed = b;
+ dispose();
+
+ firePropertyChange(IS_CLOSED_PROPERTY, false, true);
+ }
+ }
+
+ /**
+ * This method sets the Container to be used as a Content Pane for this
+ * JInternalFrame.
+ *
+ * @param c The Container to use as a Content Pane.
+ */
+ public void setContentPane(Container c)
+ {
+ if (c != getContentPane())
+ {
+ Container old = getContentPane();
+ getRootPane().setContentPane(c);
+ firePropertyChange(CONTENT_PANE_PROPERTY, old, c);
+ }
+ }
+
+ /**
+ * Sets a code for the action to be taken when this
+ * JInternalFrame is closed. Note that no validation is
+ * performed on the operation code, any integer will be
+ * accepted (nevertheless, you should pass in one of the listed values).
+ *
+ * @param operation one of {@link WindowConstants#DO_NOTHING_ON_CLOSE},
+ * {@link WindowConstants#HIDE_ON_CLOSE} or
+ * {@link WindowConstants#DISPOSE_ON_CLOSE}.
+ *
+ * @see #getDefaultCloseOperation()
+ * @see #doDefaultCloseAction()
+ */
+ public void setDefaultCloseOperation(int operation)
+ {
+ /* Reference implementation allows invalid operations to be specified.
+ In that case, behaviour defaults to DO_NOTHING_ON_CLOSE.
+ processWindowEvent handles the behaviour. getDefaultCloseOperation
+ must return the invalid operator code. */
+ defaultCloseOperation = operation;
+ }
+
+ /**
+ * Sets the JDesktopIcon instance that represents this
+ * JInternalFrame while it is iconified and, if the new icon is
+ * not the same instance as the existing icon, sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * "desktopIcon") to all registered listeners..
+ *
+ * @param d the icon.
+ *
+ * @see #getDesktopIcon()
+ */
+ public void setDesktopIcon(JDesktopIcon d)
+ {
+ if (desktopIcon != d)
+ {
+ JDesktopIcon oldIcon = desktopIcon;
+ desktopIcon = d;
+ firePropertyChange("desktopIcon", oldIcon, d);
+ }
+ }
+
+ /**
+ * This method does nothing because this must be the root of a focus
+ * traversal cycle.
+ *
+ * @param focusCycleRoot Not used.
+ */
+ public final void setFocusCycleRoot(boolean focusCycleRoot)
+ {
+ // Do nothing
+ }
+
+ /**
+ * This method sets the Icon to be used in two places. The first is icon
+ * that is painted at the top left corner of the JInternalFrame when it is
+ * not iconified (clicking on that icon will activate the TitlePane
+ * JMenuBar). When the JInternalFrame is iconified, it will be the icon
+ * displayed in the JDesktopIcon. If no icon is set, the JInternalFrame
+ * will use a Look and Feel default.
+ *
+ * @param icon The Icon used in the TitlePane JMenuBar and iconified frames.
+ */
+ public void setFrameIcon(Icon icon)
+ {
+ if (icon != frameIcon)
+ {
+ Icon old = frameIcon;
+ frameIcon = icon;
+ firePropertyChange(FRAME_ICON_PROPERTY, old, frameIcon);
+ }
+ }
+
+ /**
+ * This method sets the Glass Pane used with this JInternalFrame.
+ *
+ * @param glass The Glass Pane to use with this JInternalFrame.
+ */
+ public void setGlassPane(Component glass)
+ {
+ if (glass != getGlassPane())
+ {
+ Component old = getGlassPane();
+ getRootPane().setGlassPane(glass);
+ firePropertyChange(GLASS_PANE_PROPERTY, old, glass);
+ }
+ }
+
+ /**
+ * This method iconifies or deiconifies this JInternalFrame given the
+ * boolean argument. If the JInternalFrame becomes iconified, it will fire
+ * an INTERNAL_FRAME_ICONIFIED event. If the JInternalFrame becomes
+ * deiconified, it will fire anINTERNAL_FRAME_DEICONIFIED event.
+ *
+ * @param b Whether this JInternalFrame is to be iconified or deiconified.
+ *
+ * @throws PropertyVetoException DOCUMENT ME!
+ */
+ public void setIcon(boolean b) throws PropertyVetoException
+ {
+ if (b != isIcon())
+ {
+ fireVetoableChange(IS_ICON_PROPERTY, b, isIcon);
+
+ isIcon = b;
+
+ firePropertyChange(IS_ICON_PROPERTY, ! isIcon, isIcon);
+ if (b)
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ICONIFIED);
+ else
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEICONIFIED);
+ }
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be iconified. (This means
+ * that the JInternalFrame can be turned into an icon if minimized).
+ *
+ * @param b Whether the JInternalFrame can be iconified.
+ */
+ public void setIconifiable(boolean b)
+ {
+ if (iconable != b)
+ {
+ iconable = b;
+ firePropertyChange("iconable", ! iconable, iconable);
+ }
+ }
+
+ /**
+ * This method sets the JMenuBar to be used with this JInternalFrame.
+ *
+ * @param b The JMenuBar to be used with this JInternalFrame.
+ */
+ public void setJMenuBar(JMenuBar b)
+ {
+ JMenuBar old = getJMenuBar();
+ getRootPane().setJMenuBar(b);
+ firePropertyChange(MENU_BAR_PROPERTY, old, b);
+ }
+
+ /**
+ * A helper method that set the layer that this JInternalFrame resides in.
+ * Using this version of the method means that the user should not set it
+ * to values that are already defined in JLayeredPane. If predefined values
+ * are to be used, the user should use the setLayer(Integer) version.
+ *
+ * @param layer The layer to place this JInternalFrame in.
+ */
+ public void setLayer(int layer)
+ {
+ setLayer(new Integer(layer));
+ }
+
+ /**
+ * A helper method that sets the layer that this JInternalFrame resides in.
+ * Calling this version of the method should use layer values that are
+ * already defined in JLayeredPane.
+ *
+ * @param layer The layer to place this JInternalFrame in.
+ */
+ public void setLayer(Integer layer)
+ {
+ Container p = getParent();
+ if (p instanceof JLayeredPane)
+ {
+ JLayeredPane lp = (JLayeredPane) p;
+ lp.setLayer(this, layer.intValue(), lp.getPosition(this));
+ }
+ else
+ {
+ JLayeredPane.putLayer(this, layer.intValue());
+ if (p != null)
+ p.repaint(getX(), getY(), getWidth(), getHeight());
+ }
+ }
+
+ /**
+ * This method sets the JLayeredPane to use with this JInternalFrame.
+ *
+ * @param layered The JLayeredPane to use as a layeredPane.
+ */
+ public void setLayeredPane(JLayeredPane layered)
+ {
+ if (layered == null)
+ throw new IllegalComponentStateException("LayeredPane must not be null");
+
+ if (layered != getLayeredPane())
+ {
+ JLayeredPane old = getLayeredPane();
+ getRootPane().setLayeredPane(layered);
+ firePropertyChange(LAYERED_PANE_PROPERTY, old, layered);
+ }
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be maximized.
+ *
+ * @param b Whether this JInternalFrame can be maximized.
+ */
+ public void setMaximizable(boolean b)
+ {
+ if (maximizable != b)
+ {
+ maximizable = b;
+ firePropertyChange("maximizable", ! maximizable, maximizable);
+ }
+ }
+
+ /**
+ * This method sets the Layout Manager used in the JInternalFrame. SetLayout
+ * should not be called on the JInternalFrame directly. Instead, it should
+ * be called with JInternalFrame.getContentPane().setLayout. Calls to this
+ * method with root pane checking enabled will cause exceptions to be
+ * thrown.
+ *
+ * @param manager The Layout Manager to be used with the JInternalFrame.
+ *
+ * @throws Error If rootPaneChecking is enabled.
+ */
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ /**
+ * This method sets the JInternalFrame to maximized (if the given argument
+ * is true) or restores the JInternalFrame to its normal bounds otherwise.
+ *
+ * @param b Whether this JInteralFrame will be maximized or restored.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setMaximum(boolean b) throws PropertyVetoException
+ {
+ if (b != isMaximum)
+ {
+ fireVetoableChange(IS_MAXIMUM_PROPERTY, isMaximum, b);
+ isMaximum = b;
+ firePropertyChange(IS_MAXIMUM_PROPERTY, ! isMaximum, isMaximum);
+ }
+ }
+
+ /**
+ * This method is deprecated. This method sets the JMenuBar used with this
+ * JInternalFrame.
+ *
+ * @param m The JMenuBar to use with this JInternalFrame.
+ *
+ * @deprecated 1.0.3
+ */
+ public void setMenuBar(JMenuBar m)
+ {
+ setJMenuBar(m);
+ }
+
+ /**
+ * This method sets the bounds that this JInternalFrame will be restored to.
+ *
+ * @param r The bounds that this JInternalFrame will be restored to.
+ */
+ public void setNormalBounds(Rectangle r)
+ {
+ storedBounds = r;
+ }
+
+ /**
+ * This method sets whether the JInternalFrame can be resized by a user
+ * action (like dragging at the frame borders).
+ *
+ * @param b Whether this JInternalFramer can be resized.
+ */
+ public void setResizable(boolean b)
+ {
+ if (b != resizable)
+ {
+ resizable = b;
+ firePropertyChange("resizable", ! resizable, resizable);
+ }
+ }
+
+ /**
+ * This method sets the Root Pane for this JInternalFrame.
+ *
+ * @param root The Root Pane for this JInternalFrame.
+ */
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ JRootPane old = rootPane;
+ rootPane = root;
+
+ if (rootPane != null)
+ {
+ boolean checkingEnabled = isRootPaneCheckingEnabled();
+ try
+ {
+ setRootPaneCheckingEnabled(false);
+ add(rootPane, BorderLayout.CENTER);
+ }
+ finally
+ {
+ setRootPaneCheckingEnabled(checkingEnabled);
+ }
+ }
+ firePropertyChange(ROOT_PANE_PROPERTY, old, rootPane);
+ }
+
+ /**
+ * This method sets whether root pane checking is enabled. If root pane
+ * checking is enabled, then calls to addImpl and setLayout will throw
+ * exceptions.
+ *
+ * @param enabled Whether root pane checking is enabled.
+ */
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ /**
+ * This method sets whether this JInternalFrame is the selected frame in the
+ * JDesktopPane (or other container). When selected, a JInternalFrame will
+ * have focus and paint its TitlePane differently (usually a different
+ * colour). If this method selects the frame, this JInternalFrame will fire
+ * an INTERNAL_FRAME_ACTIVATED event. If it deselects this frame, it will
+ * fire an INTERNAL_FRAME_DEACTIVATED event.
+ *
+ * @param selected Whether this JInternalFrame will become selected or
+ * deselected.
+ *
+ * @throws PropertyVetoException If a VetoableChangeListener vetoes the change.
+ */
+ public void setSelected(boolean selected) throws PropertyVetoException
+ {
+ if (selected != isSelected
+ && (! selected || (isIcon ? desktopIcon.isShowing() : isShowing())))
+ {
+ fireVetoableChange(IS_SELECTED_PROPERTY, isSelected, selected);
+
+ if (! selected)
+ defaultFocus = getMostRecentFocusOwner();
+
+ isSelected = selected;
+ firePropertyChange(IS_SELECTED_PROPERTY, ! isSelected, isSelected);
+
+ if (isSelected)
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_ACTIVATED);
+ else
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_DEACTIVATED);
+
+ if (selected)
+ restoreSubcomponentFocus();
+
+ repaint();
+ }
+ }
+
+ /**
+ * Sets the title for the JInternalFrame and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #TITLE_PROPERTY}) to all registered listeners.
+ *
+ * @param title the new title (null permitted).
+ *
+ * @see #getTitle()
+ */
+ public void setTitle(String title)
+ {
+ String old = this.title;
+ this.title = title;
+ firePropertyChange(TITLE_PROPERTY, old, this.title);
+ }
+
+ /**
+ * This method displays the JInternalFrame. If it is not visible, this
+ * method will bring this JInternalFrame to the front, make it visible and
+ * select it. If this is the first time this JInternalFrame is made
+ * visible, an INTERNAL_FRAME_OPENED event will be fired.
+ */
+ public void show()
+ {
+ if (! isVisible())
+ {
+ if (isFirstTimeVisible)
+ {
+ isFirstTimeVisible = false;
+ fireInternalFrameEvent(InternalFrameEvent.INTERNAL_FRAME_OPENED);
+ }
+
+ getDesktopIcon().setVisible(true);
+
+ toFront();
+ super.show();
+
+ if (isIcon())
+ return;
+
+ if (! isSelected())
+ {
+ try
+ {
+ setSelected(true);
+ }
+ catch (PropertyVetoException e)
+ {
+ // Do nothing. if they don't want to be selected.
+ }
+ }
+ }
+ }
+
+ /**
+ * This method is used to set the UI responsible for the JInternalFrame.
+ *
+ * @param ui The UI responsible for the JInternalFrame.
+ */
+ public void setUI(InternalFrameUI ui)
+ {
+ // We must temporarily go into init mode so that the UI can directly
+ // manipulate the JInternalFrame.
+ boolean old = isRootPaneCheckingEnabled();
+ setRootPaneCheckingEnabled(false);
+ super.setUI(ui);
+ setRootPaneCheckingEnabled(old);
+ }
+
+ /**
+ * This method causes the JInternalFrame to be brough to back in the
+ * z-order.
+ */
+ public void toBack()
+ {
+ moveToBack();
+ }
+
+ /**
+ * This method causes the JInternalFrame to be brought to front in the
+ * z-order.
+ */
+ public void toFront()
+ {
+ moveToFront();
+ }
+
+ /**
+ * This method resets the UI to the Look and Feel defaults.
+ */
+ public void updateUI()
+ {
+ // We must go into the init stage when updating the UI, so the UI can
+ // set layout and components directly on the internal frame, not its
+ // content pane.
+ boolean old = isRootPaneCheckingEnabled();
+ setRootPaneCheckingEnabled(false);
+ setUI((InternalFrameUI) UIManager.getUI(this));
+ setRootPaneCheckingEnabled(old);
+ }
+
+ /**
+ * This helper method allows JInternalFrames to signal that they were
+ * iconned for the first time.
+ *
+ * @param b Whether the JInternalFrame was iconned.
+ * @param ID The identifier of the property change event to fire if the
+ * JInternalFrame is iconned for the first time.
+ */
+ void setWasIcon(boolean b, String ID)
+ {
+ if (b && ! wasIcon)
+ {
+ wasIcon = b;
+ firePropertyChange(ID, ! b, b);
+ }
+ }
+
+ /**
+ * This helper method returns whether the JInternalFrame has been iconned
+ * once already.
+ *
+ * @return Whether the JInternalFrame has been iconned once already.
+ */
+ boolean getWasIcon()
+ {
+ return wasIcon;
+ }
+
+ /**
+ * This method is a convenience method to fire vetoable property changes.
+ *
+ * @param name The identifier of the property change.
+ * @param oldValue The old value.
+ * @param newValue The new value.
+ *
+ * @throws PropertyVetoException Fired if a vetoable change listener vetoes
+ * the change.
+ */
+ private void fireVetoableChange(String name, boolean oldValue,
+ boolean newValue)
+ throws PropertyVetoException
+ {
+ super.fireVetoableChange(name, Boolean.valueOf(oldValue), Boolean.valueOf(newValue));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JLabel.java b/libjava/classpath/javax/swing/JLabel.java
new file mode 100644
index 000000000..72354c56e
--- /dev/null
+++ b/libjava/classpath/javax/swing/JLabel.java
@@ -0,0 +1,1122 @@
+/* JLabel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.KeyEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedComponent;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleText;
+import javax.swing.plaf.LabelUI;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Position;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.View;
+
+/**
+ * A component that displays a static text message and/or an icon.
+ */
+public class JLabel extends JComponent implements Accessible, SwingConstants
+{
+
+ /**
+ * Provides the accessibility features for the JLabel
+ * component.
+ */
+ protected class AccessibleJLabel
+ extends JComponent.AccessibleJComponent
+ implements AccessibleText, AccessibleExtendedComponent
+ {
+
+ /**
+ * Returns the accessible name.
+ *
+ * @return The accessible name.
+ */
+ public String getAccessibleName()
+ {
+ if (accessibleName != null)
+ return accessibleName;
+ if (text != null)
+ return text;
+ else
+ return super.getAccessibleName();
+ }
+
+ /**
+ * Returns the accessible role for the JLabel component.
+ *
+ * @return {@link AccessibleRole#LABEL}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LABEL;
+ }
+
+ /**
+ * Returns the selected text. This is null since JLabels
+ * are not selectable.
+ *
+ * @return null because JLabels cannot have selected text
+ */
+ public String getSelectedText()
+ {
+ // We return null here since JLabel's text is not selectable.
+ return null;
+ }
+
+ /**
+ * Returns the start index of the selected text.
+ *
+ * @return the start index of the selected text
+ */
+ public int getSelectionStart()
+ {
+ // JLabel don't have selected text, so we return -1 here.
+ return -1;
+ }
+
+ /**
+ * Returns the end index of the selected text.
+ *
+ * @return the end index of the selected text
+ */
+ public int getSelectionEnd()
+ {
+ // JLabel don't have selected text, so we return -1 here.
+ return -1;
+ }
+
+ /**
+ * Returns an {@link AttributeSet} that reflects the text attributes of
+ * the specified character. We return an empty
+ * AttributeSet here, because JLabels don't support text
+ * attributes (at least not yet).
+ *
+ * @param index the index of the character
+ *
+ * @return an {@link AttributeSet} that reflects the text attributes of
+ * the specified character
+ */
+ public AttributeSet getCharacterAttribute(int index)
+ {
+ // FIXME: Return null here for simple labels, and query the HTML
+ // view for HTML labels.
+ return new SimpleAttributeSet();
+ }
+
+ /**
+ * Returns the character, word or sentence at the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after index
+ */
+ public String getAtIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.lastIndexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.lastIndexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence after the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence after the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence after index
+ */
+ public String getAfterIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index + 1));
+ break;
+ case AccessibleText.WORD:
+ startIndex = text.indexOf(' ', index);
+ endIndex = text.indexOf(' ', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ startIndex = text.indexOf('.', index);
+ endIndex = text.indexOf('.', startIndex + 1);
+ if (endIndex == -1)
+ endIndex = startIndex + 1;
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the character, word or sentence before the specified index. The
+ * part parameter determines what is returned, the character,
+ * word or sentence before the index.
+ *
+ * @param part one of {@link AccessibleText#CHARACTER},
+ * {@link AccessibleText#WORD} or
+ * {@link AccessibleText#SENTENCE}, specifying what is returned
+ * @param index the index
+ *
+ * @return the character, word or sentence before index
+ */
+ public String getBeforeIndex(int part, int index)
+ {
+ String result = "";
+ int startIndex = -1;
+ int endIndex = -1;
+ switch(part)
+ {
+ case AccessibleText.CHARACTER:
+ result = String.valueOf(text.charAt(index - 1));
+ break;
+ case AccessibleText.WORD:
+ endIndex = text.lastIndexOf(' ', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf(' ', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ case AccessibleText.SENTENCE:
+ default:
+ endIndex = text.lastIndexOf('.', index);
+ if (endIndex == -1)
+ endIndex = 0;
+ startIndex = text.lastIndexOf('.', endIndex - 1);
+ result = text.substring(startIndex + 1, endIndex);
+ break;
+ }
+ return result;
+ }
+
+ /**
+ * Returns the caret position. This method returns -1 because JLabel don't
+ * have a caret.
+ *
+ * @return the caret position
+ */
+ public int getCaretPosition()
+ {
+ return -1;
+ }
+
+ /**
+ * Returns the number of characters that are displayed by the JLabel.
+ *
+ * @return the number of characters that are displayed by the JLabel
+ */
+ public int getCharCount()
+ {
+ // FIXME: Query HTML view for HTML labels.
+ return text.length();
+ }
+
+ /**
+ * Returns the bounding box of the character at the specified index.
+ *
+ * @param index the index of the character that we return the
+ * bounds for
+ *
+ * @return the bounding box of the character at the specified index
+ */
+ public Rectangle getCharacterBounds(int index)
+ {
+ Rectangle bounds = null;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle textR = getTextRectangle();
+ try
+ {
+ Shape s = view.modelToView(index, textR, Position.Bias.Forward);
+ bounds = s.getBounds();
+ }
+ catch (BadLocationException ex)
+ {
+ // Can't return something reasonable in this case.
+ }
+ }
+ return bounds;
+ }
+
+ /**
+ * Returns the rectangle inside the JLabel, in which the actual text is
+ * rendered. This method has been adopted from the Mauve testcase
+ * gnu.testlet.javax.swing.JLabel.AccessibleJLabel.getCharacterBounds.
+ *
+ * @return the rectangle inside the JLabel, in which the actual text is
+ * rendered
+ */
+ private Rectangle getTextRectangle()
+ {
+ JLabel l = JLabel.this;
+ Rectangle textR = new Rectangle();
+ Rectangle iconR = new Rectangle();
+ Insets i = l.getInsets();
+ int w = l.getWidth();
+ int h = l.getHeight();
+ Rectangle viewR = new Rectangle(i.left, i.top, w - i.left - i.right,
+ h - i.top - i.bottom);
+ FontMetrics fm = l.getFontMetrics(l.getFont());
+ SwingUtilities.layoutCompoundLabel(l, fm, l.getText(), l.getIcon(),
+ l.getVerticalAlignment(),
+ l.getHorizontalAlignment(),
+ l.getVerticalTextPosition(),
+ l.getHorizontalTextPosition(),
+ viewR, iconR, textR,
+ l.getIconTextGap());
+ return textR;
+ }
+
+ /**
+ * Returns the index of the character that is located at the specified
+ * point.
+ *
+ * @param point the location that we lookup the character for
+ *
+ * @return the index of the character that is located at the specified
+ * point
+ */
+ public int getIndexAtPoint(Point point)
+ {
+ int index = -1;
+ View view = (View) getClientProperty(BasicHTML.propertyKey);
+ if (view != null)
+ {
+ Rectangle r = getTextRectangle();
+ index = view.viewToModel(point.x, point.y, r, new Position.Bias[0]);
+ }
+ return index;
+ }
+ }
+
+ private static final long serialVersionUID = 5496508283662221534L;
+
+ static final String LABEL_PROPERTY = "labeledBy";
+
+ /**
+ * The Component the label will give focus to when its mnemonic is
+ * activated.
+ */
+ protected Component labelFor;
+
+ /** The label's text. */
+ transient String text;
+
+ /** Where the label will be positioned horizontally. */
+ private transient int horizontalAlignment = LEADING;
+
+ /** Where the label text will be placed horizontally relative to the icon. */
+ private transient int horizontalTextPosition = TRAILING;
+
+ /** Where the label will be positioned vertically. */
+ private transient int verticalAlignment = CENTER;
+
+ /** Where the label text will be place vertically relative to the icon. */
+ private transient int verticalTextPosition = CENTER;
+
+ /** The icon painted when the label is enabled. */
+ private transient Icon icon;
+
+ /** The icon painted when the label is disabled. */
+ private transient Icon disabledIcon;
+
+ /** The label's mnemnonic key. */
+ private transient int displayedMnemonic = KeyEvent.VK_UNDEFINED;
+
+ /** The index of the mnemonic character in the text. */
+ private transient int displayedMnemonicIndex = -1;
+
+ /** The gap between the icon and the text. */
+ private transient int iconTextGap = 4;
+
+ /**
+ * Creates a new vertically centered, horizontally on the leading edge
+ * JLabel object with text and no icon.
+ */
+ public JLabel()
+ {
+ this("", null, LEADING);
+ }
+
+ /**
+ * Creates a new vertically and horizontally centered
+ * JLabel object with no text and the given icon.
+ *
+ * @param image The icon to use with the label, null permitted.
+ */
+ public JLabel(Icon image)
+ {
+ this(null, image, CENTER);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with no text and the
+ * given icon and horizontal alignment. By default, the text is TRAILING
+ * the image.
+ *
+ * @param image The icon to use with the label, null premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either CENTER, LEFT, RIGHT,
+ * LEADING or TRAILING.
+ */
+ public JLabel(Icon image, int horizontalAlignment)
+ {
+ this(null, image, horizontalAlignment);
+ }
+
+ /**
+ * Creates a new horizontally leading and vertically centered JLabel
+ * object with no icon and the given text.
+ *
+ * @param text The text to use with the label, null permitted.
+ */
+ public JLabel(String text)
+ {
+ this(text, null, LEADING);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with no icon and the
+ * given text and horizontal alignment.
+ *
+ * @param text The text to use with the label, null permitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either CENTER, LEFT, RIGHT,
+ * LEADING or TRAILING.
+ */
+ public JLabel(String text, int horizontalAlignment)
+ {
+ this(text, null, horizontalAlignment);
+ }
+
+ /**
+ * Creates a new vertically centered JLabel object with the given text,
+ * icon, and horizontal alignment.
+ *
+ * @param text The text to use with the label, null permitted.
+ * @param icon The icon to use with the label, null premitted.
+ * @param horizontalAlignment The horizontal alignment of the label, must be
+ * either CENTER, LEFT, RIGHT,
+ * LEADING or TRAILING.
+ */
+ public JLabel(String text, Icon icon, int horizontalAlignment)
+ {
+ if (horizontalAlignment != SwingConstants.LEFT
+ && horizontalAlignment != SwingConstants.RIGHT
+ && horizontalAlignment != SwingConstants.CENTER
+ && horizontalAlignment != SwingConstants.LEADING
+ && horizontalAlignment != SwingConstants.TRAILING)
+ throw new IllegalArgumentException();
+
+ this.text = text;
+ this.icon = icon;
+ this.horizontalAlignment = horizontalAlignment;
+ setAlignmentX(0.0F);
+ setInheritsPopupMenu(true);
+ updateUI();
+ }
+
+ /**
+ * Returns the label's UI delegate.
+ *
+ * @return The label's UI delegate.
+ */
+ public LabelUI getUI()
+ {
+ return (LabelUI) ui;
+ }
+
+ /**
+ * Sets the label's UI delegate.
+ *
+ * @param ui The label's UI delegate (null not permitted).
+ */
+ public void setUI(LabelUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Resets the label's UI delegate to the default UI for the current look and
+ * feel.
+ */
+ public void updateUI()
+ {
+ setUI((LabelUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns a name to identify which look and feel class will be
+ * the UI delegate for this label.
+ *
+ * @return "LabelUI"
+ */
+ public String getUIClassID()
+ {
+ return "LabelUI";
+ }
+
+ /**
+ * Returns a string describing the attributes for the JLabel
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JLabel.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",defaultIcon=");
+ if (icon != null)
+ sb.append(icon);
+ sb.append(",disabledIcon=");
+ if (disabledIcon != null)
+ sb.append(disabledIcon);
+ sb.append(",horizontalAlignment=");
+ sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
+ horizontalAlignment));
+ sb.append(",horizontalTextPosition=");
+ sb.append(SwingUtilities.convertHorizontalAlignmentCodeToString(
+ horizontalTextPosition));
+ sb.append(",iconTextGap=").append(iconTextGap);
+ sb.append(",labelFor=");
+ if (labelFor != null)
+ sb.append(labelFor);
+ sb.append(",text=");
+ if (text != null)
+ sb.append(text);
+ sb.append(",verticalAlignment=");
+ sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
+ verticalAlignment));
+ sb.append(",verticalTextPosition=");
+ sb.append(SwingUtilities.convertVerticalAlignmentCodeToString(
+ verticalTextPosition));
+ return sb.toString();
+ }
+
+ /**
+ * Returns the text displayed by the label.
+ *
+ * @return The label text (possibly null).
+ *
+ * @see #setText(String)
+ */
+ public String getText()
+ {
+ return text;
+ }
+
+ /**
+ * Sets the text for the label and sends a {@link PropertyChangeEvent} (with
+ * the name 'text') to all registered listeners. This method will also
+ * update the displayedMnemonicIndex, if necessary.
+ *
+ * @param newText The text (null permitted).
+ *
+ * @see #getText()
+ * @see #getDisplayedMnemonicIndex()
+ */
+ public void setText(String newText)
+ {
+ if (text == null && newText == null)
+ return;
+ if (text != null && text.equals(newText))
+ return;
+
+ String oldText = text;
+ text = newText;
+ firePropertyChange("text", oldText, newText);
+
+ if (text != null)
+ setDisplayedMnemonicIndex(text.toUpperCase().indexOf(displayedMnemonic));
+ else
+ setDisplayedMnemonicIndex(-1);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns the active icon. The active icon is painted when the label is
+ * enabled.
+ *
+ * @return The active icon.
+ *
+ * @see #setIcon(Icon)
+ * @see #getDisabledIcon()
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * Sets the icon for the label (this is a bound property with the name
+ * 'icon'). This icon will be displayed when the label is enabled.
+ *
+ * @param newIcon The icon (null permitted).
+ *
+ * @see #getIcon()
+ * @see #setDisabledIcon(Icon)
+ */
+ public void setIcon(Icon newIcon)
+ {
+ if (icon != newIcon)
+ {
+ Icon oldIcon = icon;
+ icon = newIcon;
+ firePropertyChange("icon", oldIcon, newIcon);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the disabled icon. The disabled icon is painted when the label is
+ * disabled. If the disabled icon is null and the active icon
+ * is an {@link ImageIcon}, this method returns a grayed version of the icon.
+ * The grayed version of the icon becomes the disabledIcon.
+ *
+ * @return The disabled icon.
+ *
+ * @see #setDisabledIcon(Icon)
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && icon instanceof ImageIcon)
+ disabledIcon = new ImageIcon(
+ GrayFilter.createDisabledImage(((ImageIcon) icon).getImage()));
+
+ return disabledIcon;
+ }
+
+ /**
+ * Sets the icon displayed when the label is disabled (this is a bound
+ * property with the name 'disabledIcon').
+ *
+ * @param newIcon The disabled icon (null permitted).
+ *
+ * @see #getDisabledIcon()
+ */
+ public void setDisabledIcon(Icon newIcon)
+ {
+ if (disabledIcon != newIcon)
+ {
+ Icon oldIcon = disabledIcon;
+ disabledIcon = newIcon;
+ firePropertyChange("disabledIcon", oldIcon, newIcon);
+ }
+ }
+
+ /**
+ * Sets the keycode that will be the label's mnemonic (this is a bound
+ * property with the name 'displayedMnemonic'). If the label is used as a
+ * label for another component, the label will give focus to that component
+ * when the mnemonic is activated.
+ *
+ * @param mnemonic The keycode to use for the mnemonic.
+ *
+ * @see #getDisplayedMnemonic()
+ */
+ public void setDisplayedMnemonic(int mnemonic)
+ {
+ if (displayedMnemonic != mnemonic)
+ {
+ int old = displayedMnemonic;
+ displayedMnemonic = mnemonic;
+ firePropertyChange("displayedMnemonic", old, displayedMnemonic);
+ if (text != null)
+ setDisplayedMnemonicIndex(text.toUpperCase().indexOf(mnemonic));
+ }
+ }
+
+ /**
+ * Sets the character that will be the label's mnemonic. If the
+ * label is used as a label for another component, the label will give
+ * focus to that component when the mnemonic is activated via the keyboard.
+ *
+ * @param mnemonic The character to use for the mnemonic (this will be
+ * converted to the equivalent upper case character).
+ *
+ * @see #getDisplayedMnemonic()
+ */
+ public void setDisplayedMnemonic(char mnemonic)
+ {
+ setDisplayedMnemonic((int) Character.toUpperCase(mnemonic));
+ }
+
+ /**
+ * Returns the keycode that is used for the label's mnemonic.
+ *
+ * @return The keycode that is used for the label's mnemonic.
+ *
+ * @see #setDisplayedMnemonic(int)
+ */
+ public int getDisplayedMnemonic()
+ {
+ return displayedMnemonic;
+ }
+
+ /**
+ * Sets the index of the character in the text that will be underlined to
+ * indicate that it is the mnemonic character for the label. You only need
+ * to call this method if you wish to override the automatically calculated
+ * character index. For instance, for a label "Find Next" with the mnemonic
+ * character 'n', you might wish to underline the second occurrence of 'n'
+ * rather than the first (which is the default).
+ *
+ * Note that this method does not validate the character at the specified
+ * index to ensure that it matches the key code returned by
+ * {@link #getDisplayedMnemonic()}.
+ *
+ * @param newIndex The index of the character to underline.
+ *
+ * @throws IllegalArgumentException If index less than -1 or index is greater
+ * than or equal to the label length.
+ *
+ * @see #getDisplayedMnemonicIndex()
+ * @since 1.4
+ */
+ public void setDisplayedMnemonicIndex(int newIndex)
+ throws IllegalArgumentException
+ {
+ int maxValid = -1;
+ if (text != null)
+ maxValid = text.length() - 1;
+ if (newIndex < -1 || newIndex > maxValid)
+ throw new IllegalArgumentException();
+
+ if (newIndex != displayedMnemonicIndex)
+ {
+ int oldIndex = displayedMnemonicIndex;
+ displayedMnemonicIndex = newIndex;
+ firePropertyChange("displayedMnemonicIndex", oldIndex, newIndex);
+ }
+ }
+
+ /**
+ * Returns the index of the character in the label's text that will be
+ * underlined (to indicate that it is the mnemonic character), or -1 if no
+ * character is to be underlined.
+ *
+ * @return The index of the character that will be underlined.
+ *
+ * @see #setDisplayedMnemonicIndex(int)
+ * @since 1.4
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return displayedMnemonicIndex;
+ }
+
+ /**
+ * Checks the specified key to ensure that it is valid as a horizontal
+ * alignment, throwing an {@link IllegalArgumentException} if the key is
+ * invalid. Valid keys are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param key The key to check.
+ * @param message The message of the exception to be thrown if the key is
+ * invalid.
+ *
+ * @return The key if it is valid.
+ *
+ * @throws IllegalArgumentException If the key is invalid.
+ */
+ protected int checkHorizontalKey(int key, String message)
+ {
+ if (key != LEFT && key != CENTER && key != RIGHT && key != LEADING
+ && key != TRAILING)
+ throw new IllegalArgumentException(message);
+ else
+ return key;
+ }
+
+ /**
+ * Checks the specified key to ensure that it is valid as a vertical
+ * alignment, throwing an {@link IllegalArgumentException} if the key is
+ * invalid. Valid keys are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @param key The key to check.
+ * @param message The message of the exception to be thrown if the key is
+ * invalid.
+ *
+ * @return The key if it is valid.
+ *
+ * @throws IllegalArgumentException If the key is invalid.
+ */
+ protected int checkVerticalKey(int key, String message)
+ {
+ if (key != TOP && key != BOTTOM && key != CENTER)
+ throw new IllegalArgumentException(message);
+ else
+ return key;
+ }
+
+ /**
+ * Returns the gap between the icon and the text.
+ *
+ * @return The gap between the icon and the text.
+ *
+ * @see #setIconTextGap(int)
+ */
+ public int getIconTextGap()
+ {
+ return iconTextGap;
+ }
+
+ /**
+ * Sets the gap between the icon and the text, in the case that both are
+ * visible (this is a bound property with the name 'iconTextGap').
+ *
+ * @param newGap The gap (in pixels).
+ *
+ * @see #getIconTextGap()
+ */
+ public void setIconTextGap(int newGap)
+ {
+ if (iconTextGap != newGap)
+ {
+ firePropertyChange("iconTextGap", iconTextGap, newGap);
+ iconTextGap = newGap;
+ }
+ }
+
+ /**
+ * Returns the vertical alignment of the label (one of
+ * {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}). The default value
+ * depends on the installed look and feel, but is usually {@link #CENTER}.
+ *
+ * @return The vertical alignment.
+ *
+ * @see #setVerticalAlignment(int)
+ */
+ public int getVerticalAlignment()
+ {
+ return verticalAlignment;
+ }
+
+ /**
+ * Sets the vertical alignment for the label (this is a bound property with
+ * the name 'verticalAlignment'). The vertical alignment determines where
+ * the label (icon and text) will be placed vertically within the component
+ * bounds. Valid alignment codes are {@link #TOP}, {@link #CENTER} and
+ * {@link #BOTTOM}.
+ *
+ * @param alignment The vertical alignment of the label.
+ *
+ * @throws IllegalArgumentException if alignment is not one of
+ * the specified values.
+ *
+ * @see #getVerticalAlignment()
+ */
+ public void setVerticalAlignment(int alignment)
+ {
+ if (alignment == verticalAlignment)
+ return;
+
+ int oldAlignment = verticalAlignment;
+ verticalAlignment = checkVerticalKey(alignment, "verticalAlignment");
+ firePropertyChange("verticalAlignment", oldAlignment, verticalAlignment);
+ }
+
+ /**
+ * Returns the horizontal alignment of the label (one of {@link #LEFT},
+ * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}).
+ * The default value depends on the installed look and feel, but is usually
+ * {@link #LEFT}.
+ *
+ * @return The horizontal alignment.
+ *
+ * @see #setHorizontalAlignment(int)
+ */
+ public int getHorizontalAlignment()
+ {
+ return horizontalAlignment;
+ }
+
+ /**
+ * Sets the horizontal alignment for the label (this is a bound property with
+ * the name 'horizontalAlignment'). The horizontal alignment determines where
+ * the label (icon and text) will be placed horizontally within the
+ * component bounds. Valid alignment codes are {@link #LEFT},
+ * {@link #CENTER}, {@link #RIGHT}, {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param alignment The horizontal alignment of the label.
+ *
+ * @throws IllegalArgumentException if alignment is not one of
+ * the specified values.
+ *
+ * @see #getHorizontalAlignment()
+ */
+ public void setHorizontalAlignment(int alignment)
+ {
+ if (horizontalAlignment == alignment)
+ return;
+
+ int oldAlignment = horizontalAlignment;
+ horizontalAlignment = checkHorizontalKey(alignment, "horizontalAlignment");
+ firePropertyChange("horizontalAlignment", oldAlignment,
+ horizontalAlignment);
+ }
+
+ /**
+ * Returns the vertical position of the label's text relative to the icon.
+ * This will be one of {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @return The vertical position of the label's text relative to the icon.
+ *
+ * @see #setVerticalTextPosition(int)
+ */
+ public int getVerticalTextPosition()
+ {
+ return verticalTextPosition;
+ }
+
+ /**
+ * Sets the vertical position of the label's text relative to the icon (this
+ * is a bound property with the name 'verticalTextPosition'). Valid
+ * positions are {@link #TOP}, {@link #CENTER} and {@link #BOTTOM}.
+ *
+ * @param textPosition The vertical text position.
+ *
+ * @throws IllegalArgumentException if textPosition is not one
+ * of the specified values.
+ */
+ public void setVerticalTextPosition(int textPosition)
+ {
+ if (textPosition != verticalTextPosition)
+ {
+ int oldPos = verticalTextPosition;
+ verticalTextPosition = checkVerticalKey(textPosition,
+ "verticalTextPosition");
+ firePropertyChange("verticalTextPosition", oldPos,
+ verticalTextPosition);
+ }
+ }
+
+ /**
+ * Returns the horizontal position of the label's text relative to the icon.
+ * This will be one of {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @return The horizontal position of the label's text relative to the icon.
+ *
+ * @see #setHorizontalTextPosition(int)
+ */
+ public int getHorizontalTextPosition()
+ {
+ return horizontalTextPosition;
+ }
+
+ /**
+ * Sets the horizontal position of the label's text relative to the icon (this
+ * is a bound property with the name 'horizontalTextPosition'). Valid
+ * positions are {@link #LEFT}, {@link #CENTER}, {@link #RIGHT},
+ * {@link #LEADING} and {@link #TRAILING}.
+ *
+ * @param textPosition The horizontal text position.
+ *
+ * @throws IllegalArgumentException if textPosition is not one
+ * of the specified values.
+ */
+ public void setHorizontalTextPosition(int textPosition)
+ {
+ if (textPosition != horizontalTextPosition)
+ {
+ int oldPos = horizontalTextPosition;
+ horizontalTextPosition = checkHorizontalKey(textPosition,
+ "horizontalTextPosition");
+ firePropertyChange("horizontalTextPosition", oldPos,
+ horizontalTextPosition);
+ }
+ }
+
+ /**
+ * Returns false if the current icon image (current icon will depend on
+ * whether the label is enabled) is not equal to the passed in image.
+ *
+ * @param img The image to check.
+ * @param infoflags The bitwise inclusive OR of ABORT, ALLBITS, ERROR,
+ * FRAMEBITS, HEIGHT, PROPERTIES, SOMEBITS, and WIDTH
+ * @param x The x position
+ * @param y The y position
+ * @param w The width
+ * @param h The height
+ *
+ * @return Whether the current icon image is equal to the image given.
+ */
+ public boolean imageUpdate(Image img, int infoflags, int x, int y, int w,
+ int h)
+ {
+ Icon currIcon = isEnabled() ? icon : disabledIcon;
+
+ // XXX: Is this the correct way to check for image equality?
+ if (currIcon != null && currIcon instanceof ImageIcon)
+ return (((ImageIcon) currIcon).getImage() == img);
+
+ return false;
+ }
+
+ /**
+ * Returns the component that this JLabel is providing the label
+ * for. This component will typically receive the focus when the label's
+ * mnemonic key is activated via the keyboard.
+ *
+ * @return The component (possibly null).
+ */
+ public Component getLabelFor()
+ {
+ return labelFor;
+ }
+
+ /**
+ * Sets the component that this JLabel is providing the label
+ * for (this is a bound property with the name 'labelFor'). This component
+ * will typically receive the focus when the label's mnemonic key is
+ * activated via the keyboard.
+ *
+ * @param c the component (null permitted).
+ *
+ * @see #getLabelFor()
+ */
+ public void setLabelFor(Component c)
+ {
+ if (c != labelFor)
+ {
+ Component oldLabelFor = labelFor;
+ labelFor = c;
+ firePropertyChange("labelFor", oldLabelFor, labelFor);
+
+ // We put the label into the client properties for the labeled
+ // component so that it can be read by the AccessibleJComponent.
+ // The other option would be to reserve a default visible field
+ // in JComponent, but since this is relatively seldomly used, it
+ // would be unnecessary waste of memory to do so.
+ if (oldLabelFor instanceof JComponent)
+ {
+ ((JComponent) oldLabelFor).putClientProperty(LABEL_PROPERTY, null);
+ }
+
+ if (labelFor instanceof JComponent)
+ {
+ ((JComponent) labelFor).putClientProperty(LABEL_PROPERTY, this);
+ }
+
+ }
+ }
+
+ /**
+ * Sets the font for the label (this a bound property with the name 'font').
+ *
+ * @param f The font (null permitted).
+ */
+ public void setFont(Font f)
+ {
+ super.setFont(f);
+ repaint();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JLabel component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJLabel}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJLabel();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JLayeredPane.java b/libjava/classpath/javax/swing/JLayeredPane.java
new file mode 100644
index 000000000..9a4e31efe
--- /dev/null
+++ b/libjava/classpath/javax/swing/JLayeredPane.java
@@ -0,0 +1,755 @@
+/* JLayeredPane.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.util.ArrayList;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * A container that adds depth to the usual Container semantics.
+ * Each child component of a Layered Pane is placed within one
+ * of several layers. JLayeredPane defines a set of standard
+ * layers. The pre-defined sets are (in the order from button to top):
+ *
+ *
+ *
{@link #DEFAULT_LAYER}
+ *
The layer where most of the normal components are placed. This
+ * is the bottommost layer.
+ *
+ *
{@link #PALETTE_LAYER}
+ *
Palette windows are placed in this layer.
+ *
+ *
{@link #MODAL_LAYER}
+ *
The layer where internal modal dialog windows are placed.
+ *
+ *
{@link #POPUP_LAYER}
+ *
The layer for popup menus
+ *
+ *
{@link #DRAG_LAYER}
+ *
Components that are beeing dragged are temporarily placed in
+ * this layer.
+ *
+ *
+ *
A child is in exactly one of these layers at any time, though there may
+ * be other layers if someone creates them.
+ *
+ *
You can add a component to a specific layer using the
+ * {@link Container#add(Component, Object)} method. I.e.
+ * layeredPane.add(comp, JLayeredPane.MODAL_LAYER) will add the
+ * component comp to the modal layer of layeredPane.
+ *
+ *
+ *
To change the layer of a component that is already a child of
+ * a JLayeredPane, use the {@link #setLayer(Component, int)}
+ * method.
+ *
+ *
The purpose of this class is to translate this view of "layers" into a
+ * contiguous array of components: the one held in our ancestor,
+ * {@link java.awt.Container}.
+ *
+ *
There is a precise set of words we will use to refer to numbers within
+ * this class:
+ *
+ *
+ *
Component Index:
+ *
An offset into the component array held in our ancestor,
+ * {@link java.awt.Container}, from [0 .. component.length). The drawing
+ * rule with indices is that 0 is drawn last.
+ *
+ *
Layer Number:
+ *
A general int specifying a layer within this component. Negative
+ * numbers are drawn first, then layer 0, then positive numbered layers, in
+ * ascending order.
+ *
+ *
Position:
+ *
An offset into a layer's "logical drawing order". Layer position 0
+ * is drawn last. Layer position -1 is a synonym for the first layer
+ * position (the logical "bottom").
+ *
+ *
+ *
Note: the layer numbering order is the reverse of the
+ * component indexing and position order
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class JLayeredPane extends JComponent implements Accessible
+{
+
+ /**
+ * Provides accessibility support for JLayeredPane.
+ */
+ protected class AccessibleJLayeredPane extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of AccessibleJLayeredPane.
+ */
+ protected AccessibleJLayeredPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessble role of JLayeredPane,
+ * {@link AccessibleRole#LAYERED_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LAYERED_PANE;
+ }
+ }
+
+ private static final long serialVersionUID = 5534920399324590459L;
+
+ public static final String LAYER_PROPERTY = "layeredContainerLayer";
+
+ public static final Integer FRAME_CONTENT_LAYER = new Integer(-30000);
+
+ public static final Integer DEFAULT_LAYER = new Integer(0);
+ public static final Integer PALETTE_LAYER = new Integer(100);
+ public static final Integer MODAL_LAYER = new Integer(200);
+ public static final Integer POPUP_LAYER = new Integer(300);
+ public static final Integer DRAG_LAYER = new Integer(400);
+
+ private Hashtable componentToLayer; // Component -> Layer Number (Integer)
+
+ public JLayeredPane()
+ {
+ componentToLayer = new Hashtable();
+ setLayout(null);
+ }
+
+ /**
+ * Looks up the layer a child component is currently assigned to.
+ *
+ * If c is an instance of {@link JComponent}, then the layer
+ * is fetched from the client property with the key {@link #LAYER_PROPERTY}.
+ * Otherwise it is looked up in an internal hashtable that maps
+ * non-JComponent components to layers. If the components cannot be found
+ * in either way, the {@link #DEFAULT_LAYER} is returned.
+ *
+ * @param c the component to look up.
+ *
+ * @return the layer the component is currently assigned to; if the component
+ * is not in this layered pane, then 0 (DEFAULT_LAYER) is returned
+ */
+ public int getLayer(Component c)
+ {
+ Integer layerObj;
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ layerObj = (Integer) jc.getClientProperty(LAYER_PROPERTY);
+ }
+ else
+ layerObj = (Integer) componentToLayer.get(c);
+
+ if (layerObj == null)
+ layerObj = DEFAULT_LAYER;
+
+ return layerObj.intValue();
+ }
+
+ /**
+ * Looks up the layer in the client property with the key
+ * {@link #LAYER_PROPERTY} of comp. If no such property can be
+ * found, we return 0 ({@link #DEFAULT_LAYER}).
+ *
+ * @param comp the component for which the layer is looked up
+ *
+ * @return the layer of comp as stored in the corresponding
+ * client property, or 0 if there is no such property
+ */
+ public static int getLayer(JComponent comp)
+ {
+ Integer layerObj = (Integer) comp.getClientProperty(LAYER_PROPERTY);
+ if (layerObj == null)
+ layerObj = DEFAULT_LAYER;
+ return layerObj.intValue();
+ }
+
+ /**
+ * Returns the first JLayeredPane that contains the Component
+ * comp or null if comp is
+ * not contained in a JLayeredPane.
+ *
+ * @param comp the component for which we are searching the JLayeredPane
+ * ancestor
+ *
+ * @return the first JLayeredPane that contains the Component
+ * comp or null if comp is
+ * not contained in a JLayeredPane
+ */
+ public static JLayeredPane getLayeredPaneAbove(Component comp)
+ {
+ JLayeredPane lp = (JLayeredPane) SwingUtilities.getAncestorOfClass
+ (JLayeredPane.class, comp);
+ return lp;
+ }
+
+ /**
+ * Return the greatest layer number currently in use, in this container.
+ * This number may legally be positive or negative.
+ *
+ * @return the highest layer number
+ *
+ * @see #lowestLayer()
+ */
+ public int highestLayer()
+ {
+ Component[] components = getComponents();
+ int highest;
+ if (components.length == 0)
+ highest = 0;
+ else
+ {
+ highest = Integer.MIN_VALUE;
+ for (int i = 0; i < components.length; i++)
+ highest = Math.max(highest, getLayer(components[i]));
+ }
+ return highest;
+ }
+
+ /**
+ * Return the least layer number currently in use, in this container.
+ * This number may legally be positive or negative.
+ *
+ * @return the least layer number
+ *
+ * @see #highestLayer()
+ */
+ public int lowestLayer()
+ {
+ Component[] components = getComponents();
+ int lowest;
+ if (components.length == 0)
+ lowest = 0;
+ else
+ {
+ lowest = Integer.MAX_VALUE;
+ for (int i = 0; i < components.length; i++)
+ lowest = Math.max(lowest, getLayer(components[i]));
+ }
+ return lowest;
+ }
+
+ /**
+ * Moves a component to the "front" of its layer. The "front" is a
+ * synonym for position 0, which is also the last position drawn in each
+ * layer, so is usually the component which occludes the most other
+ * components in its layer.
+ *
+ * @param c the component to move to the front of its layer
+ *
+ * @see #moveToBack
+ */
+ public void moveToFront(Component c)
+ {
+ setPosition (c, 0);
+ }
+
+ /**
+ *
Moves a component to the "back" of its layer. The "back" is a
+ * synonym for position N-1 (also known as position -1), where N is the
+ * size of the layer.
+ *
+ *
The "back" of a layer is the first position drawn, so the component at
+ * the "back" is usually the component which is occluded by the most
+ * other components in its layer.
+ *
+ * @param c the component to move to the back of its layer.
+ *
+ * @see #moveToFront
+ */
+ public void moveToBack(Component c)
+ {
+ setPosition (c, -1);
+ }
+
+ /**
+ * Return the position of a component within its layer. Positions are assigned
+ * from the "front" (position 0) to the "back" (position N-1), and drawn from
+ * the back towards the front.
+ *
+ * @param c the component to get the position of
+ *
+ * @return the position of c within its layer or -1 if
+ * c is not a child of this layered pane
+ *
+ * @see #setPosition
+ */
+ public int getPosition(Component c)
+ {
+ int pos = -1;
+ int index = getIndexOf(c);
+ if (index >= 0)
+ {
+ pos = 0;
+ int layer = getLayer(c);
+ for (int i = index - 1; i >= 0; --i)
+ {
+ if (layer == getLayer(getComponent(i)))
+ pos++;
+ else
+ break;
+ }
+ }
+ return pos;
+ }
+
+ /**
+ * Change the position of a component within its layer. Positions are assigned
+ * from the "front" (position 0) to the "back" (position N-1), and drawn from
+ * the back towards the front.
+ *
+ * @param c the component to change the position of
+ * @param position the position to assign the component to
+ *
+ * @see #getPosition
+ */
+ public void setPosition(Component c, int position)
+ {
+ setLayer(c, getLayer(c), position);
+ }
+
+ /**
+ * Return an array of all components within a layer of this
+ * container. Components are ordered front-to-back, with the "front"
+ * element (which draws last) at position 0 of the returned array.
+ *
+ * @param layer the layer to return components from
+ *
+ * @return the components in the layer
+ */
+ public Component[] getComponentsInLayer(int layer)
+ {
+ Component[] inLayer = new Component[getComponentCountInLayer(layer)];
+ Component[] components = getComponents();
+ int j = 0;
+ for (int i = 0; i < components.length; ++i)
+ {
+ if (layer == getLayer(components[i]))
+ {
+ inLayer[j] = components[i];
+ j++;
+ }
+ }
+ return inLayer;
+ }
+
+ /**
+ * Return the number of components within a layer of this
+ * container.
+ *
+ * @param layer the layer count components in
+ *
+ * @return the number of components in the layer
+ */
+ public int getComponentCountInLayer(int layer)
+ {
+ Component[] components = getComponents();
+ int count = 0;
+ for (int i = components.length - 1; i >= 0; --i)
+ {
+ if (getLayer(components[i]) == layer)
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Return a hashtable mapping child components of this container to
+ * Integer objects representing the component's layer assignments.
+ */
+ protected Hashtable getComponentToLayer()
+ {
+ return componentToLayer;
+ }
+
+ /**
+ * Return the index of a component within the underlying (contiguous)
+ * array of children. This is a "raw" number which does not represent the
+ * child's position in a layer, but rather its position in the logical
+ * drawing order of all children of the container.
+ *
+ * @param c the component to look up.
+ *
+ * @return the external index of the component or -1 if
+ * c is not a child of this layered pane
+ */
+ public int getIndexOf(Component c)
+ {
+ return getComponentZOrder(c);
+ }
+
+ /**
+ * Return an Integer object which holds the same int value as the
+ * parameter. This is strictly an optimization to minimize the number of
+ * identical Integer objects which we allocate.
+ *
+ * @param layer the layer number as an int.
+ *
+ * @return the layer number as an Integer, possibly shared.
+ */
+ protected Integer getObjectForLayer(int layer)
+ {
+ switch (layer)
+ {
+ case -30000:
+ return FRAME_CONTENT_LAYER;
+
+ case 0:
+ return DEFAULT_LAYER;
+
+ case 100:
+ return PALETTE_LAYER;
+
+ case 200:
+ return MODAL_LAYER;
+
+ case 300:
+ return POPUP_LAYER;
+
+ case 400:
+ return DRAG_LAYER;
+
+ default:
+ break;
+ }
+
+ return new Integer(layer);
+ }
+
+ /**
+ * Computes an index at which to request the superclass {@link
+ * java.awt.Container} inserts a component, given an abstract layer and
+ * position number.
+ *
+ * @param layer the layer in which to insert a component.
+ * @param position the position in the layer at which to insert a component.
+ *
+ * @return the index at which to insert the component.
+ */
+ protected int insertIndexForLayer(int layer, int position)
+ {
+ return insertIndexForLayer(null, layer, position);
+ }
+
+ /**
+ * Similar to {@link #insertIndexForLayer(int, int)}, only that it takes a
+ * component parameter, which should be ignored in the search. This is
+ * necessary to support {@link #setLayer(Component, int, int)} which uses
+ * Container.setComponentZOrder(), which doesn't remove the component.
+ *
+ * @param comp the component to ignore
+ * @param layer the layer
+ * @param position the position
+ *
+ * @return the insertion index
+ */
+ private int insertIndexForLayer(Component comp, int layer, int position)
+ {
+ // Create the component list to search through.
+ ArrayList l = new ArrayList();
+ int count = getComponentCount();
+ for (int i = 0; i < count; i++)
+ {
+ Component c = getComponent(i);
+ if (c != comp)
+ l.add(c);
+ }
+
+ count = l.size();
+ int layerStart = -1;
+ int layerEnd = -1;
+ for (int i = 0; i < count; i++)
+ {
+ int layerOfComponent = getLayer((Component) l.get(i));
+ if (layerStart == -1 && layerOfComponent == layer)
+ layerStart = i;
+ if (layerOfComponent < layer)
+ {
+ // We are beyond the layer that we are looking for. Update the
+ // layerStart and layerEnd and exit the loop.
+ if (i == 0)
+ {
+ layerStart = 0;
+ layerEnd = 0;
+ }
+ else
+ layerEnd = i;
+ break;
+ }
+ }
+
+ // No layer found. The requested layer is lower than any existing layer,
+ // put the component at the end.
+ int insertIndex;
+ if (layerStart == -1 && layerEnd == -1)
+ {
+ insertIndex = count;
+ }
+ else
+ {
+ // Corner cases.
+ if (layerStart != -1 && layerEnd == -1)
+ layerEnd = count;
+ if (layerStart == -1 && layerEnd != -1)
+ layerStart = layerEnd;
+
+ // Adding to the bottom of a layer returns the end index
+ // in the layer.
+ if (position == -1)
+ insertIndex = layerEnd;
+ else
+ {
+ // Insert into a layer.
+ if (position > -1 && layerStart + position <= layerEnd)
+ insertIndex = layerStart + position;
+ else
+ insertIndex = layerEnd;
+ }
+ }
+ return insertIndex;
+ }
+
+ /**
+ * Removes a child from this container. The child is specified by
+ * index. After removal, the child no longer occupies a layer.
+ *
+ * @param index the index of the child component to remove.
+ */
+ public void remove(int index)
+ {
+ Component c = getComponent(index);
+ if (! (c instanceof JComponent))
+ componentToLayer.remove(c);
+ super.remove(index);
+ }
+
+ /**
+ * Removes all components from this container.
+ *
+ * @since 1.5
+ */
+ public void removeAll()
+ {
+ componentToLayer.clear();
+ super.removeAll();
+ }
+
+ /**
+ *
Set the layer property for a component, within this container. The
+ * component will be implicitly mapped to the bottom-most position in the
+ * layer, but only if added after calling this method.
+ *
+ *
Read that carefully: this method should be called before the
+ * component is added to the container.
+ *
+ * @param c the component to set the layer property for.
+ * @param layer the layer number to assign to the component.
+ */
+ public void setLayer(Component c, int layer)
+ {
+ setLayer(c, layer, -1);
+ }
+
+ /**
+ * Set the layer and position of a component, within this container.
+ *
+ * @param c the child component to set the layer property for.
+ * @param layer the layer number to assign to the component.
+ * @param position the position number to assign to the component.
+ */
+ public void setLayer(Component c, int layer, int position)
+ {
+ Integer layerObj = getObjectForLayer(layer);
+
+ // Nothing to do if neither the layer nor the position is
+ // changed.
+ if (layer != getLayer(c) || position != getPosition(c))
+ {
+ // Store the layer either in the JComponent or in the hashtable
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ jc.putClientProperty(LAYER_PROPERTY, layerObj);
+ }
+ else
+ componentToLayer.put (c, layerObj);
+
+ // Update the component in the Z order of the Container.
+ Container parent = c.getParent();
+ if (parent == this)
+ {
+ int index = insertIndexForLayer(c, layer, position);
+ setComponentZOrder(c, index);
+ }
+ }
+ repaint(c.getX(), c.getY(), c.getWidth(), c.getHeight());
+ }
+
+ /**
+ * Overrides the default implementation from {@link java.awt.Container}
+ * such that layerConstraint is interpreted as an {@link
+ * Integer}, specifying the layer to which the component will be added
+ * (at the bottom position).
+ *
+ * The argument index specifies the position within the layer
+ * at which the component should be added, where 0 is the top
+ * position greater values specify positions below that and -1
+ * specifies the bottom position.
+ *
+ * @param comp the component to add
+ * @param layerConstraint an integer specifying the layer to add the
+ * component to
+ * @param index the position within the layer
+ */
+ protected void addImpl(Component comp, Object layerConstraint, int index)
+ {
+ int layer;
+ if (layerConstraint != null && layerConstraint instanceof Integer)
+ {
+ layer = ((Integer) layerConstraint).intValue();
+ setLayer(comp, layer);
+ }
+ else
+ layer = getLayer(comp);
+
+ int newIdx = insertIndexForLayer(layer, index);
+ super.addImpl(comp, layerConstraint, newIdx);
+ comp.validate();
+ comp.repaint();
+ }
+
+ /**
+ * Sets the layer property for a JComponent.
+ *
+ * @param component the component for which to set the layer
+ * @param layer the layer property to set
+ */
+ public static void putLayer(JComponent component, int layer)
+ {
+ component.putClientProperty(LAYER_PROPERTY, new Integer(layer));
+ }
+
+ /**
+ * Returns the accessible context for this JLayeredPane.
+ *
+ * @return the accessible context for this JLayeredPane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJLayeredPane();
+ return accessibleContext;
+ }
+
+ /**
+ * This method is overridden order to provide a reasonable painting
+ * mechanism for JLayeredPane. This is necessary since
+ * JLayeredPane's do not have an own UI delegate.
+ *
+ * Basically this method clears the background for the
+ * JLayeredPane and then calls super.paint(g).
+ *
+ * @param g the graphics context to use
+ */
+ public void paint(Graphics g)
+ {
+ if (isOpaque())
+ {
+ Color oldColor = g.getColor();
+ Rectangle clip = g.getClipBounds();
+ g.setColor(getBackground());
+ g.fillRect(clip.x, clip.y, clip.width, clip.height);
+ g.setColor(oldColor);
+ }
+ super.paint(g);
+ }
+
+ /**
+ * Returns false if components in this layered pane can overlap,
+ * otherwise true.
+ *
+ * @return false if components in this layered pane can overlap,
+ * otherwise true
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ int numChildren = getComponentCount();
+ boolean result = true;
+ for (int i = 0; i < numChildren; ++i)
+ {
+ Component c1 = getComponent(i);
+ if (! c1.isVisible())
+ continue;
+ Rectangle r1 = c1.getBounds();
+ if (r1.isEmpty())
+ continue;
+
+ for (int j = i + 1; j < numChildren; ++j)
+ {
+ Component c2 = getComponent(j);
+ if (! c2.isVisible())
+ continue;
+ Rectangle r2 = c2.getBounds();
+ if (r2.isEmpty())
+ continue;
+ if (r1.intersects(r2))
+ {
+ result = false;
+ break;
+ }
+ if (result == false)
+ break;
+ }
+ }
+ return result;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java
new file mode 100644
index 000000000..b12fda365
--- /dev/null
+++ b/libjava/classpath/javax/swing/JList.java
@@ -0,0 +1,2499 @@
+/* JList.java --
+ Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.event.ListDataEvent;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.plaf.ListUI;
+import javax.swing.text.Position;
+
+/**
+ *
This class is a facade over three separate objects: {@link
+ * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and
+ * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list"
+ * concept, with independently replacable (possibly client-provided) models
+ * for its contents and its current selection. In addition, each element in
+ * the list is rendered via a strategy class {@link
+ * javax.swing.ListCellRenderer}.
+ *
+ *
Lists have many properties, some of which are stored in this class
+ * while others are delegated to the list's model or selection. The
+ * following properties are available:
+ *
+ *
+ *
Property
Stored in
Bound?
+ *
accessibleContext
list
no
+ *
anchorSelectionIndex
selection
no
+ *
cellRenderer
list
yes
+ *
dragEnabled
list
no
+ *
firstVisibleIndex
list
no
+ *
fixedCellHeight
list
yes
+ *
fixedCellWidth
list
yes
+ *
lastVisibleIndex
list
no
+ *
layoutOrientation
list
yes
+ *
leadSelectionIndex
selection
no
+ *
maxSelectionIndex
selection
no
+ *
minSelectionIndex
selection
no
+ *
model
list
yes
+ *
opaque
list
no
+ *
preferredScrollableViewportSize
list
no
+ *
prototypeCellValue
list
yes
+ *
scrollableTracksViewportHeight
list
no
+ *
scrollableTracksViewportWidth
list
no
+ *
selectedIndex
selection
no
+ *
selectedIndices
selection
no
+ *
selectedValue
model
no
+ *
selectedValues
model
no
+ *
selectionBackground
list
yes
+ *
selectionEmpty
selection
no
+ *
selectionForeground
list
yes
+ *
selectionMode
selection
no
+ *
selectionModel
list
yes
+ *
UI
list
yes
+ *
UIClassID
list
no
+ *
valueIsAdjusting
list
no
+ *
visibleRowCount
list
no
+ *
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+
+public class JList extends JComponent implements Accessible, Scrollable
+{
+
+ /**
+ * Provides accessibility support for JList.
+ */
+ protected class AccessibleJList extends AccessibleJComponent
+ implements AccessibleSelection, PropertyChangeListener,
+ ListSelectionListener, ListDataListener
+ {
+
+ /**
+ * Provides accessibility support for list elements in JLists.
+ */
+ protected class AccessibleJListChild extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ /**
+ * The parent list.
+ */
+ JList parent;
+
+ /**
+ * The index in the list for that child.
+ */
+ int listIndex;
+
+ /**
+ * The cursor for this list child.
+ */
+ // TODO: Testcases show that this class somehow stores state about the
+ // cursor. I cannot make up though how that could affect
+ // the actual list.
+ Cursor cursor = Cursor.getDefaultCursor();
+
+ /**
+ * Creates a new instance of AccessibleJListChild.
+ *
+ * @param list the list of which this is an accessible child
+ * @param index the list index for this child
+ */
+ public AccessibleJListChild(JList list, int index)
+ {
+ parent = list;
+ listIndex = index;
+ }
+
+ /**
+ * Returns the accessible context of this object. Returns
+ * this since AccessibleJListChilds are their
+ * own accessible contexts.
+ *
+ * @return the accessible context of this object, this
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the background color for this list child. This returns the
+ * background of the JList itself since the background
+ * cannot be set on list children individually
+ *
+ * @return the background color for this list child
+ */
+ public Color getBackground()
+ {
+ return parent.getBackground();
+ }
+
+ /**
+ * Calling this method has no effect, since the background color cannot be
+ * set on list children individually.
+ *
+ * @param color not used here.
+ */
+ public void setBackground(Color color)
+ {
+ // Calling this method has no effect, since the background color cannot
+ // be set on list children individually.
+ }
+
+ /**
+ * Returns the foreground color for this list child. This returns the
+ * background of the JList itself since the foreground
+ * cannot be set on list children individually.
+ *
+ * @return the background color for this list child
+ */
+ public Color getForeground()
+ {
+ return parent.getForeground();
+ }
+
+ /**
+ * Calling this method has no effect, since the foreground color cannot be
+ * set on list children individually.
+ *
+ * @param color not used here.
+ */
+ public void setForeground(Color color)
+ {
+ // Calling this method has no effect, since the foreground color cannot
+ // be set on list children individually.
+ }
+
+ /**
+ * Returns the cursor for this list child.
+ *
+ * @return the cursor for this list child
+ */
+ public Cursor getCursor()
+ {
+ // TODO: Testcases show that this method returns the cursor that has
+ // been set by setCursor. I cannot make up though how that could affect
+ // the actual list.
+ return cursor;
+ }
+
+ /**
+ * Sets the cursor for this list child.
+ */
+ public void setCursor(Cursor cursor)
+ {
+ this.cursor = cursor;
+ // TODO: Testcases show that this method returns the cursor that has
+ // been set by setCursor. I cannot make up though how that could affect
+ // the actual list.
+ }
+
+ /**
+ * Returns the font of the JList since it is not possible to
+ * set fonts for list children individually.
+ *
+ * @return the font of the JList
+ */
+ public Font getFont()
+ {
+ return parent.getFont();
+ }
+
+ /**
+ * Does nothing since it is not possible to set the font on list children
+ * individually.
+ *
+ * @param font not used here
+ */
+ public void setFont(Font font)
+ {
+ // Does nothing since it is not possible to set the font on list
+ // children individually.
+ }
+
+ /**
+ * Returns the font metrics for the specified font. This method forwards
+ * to the parent JList.
+ *
+ * @param font the font for which the font metrics is queried
+ *
+ * @return the font metrics for the specified font
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return parent.getFontMetrics(font);
+ }
+
+ /**
+ * Returns true if the parent JList is enabled,
+ * false otherwise. The list children cannot have an enabled
+ * flag set individually.
+ *
+ * @return true if the parent JList is enabled,
+ * false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return parent.isEnabled();
+ }
+
+ /**
+ * Does nothing since the enabled flag cannot be set for list children
+ * individually.
+ *
+ * @param b not used here
+ */
+ public void setEnabled(boolean b)
+ {
+ // Does nothing since the enabled flag cannot be set for list children
+ // individually.
+ }
+
+ /**
+ * Returns true if this list child is visible,
+ * false otherwise. The value of this property depends
+ * on {@link JList#getFirstVisibleIndex()} and
+ * {@link JList#getLastVisibleIndex()}.
+ *
+ * @return true if this list child is visible,
+ * false otherwise
+ */
+ public boolean isVisible()
+ {
+ return listIndex >= parent.getFirstVisibleIndex()
+ && listIndex <= parent.getLastVisibleIndex();
+ }
+
+ /**
+ * The value of the visible property cannot be modified, so this method
+ * does nothing.
+ *
+ * @param b not used here
+ */
+ public void setVisible(boolean b)
+ {
+ // The value of the visible property cannot be modified, so this method
+ // does nothing.
+ }
+
+ /**
+ * Returns true if this list child is currently showing on
+ * screen and false otherwise. The list child is showing if
+ * it is visible and if it's parent JList is currently showing.
+ *
+ * @return true if this list child is currently showing on
+ * screen and false otherwise
+ */
+ public boolean isShowing()
+ {
+ return isVisible() && parent.isShowing();
+ }
+
+ /**
+ * Returns true if this list child covers the screen location
+ * point (relative to the JList coordinate
+ * system, false otherwise.
+ *
+ * @return true if this list child covers the screen location
+ * point , false otherwise
+ */
+ public boolean contains(Point point)
+ {
+ return getBounds().contains(point);
+ }
+
+ /**
+ * Returns the absolute screen location of this list child.
+ *
+ * @return the absolute screen location of this list child
+ */
+ public Point getLocationOnScreen()
+ {
+ Point loc = getLocation();
+ SwingUtilities.convertPointToScreen(loc, parent);
+ return loc;
+ }
+
+ /**
+ * Returns the screen location of this list child relative to it's parent.
+ *
+ * @return the location of this list child relative to it's parent
+ *
+ * @see JList#indexToLocation(int)
+ */
+ public Point getLocation()
+ {
+ return parent.indexToLocation(listIndex);
+ }
+
+ /**
+ * Does nothing since the screen location cannot be set on list children
+ * explictitly.
+ *
+ * @param point not used here
+ */
+ public void setLocation(Point point)
+ {
+ // Does nothing since the screen location cannot be set on list children
+ // explictitly.
+ }
+
+ /**
+ * Returns the bounds of this list child.
+ *
+ * @return the bounds of this list child
+ *
+ * @see JList#getCellBounds(int, int)
+ */
+ public Rectangle getBounds()
+ {
+ return parent.getCellBounds(listIndex, listIndex);
+ }
+
+ /**
+ * Does nothing since the bounds cannot be set on list children
+ * individually.
+ *
+ * @param rectangle not used here
+ */
+ public void setBounds(Rectangle rectangle)
+ {
+ // Does nothing since the bounds cannot be set on list children
+ // individually.
+ }
+
+ /**
+ * Returns the size of this list child.
+ *
+ * @return the size of this list child
+ */
+ public Dimension getSize()
+ {
+ Rectangle b = getBounds();
+ return b.getSize();
+ }
+
+ /**
+ * Does nothing since the size cannot be set on list children
+ * individually.
+ *
+ * @param dimension not used here
+ */
+ public void setSize(Dimension dimension)
+ {
+ // Does nothing since the size cannot be set on list children
+ // individually.
+ }
+
+ /**
+ * Returns null because list children do not have children
+ * themselves
+ *
+ * @return null
+ */
+ public Accessible getAccessibleAt(Point point)
+ {
+ return null;
+ }
+
+ /**
+ * Returns true since list children are focus traversable.
+ *
+ * @return true
+ */
+ public boolean isFocusTraversable()
+ {
+ // TODO: Is this 100% ok?
+ return true;
+ }
+
+ /**
+ * Requests focus on the parent list. List children cannot request focus
+ * individually.
+ */
+ public void requestFocus()
+ {
+ // TODO: Is this 100% ok?
+ parent.requestFocus();
+ }
+
+ /**
+ * Adds a focus listener to the parent list. List children do not have
+ * their own focus management.
+ *
+ * @param listener the focus listener to add
+ */
+ public void addFocusListener(FocusListener listener)
+ {
+ // TODO: Is this 100% ok?
+ parent.addFocusListener(listener);
+ }
+
+ /**
+ * Removes a focus listener from the parent list. List children do not
+ * have their own focus management.
+ *
+ * @param listener the focus listener to remove
+ */
+ public void removeFocusListener(FocusListener listener)
+ {
+ // TODO: Is this 100%
+ parent.removeFocusListener(listener);
+ }
+
+ /**
+ * Returns the accessible role of this list item, which is
+ * {@link AccessibleRole#LABEL}.
+ *
+ * @return {@link AccessibleRole#LABEL}
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LABEL;
+ }
+
+ /**
+ * Returns the accessible state set of this list item.
+ *
+ * @return the accessible state set of this list item
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet states = new AccessibleStateSet();
+ if (isVisible())
+ states.add(AccessibleState.VISIBLE);
+ if (isShowing())
+ states.add(AccessibleState.SHOWING);
+ if (isFocusTraversable())
+ states.add(AccessibleState.FOCUSABLE);
+ // TODO: How should the active state be handled? The API docs
+ // suggest that this state is set on the activated list child,
+ // that is the one that is drawn with a box. However, I don't know how
+ // to implement this.
+
+ // TODO: We set the selectable state here because list children are
+ // selectable. Is there a way to disable single children?
+ if (parent.isEnabled())
+ states.add(AccessibleState.SELECTABLE);
+
+ if (parent.isSelectedIndex(listIndex))
+ states.add(AccessibleState.SELECTED);
+
+ // TODO: Handle more states here?
+ return states;
+ }
+
+ /**
+ * Returns the index of this list child within it's parent list.
+ *
+ * @return the index of this list child within it's parent list
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return listIndex;
+ }
+
+ /**
+ * Returns 0 since list children don't have children
+ * themselves.
+ *
+ * @return 0
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns null since list children don't have children
+ * themselves.
+ *
+ * @return null
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale of this component. This call is forwarded to the
+ * parent list since list children don't have a separate locale setting.
+ *
+ * @return the locale of this component
+ */
+ public Locale getLocale()
+ {
+ return parent.getLocale();
+ }
+
+ /**
+ * This method does
+ * nothing, list children are transient accessible objects which means
+ * that they don't fire property change events.
+ *
+ * @param l not used here
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * This method does
+ * nothing, list children are transient accessible objects which means
+ * that they don't fire property change events.
+ *
+ * @param l not used here
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ // Do nothing here.
+ }
+
+ // TODO: Implement the remaining methods of this class.
+ }
+
+ /**
+ * Create a new AccessibleJList.
+ */
+ public AccessibleJList()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of selected accessible children.
+ *
+ * @return the number of selected accessible children
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectedIndices().length;
+ }
+
+ /**
+ * Returns the n-th selected accessible child.
+ *
+ * @param n the index of the selected child to return
+ *
+ * @return the n-th selected accessible child
+ */
+ public Accessible getAccessibleSelection(int n)
+ {
+ return new AccessibleJListChild(JList.this, getSelectedIndices()[n]);
+ }
+
+ /**
+ * Returns true if the n-th child is selected,
+ * false otherwise.
+ *
+ * @param n the index of the child of which the selected state is queried
+ *
+ * @return true if the n-th child is selected,
+ * false otherwise
+ */
+ public boolean isAccessibleChildSelected(int n)
+ {
+ return isSelectedIndex(n);
+ }
+
+ /**
+ * Adds the accessible item with the specified index to the selected items.
+ * If multiple selections are supported, the item is added to the selection,
+ * otherwise the item replaces the current selection.
+ *
+ * @param i the index of the item to add to the selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ addSelectionInterval(i, i);
+ }
+
+ /**
+ * Removes the accessible item with the specified index to the selection.
+ *
+ * @param i the index of the item to be removed from the selection
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ removeSelectionInterval(i, i);
+ }
+
+ /**
+ * Remove all selection items from the selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Selects all items if multiple selections are supported.
+ * Otherwise do nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ addSelectionInterval(0, getModel().getSize());
+ }
+
+ /**
+ * Receices notification when the list selection is changed. This method
+ * fires two property change events, the first with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second
+ * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}.
+ *
+ * @param event the list selection event
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items have changed in the
+ * JList. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void contentsChanged(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items are inserted into the
+ * JList. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void intervalAdded(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+ /**
+ * Receives notification when items are removed from the
+ * JList. This method fires a property change event with
+ * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+ *
+ * @param event the list data event
+ */
+ public void intervalRemoved(ListDataEvent event)
+ {
+ firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+ Boolean.TRUE);
+ }
+
+
+ /**
+ * Receives notification about changes of the JList's
+ * properties. This is used to re-register this object as listener to
+ * the data model and selection model when the data model or selection model
+ * changes.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propertyName = e.getPropertyName();
+ if (propertyName.equals("model"))
+ {
+ ListModel oldModel = (ListModel) e.getOldValue();
+ oldModel.removeListDataListener(this);
+ ListModel newModel = (ListModel) e.getNewValue();
+ newModel.addListDataListener(this);
+ }
+ else if (propertyName.equals("selectionModel"))
+ {
+ ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
+ oldModel.removeListSelectionListener(this);
+ ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
+ oldModel.addListSelectionListener(this);
+ }
+ }
+
+ /**
+ * Return the state set of the JList.
+ *
+ * @return the state set of the JList
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // TODO: Figure out if there is possibly more state that must be
+ // handled here.
+ AccessibleStateSet s = super.getAccessibleStateSet();
+ if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION)
+ s.add(AccessibleState.MULTISELECTABLE);
+ return s;
+ }
+
+ /**
+ * Returns the accessible role for JList,
+ * {@link AccessibleRole#LIST}.
+ *
+ * @return the accessible role for JList
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.LIST;
+ }
+
+ /**
+ * Returns the accessible child at the visual location p
+ * (relative to the upper left corner of the JList). If there
+ * is no child at that location, this returns null.
+ *
+ * @param p the screen location for which to return the accessible child
+ *
+ * @return the accessible child at the specified location, or
+ * null if there is no child at that location
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ int childIndex = locationToIndex(p);
+ return getAccessibleChild(childIndex);
+ }
+
+ /**
+ * Returns the number of accessible children in the JList.
+ *
+ * @return the number of accessible children in the JList
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return getModel().getSize();
+ }
+
+ /**
+ * Returns the n-th accessible child of this JList. This will
+ * be an instance of {@link AccessibleJListChild}. If there is no child
+ * at that index, null is returned.
+ *
+ * @param n the index of the child to return
+ *
+ * @return the n-th accessible child of this JList
+ */
+ public Accessible getAccessibleChild(int n)
+ {
+ if (getModel().getSize() <= n)
+ return null;
+ return new AccessibleJListChild(JList.this, n);
+ }
+ }
+
+ private static final long serialVersionUID = 4406629526391098046L;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in a single vertical column. This is the default.
+ */
+ public static final int VERTICAL = 0;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in multiple columns "newspaper style", filling
+ * vertically first, then horizontally.
+ */
+ public static final int VERTICAL_WRAP = 1;
+
+ /**
+ * Constant value used in "layoutOrientation" property. This value means
+ * that cells are laid out in multiple columns "newspaper style",
+ * filling horizontally first, then vertically.
+ */
+ public static final int HORIZONTAL_WRAP = 2;
+
+ /**
+ * This property indicates whether "drag and drop" functions are enabled
+ * on the list.
+ */
+ boolean dragEnabled;
+
+ /** This property provides a strategy for rendering cells in the list. */
+ ListCellRenderer cellRenderer;
+
+ /**
+ * This property indicates an fixed width to assign to all cells in the
+ * list. If its value is -1, no width has been
+ * assigned. This value can be set explicitly, or implicitly by setting
+ * the {@link #prototypeCellValue} property.
+ */
+ int fixedCellWidth;
+
+ /**
+ * This property indicates an fixed height to assign to all cells in the
+ * list. If its value is -1, no height has been
+ * assigned. This value can be set explicitly, or implicitly by setting
+ * the {@link #prototypeCellValue} property.
+ */
+ int fixedCellHeight;
+
+ /**
+ * This property holds the current layout orientation of the list, which
+ * is one of the integer constants {@link #VERTICAL}, {@link
+ * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}.
+ */
+ int layoutOrientation;
+
+ /** This property holds the data elements displayed by the list. */
+ ListModel model;
+
+ /**
+ *
This property holds a reference to a "prototype" data value --
+ * typically a String -- which is used to calculate the {@link
+ * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
+ * {@link #cellRenderer} property to acquire a component to render the
+ * prototype.
+ *
+ *
It is important that you not set this value to a
+ * component. It has to be a data value such as the objects you
+ * would find in the list's model. Setting it to a component will have
+ * undefined (and undesirable) affects.
+ */
+ Object prototypeCellValue;
+
+ /**
+ * This property specifies a foreground color for the selected cells in
+ * the list. When {@link ListCellRenderer#getListCellRendererComponent}
+ * is called with a selected cell object, the component returned will
+ * have its "foreground" set to this color.
+ */
+ Color selectionBackground;
+
+ /**
+ * This property specifies a background color for the selected cells in
+ * the list. When {@link ListCellRenderer#getListCellRendererComponent}
+ * is called with a selected cell object, the component returned will
+ * have its "background" property set to this color.
+ */
+ Color selectionForeground;
+
+ /**
+ * This property holds a description of which data elements in the {@link
+ * #model} property should be considered "selected", when displaying and
+ * interacting with the list.
+ */
+ ListSelectionModel selectionModel;
+
+ /**
+ * This property indicates a preference for the number of rows
+ * displayed in the list, and will scale the
+ * {@link #getPreferredScrollableViewportSize} property accordingly. The actual
+ * number of displayed rows, when the list is placed in a real {@link
+ * JViewport} or other component, may be greater or less than this number.
+ */
+ int visibleRowCount;
+
+ /**
+ * Fire a {@link ListSelectionEvent} to all the registered
+ * ListSelectionListeners.
+ *
+ * @param firstIndex the lowest index covering the selection change.
+ * @param lastIndex the highest index covering the selection change.
+ * @param isAdjusting a flag indicating if this event is one in a series
+ * of events updating the selection.
+ */
+ protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
+ boolean isAdjusting)
+ {
+ ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
+ lastIndex, isAdjusting);
+ ListSelectionListener listeners[] = getListSelectionListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ {
+ listeners[i].valueChanged(evt);
+ }
+ }
+
+ /**
+ * This private listener propagates {@link ListSelectionEvent} events
+ * from the list's "selectionModel" property to the list's {@link
+ * ListSelectionListener} listeners. It also listens to {@link
+ * ListDataEvent} events from the list's {@link #model} property. If this
+ * class receives either type of event, it triggers repainting of the
+ * list.
+ */
+ private class ListListener
+ implements ListSelectionListener, ListDataListener
+ {
+ // ListDataListener events
+ public void contentsChanged(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ public void intervalAdded(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ public void intervalRemoved(ListDataEvent event)
+ {
+ JList.this.revalidate();
+ JList.this.repaint();
+ }
+ // ListSelectionListener events
+ public void valueChanged(ListSelectionEvent event)
+ {
+ JList.this.fireSelectionValueChanged(event.getFirstIndex(),
+ event.getLastIndex(),
+ event.getValueIsAdjusting());
+ JList.this.repaint();
+ }
+ }
+
+ /**
+ * Shared ListListener instance, subscribed to both the current {@link
+ * #model} and {@link #selectionModel} properties of the list.
+ */
+ ListListener listListener;
+
+
+ /**
+ * Creates a new JList object.
+ */
+ public JList()
+ {
+ init(new DefaultListModel());
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param items the initial list items.
+ */
+ public JList(Object[] items)
+ {
+ init(createListModel(items));
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param items the initial list items.
+ */
+ public JList(Vector> items)
+ {
+ init(createListModel(items));
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param model a model containing the list items (null not
+ * permitted).
+ *
+ * @throws IllegalArgumentException if model is
+ * null.
+ */
+ public JList(ListModel model)
+ {
+ init(model);
+ }
+
+ /**
+ * Initializes the list.
+ *
+ * @param m the list model (null not permitted).
+ */
+ private void init(ListModel m)
+ {
+ if (m == null)
+ throw new IllegalArgumentException("Null model not permitted.");
+ dragEnabled = false;
+ fixedCellHeight = -1;
+ fixedCellWidth = -1;
+ layoutOrientation = VERTICAL;
+ opaque = true;
+ visibleRowCount = 8;
+
+ cellRenderer = new DefaultListCellRenderer();
+ listListener = new ListListener();
+
+ model = m;
+ if (model != null)
+ model.addListDataListener(listListener);
+
+ selectionModel = createSelectionModel();
+ if (selectionModel != null)
+ {
+ selectionModel.addListSelectionListener(listListener);
+ selectionModel.setSelectionMode
+ (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
+ }
+ setLayout(null);
+
+ updateUI();
+ }
+
+ /**
+ * Creates the default ListSelectionModel.
+ *
+ * @return the ListSelectionModel
+ */
+ protected ListSelectionModel createSelectionModel()
+ {
+ return new DefaultListSelectionModel();
+ }
+
+ /**
+ * Gets the value of the {@link #fixedCellHeight} property. This property
+ * may be -1 to indicate that no cell height has been
+ * set. This property is also set implicitly when the
+ * {@link #prototypeCellValue} property is set.
+ *
+ * @return The current value of the property
+ *
+ * @see #fixedCellHeight
+ * @see #setFixedCellHeight
+ * @see #setPrototypeCellValue
+ */
+ public int getFixedCellHeight()
+ {
+ return fixedCellHeight;
+ }
+
+ /**
+ * Sets the value of the {@link #fixedCellHeight} property. This property
+ * may be -1 to indicate that no cell height has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set, but setting it explicitly
+ * overrides the height computed from {@link #prototypeCellValue}.
+ *
+ * @param h the height.
+ *
+ * @see #getFixedCellHeight
+ * @see #getPrototypeCellValue
+ */
+ public void setFixedCellHeight(int h)
+ {
+ if (fixedCellHeight == h)
+ return;
+
+ int old = fixedCellHeight;
+ fixedCellHeight = h;
+ firePropertyChange("fixedCellHeight", old, h);
+ }
+
+
+ /**
+ * Gets the value of the {@link #fixedCellWidth} property. This property
+ * may be -1 to indicate that no cell width has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set.
+ *
+ * @return The current value of the property
+ *
+ * @see #setFixedCellWidth
+ * @see #setPrototypeCellValue
+ */
+ public int getFixedCellWidth()
+ {
+ return fixedCellWidth;
+ }
+
+ /**
+ * Sets the value of the {@link #fixedCellWidth} property. This property
+ * may be -1 to indicate that no cell width has been
+ * set. This property is also set implicitly when the {@link
+ * #prototypeCellValue} property is set, but setting it explicitly
+ * overrides the width computed from {@link #prototypeCellValue}.
+ *
+ * @param w the width.
+ *
+ * @see #getFixedCellHeight
+ * @see #getPrototypeCellValue
+ */
+ public void setFixedCellWidth(int w)
+ {
+ if (fixedCellWidth == w)
+ return;
+
+ int old = fixedCellWidth;
+ fixedCellWidth = w;
+ firePropertyChange("fixedCellWidth", old, w);
+ }
+
+ /**
+ * Gets the value of the {@link #visibleRowCount} property. The default
+ * value is 8.
+ *
+ * @return the current value of the property.
+ *
+ * @see #setVisibleRowCount(int)
+ */
+ public int getVisibleRowCount()
+ {
+ return visibleRowCount;
+ }
+
+ /**
+ * Sets the value of the {@link #visibleRowCount} property.
+ *
+ * @param vc The new property value
+ *
+ * @see #getVisibleRowCount()
+ */
+ public void setVisibleRowCount(int vc)
+ {
+ if (visibleRowCount != vc)
+ {
+ int oldValue = visibleRowCount;
+ visibleRowCount = Math.max(vc, 0);
+ firePropertyChange("visibleRowCount", oldValue, vc);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Adds a {@link ListSelectionListener} to the listener list for this
+ * list. The listener will be called back with a {@link
+ * ListSelectionEvent} any time the list's {@link #selectionModel}
+ * property changes. The source of such events will be the JList,
+ * not the selection model.
+ *
+ * @param listener The new listener to add
+ */
+ public void addListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.add (ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a {@link ListSelectionListener} from the listener list for
+ * this list. The listener will no longer be called when the list's
+ * {@link #selectionModel} changes.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeListSelectionListener(ListSelectionListener listener)
+ {
+ listenerList.remove(ListSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns an array of all ListSelectionListeners subscribed to this
+ * list.
+ *
+ * @return The current subscribed listeners
+ *
+ * @since 1.4
+ */
+ public ListSelectionListener[] getListSelectionListeners()
+ {
+ return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
+ }
+
+ /**
+ * Returns the selection mode for the list (one of:
+ * {@link ListSelectionModel#SINGLE_SELECTION},
+ * {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION} and
+ * {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}).
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ public int getSelectionMode()
+ {
+ return selectionModel.getSelectionMode();
+ }
+
+ /**
+ * Sets the list's "selectionMode" property, which simply mirrors the
+ * same property on the list's {@link #selectionModel} property. This
+ * property should be one of the integer constants
+ * SINGLE_SELECTION, SINGLE_INTERVAL_SELECTION,
+ * or MULTIPLE_INTERVAL_SELECTION from the {@link
+ * ListSelectionModel} interface.
+ *
+ * @param a The new selection mode
+ */
+ public void setSelectionMode(int a)
+ {
+ selectionModel.setSelectionMode(a);
+ }
+
+ /**
+ * Adds the interval [a,a] to the set of selections managed
+ * by this list's {@link #selectionModel} property. Depending on the
+ * selection mode, this may cause existing selections to become invalid,
+ * or may simply expand the set of selections.
+ *
+ * @param a A number in the half-open range [0, x) where
+ * x = getModel.getSize(), indicating the index of an
+ * element in the list to select. When < 0 the selection is cleared.
+ *
+ * @see #setSelectionMode
+ * @see #selectionModel
+ */
+ public void setSelectedIndex(int a)
+ {
+ if (a < 0)
+ selectionModel.clearSelection();
+ else
+ selectionModel.setSelectionInterval(a, a);
+ }
+
+ /**
+ * For each element a[i] of the provided array
+ * a, calls {@link #setSelectedIndex} on a[i].
+ *
+ * @param a an array of selected indices (null not permitted).
+ *
+ * @throws NullPointerException if a is null.
+ * @see #setSelectionMode
+ * @see #selectionModel
+ */
+ public void setSelectedIndices(int [] a)
+ {
+ for (int i = 0; i < a.length; ++i)
+ setSelectedIndex(a[i]);
+ }
+
+ /**
+ * Returns the minimum index of an element in the list which is currently
+ * selected.
+ *
+ * @return A number in the half-open range [0, x) where
+ * x = getModel.getSize(), indicating the minimum index of
+ * an element in the list for which the element is selected, or
+ * -1 if no elements are selected
+ */
+ public int getSelectedIndex()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Returns true if the model's selection is empty, otherwise
+ * false.
+ *
+ * @return The return value of {@link ListSelectionModel#isSelectionEmpty}
+ */
+ public boolean isSelectionEmpty()
+ {
+ return selectionModel.isSelectionEmpty();
+ }
+
+ /**
+ * Returns the list index of the upper left or upper right corner of the
+ * visible rectangle of this list, depending on the {@link
+ * Component#getComponentOrientation} property.
+ *
+ * @return The index of the first visible list cell, or -1
+ * if none is visible.
+ */
+ public int getFirstVisibleIndex()
+ {
+ ComponentOrientation or = getComponentOrientation();
+ Rectangle r = getVisibleRect();
+ if (or == ComponentOrientation.RIGHT_TO_LEFT)
+ r.translate((int) r.getWidth() - 1, 0);
+ return getUI().locationToIndex(this, r.getLocation());
+ }
+
+
+ /**
+ * Returns index of the cell to which specified location is closest to. If
+ * the location is outside the bounds of the list, then the greatest index
+ * in the list model is returned. If the list model is empty, then
+ * -1 is returned.
+ *
+ * @param location for which to look for in the list
+ *
+ * @return index of the cell to which specified location is closest to.
+ */
+ public int locationToIndex(Point location)
+ {
+ return getUI().locationToIndex(this, location);
+ }
+
+ /**
+ * Returns location of the cell located at the specified index in the list.
+ * @param index of the cell for which location will be determined
+ *
+ * @return location of the cell located at the specified index in the list.
+ */
+ public Point indexToLocation(int index)
+ {
+ return getUI().indexToLocation(this, index);
+ }
+
+ /**
+ * Returns the list index of the lower right or lower left corner of the
+ * visible rectangle of this list, depending on the {@link
+ * Component#getComponentOrientation} property.
+ *
+ * @return The index of the last visible list cell, or -1
+ * if none is visible.
+ */
+ public int getLastVisibleIndex()
+ {
+ ComponentOrientation or = getComponentOrientation();
+ Rectangle r = getVisibleRect();
+ r.translate(0, (int) r.getHeight() - 1);
+ if (or == ComponentOrientation.LEFT_TO_RIGHT)
+ r.translate((int) r.getWidth() - 1, 0);
+ if (getUI().locationToIndex(this, r.getLocation()) == -1
+ && indexToLocation(getModel().getSize() - 1).y < r.y)
+ return getModel().getSize() - 1;
+ return getUI().locationToIndex(this, r.getLocation());
+ }
+
+ /**
+ * Returns the indices of values in the {@link #model} property which are
+ * selected.
+ *
+ * @return An array of model indices, each of which is selected according
+ * to the {@link #getSelectedValues} property
+ */
+ public int[] getSelectedIndices()
+ {
+ int lo, hi, n, i, j;
+ if (selectionModel.isSelectionEmpty())
+ return new int[0];
+ lo = selectionModel.getMinSelectionIndex();
+ hi = selectionModel.getMaxSelectionIndex();
+ n = 0;
+ for (i = lo; i <= hi; ++i)
+ if (selectionModel.isSelectedIndex(i))
+ n++;
+ int [] v = new int[n];
+ j = 0;
+ for (i = lo; i <= hi; ++i)
+ if (selectionModel.isSelectedIndex(i))
+ v[j++] = i;
+ return v;
+ }
+
+ /**
+ * Indicates whether the list element at a given index value is
+ * currently selected.
+ *
+ * @param a The index to check
+ * @return true if a is the index of a selected
+ * list element
+ */
+ public boolean isSelectedIndex(int a)
+ {
+ return selectionModel.isSelectedIndex(a);
+ }
+
+ /**
+ * Returns the first value in the list's {@link #model} property which is
+ * selected, according to the list's {@link #selectionModel} property.
+ * This is equivalent to calling
+ * getModel()getElementAt(getSelectedIndex()), with a check
+ * for the special index value of -1 which returns null
+ * null.
+ *
+ * @return The first selected element, or null if no element
+ * is selected.
+ *
+ * @see #getSelectedValues
+ */
+ public Object getSelectedValue()
+ {
+ int index = getSelectedIndex();
+ if (index == -1)
+ return null;
+ return getModel().getElementAt(index);
+ }
+
+ /**
+ * Returns all the values in the list's {@link #model} property which are
+ * selected, according to the list's {@link #selectionModel} property.
+ *
+ * @return An array containing all the selected values
+ * @see #setSelectedValue
+ */
+ public Object[] getSelectedValues()
+ {
+ int[] idx = getSelectedIndices();
+ Object[] v = new Object[idx.length];
+ for (int i = 0; i < idx.length; ++i)
+ v[i] = getModel().getElementAt(idx[i]);
+ return v;
+ }
+
+ /**
+ * Gets the value of the {@link #selectionBackground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionBackground} property.
+ *
+ * @param c The new value of the property
+ */
+ public void setSelectionBackground(Color c)
+ {
+ if (selectionBackground == c)
+ return;
+
+ Color old = selectionBackground;
+ selectionBackground = c;
+ firePropertyChange("selectionBackground", old, c);
+ repaint();
+ }
+
+ /**
+ * Gets the value of the {@link #selectionForeground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionForeground} property.
+ *
+ * @param c The new value of the property
+ */
+ public void setSelectionForeground(Color c)
+ {
+ if (selectionForeground == c)
+ return;
+
+ Color old = selectionForeground;
+ selectionForeground = c;
+ firePropertyChange("selectionForeground", old, c);
+ }
+
+ /**
+ * Sets the selection to cover only the specified value, if it
+ * exists in the model.
+ *
+ * @param obj The object to select
+ * @param scroll Whether to scroll the list to make the newly selected
+ * value visible
+ *
+ * @see #ensureIndexIsVisible
+ */
+
+ public void setSelectedValue(Object obj, boolean scroll)
+ {
+ for (int i = 0; i < model.getSize(); ++i)
+ {
+ if (model.getElementAt(i).equals(obj))
+ {
+ setSelectedIndex(i);
+ if (scroll)
+ ensureIndexIsVisible(i);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Scrolls this list to make the specified cell visible. This
+ * only works if the list is contained within a viewport.
+ *
+ * @param i The list index to make visible
+ *
+ * @see JComponent#scrollRectToVisible
+ */
+ public void ensureIndexIsVisible(int i)
+ {
+ Rectangle r = getUI().getCellBounds(this, i, i);
+ if (r != null)
+ scrollRectToVisible(r);
+ }
+
+ /**
+ * Sets the {@link #model} property of the list to a new anonymous
+ * {@link AbstractListModel} subclass which accesses the provided Object
+ * array directly.
+ *
+ * @param listData The object array to build a new list model on
+ * @see #setModel
+ */
+ public void setListData(Object[] listData)
+ {
+ setModel(createListModel(listData));
+ }
+
+ /**
+ * Returns a {@link ListModel} backed by the specified array.
+ *
+ * @param items the list items (don't use null).
+ *
+ * @return A list model containing the specified items.
+ */
+ private ListModel createListModel(final Object[] items)
+ {
+ return new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return items.length;
+ }
+ public Object getElementAt(int i)
+ {
+ return items[i];
+ }
+ };
+ }
+
+ /**
+ * Returns a {@link ListModel} backed by the specified vector.
+ *
+ * @param items the list items (don't use null).
+ *
+ * @return A list model containing the specified items.
+ */
+ private ListModel createListModel(final Vector items)
+ {
+ return new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return items.size();
+ }
+ public Object getElementAt(int i)
+ {
+ return items.get(i);
+ }
+ };
+ }
+
+ /**
+ * Sets the {@link #model} property of the list to a new anonymous {@link
+ * AbstractListModel} subclass which accesses the provided vector
+ * directly.
+ *
+ * @param listData The object array to build a new list model on
+ * @see #setModel
+ */
+ public void setListData(final Vector> listData)
+ {
+ setModel(new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return listData.size();
+ }
+
+ public Object getElementAt(int i)
+ {
+ return listData.elementAt(i);
+ }
+ });
+ }
+
+ /**
+ * Gets the value of the {@link #cellRenderer} property.
+ *
+ * @return The current value of the property
+ */
+ public ListCellRenderer getCellRenderer()
+ {
+ return cellRenderer;
+ }
+
+ /**
+ * Sets the value of the {@link #getCellRenderer} property.
+ *
+ * @param renderer The new property value
+ */
+ public void setCellRenderer(ListCellRenderer renderer)
+ {
+ if (cellRenderer == renderer)
+ return;
+
+ ListCellRenderer old = cellRenderer;
+ cellRenderer = renderer;
+ firePropertyChange("cellRenderer", old, renderer);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Gets the value of the {@link #model} property.
+ *
+ * @return The current value of the property
+ */
+ public ListModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets the value of the {@link #model} property. The list's {@link
+ * #listListener} is unsubscribed from the existing model, if it exists,
+ * and re-subscribed to the new model.
+ *
+ * @param model the new model (null not permitted).
+ *
+ * @throws IllegalArgumentException if model is
+ * null.
+ */
+ public void setModel(ListModel model)
+ {
+ if (model == null)
+ throw new IllegalArgumentException("Null 'model' argument.");
+ if (this.model == model)
+ return;
+
+ if (this.model != null)
+ this.model.removeListDataListener(listListener);
+
+ ListModel old = this.model;
+ this.model = model;
+
+ if (this.model != null)
+ this.model.addListDataListener(listListener);
+
+ firePropertyChange("model", old, model);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Returns the selection model for the {@link JList} component. Note that
+ * this class contains a range of convenience methods for configuring the
+ * selection model:
+ *
+ *
{@link #clearSelection()};
+ *
{@link #setSelectionMode(int)};
+ *
{@link #addSelectionInterval(int, int)};
+ *
{@link #setSelectedIndex(int)};
+ *
{@link #setSelectedIndices(int[])};
+ *
{@link #setSelectionInterval(int, int)}.
+ *
+ *
+ * @return The selection model.
+ */
+ public ListSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Sets the value of the {@link #selectionModel} property. The list's
+ * {@link #listListener} is unsubscribed from the existing selection
+ * model, if it exists, and re-subscribed to the new selection model.
+ *
+ * @param model The new property value
+ */
+ public void setSelectionModel(ListSelectionModel model)
+ {
+ if (selectionModel == model)
+ return;
+
+ if (selectionModel != null)
+ selectionModel.removeListSelectionListener(listListener);
+
+ ListSelectionModel old = selectionModel;
+ selectionModel = model;
+
+ if (selectionModel != null)
+ selectionModel.addListSelectionListener(listListener);
+
+ firePropertyChange("selectionModel", old, model);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Gets the value of the UI property.
+ *
+ * @return The current property value
+ */
+ public ListUI getUI()
+ {
+ return (ListUI) ui;
+ }
+
+ /**
+ * Sets the value of the UI property.
+ *
+ * @param ui The new property value
+ */
+ public void setUI(ListUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Calls {@link #setUI} with the {@link ListUI} subclass
+ * returned from calling {@link UIManager#getUI}.
+ */
+ public void updateUI()
+ {
+ setUI((ListUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Return the class identifier for the list's UI property. This should
+ * be the constant string "ListUI", and map to an
+ * appropriate UI class in the {@link UIManager}.
+ *
+ * @return The class identifier
+ */
+ public String getUIClassID()
+ {
+ return "ListUI";
+ }
+
+
+ /**
+ * Returns the current value of the {@link #prototypeCellValue}
+ * property. This property holds a reference to a "prototype" data value
+ * -- typically a String -- which is used to calculate the {@link
+ * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
+ * {@link #cellRenderer} property to acquire a component to render the
+ * prototype.
+ *
+ * @return The current prototype cell value
+ * @see #setPrototypeCellValue
+ */
+ public Object getPrototypeCellValue()
+ {
+ return prototypeCellValue;
+ }
+
+ /**
+ *
Set the {@link #prototypeCellValue} property. This property holds a
+ * reference to a "prototype" data value -- typically a String -- which
+ * is used to calculate the {@link #fixedCellWidth} and {@link
+ * #fixedCellHeight} properties, using the {@link #cellRenderer} property
+ * to acquire a component to render the prototype.
+ *
+ *
It is important that you not set this value to a
+ * component. It has to be a data value such as the objects you
+ * would find in the list's model. Setting it to a component will have
+ * undefined (and undesirable) affects.
+ *
+ * @param obj The new prototype cell value
+ * @see #getPrototypeCellValue
+ */
+ public void setPrototypeCellValue(Object obj)
+ {
+ if (prototypeCellValue == obj)
+ return;
+
+ Object old = prototypeCellValue;
+ Component comp = getCellRenderer()
+ .getListCellRendererComponent(this, obj, 0, false, false);
+ Dimension d = comp.getPreferredSize();
+ fixedCellWidth = d.width;
+ fixedCellHeight = d.height;
+ prototypeCellValue = obj;
+ firePropertyChange("prototypeCellValue", old, obj);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJList();
+ }
+
+ /**
+ * Returns a size indicating how much space this list would like to
+ * consume, when contained in a scrollable viewport. This is part of the
+ * {@link Scrollable} interface, which interacts with {@link
+ * ScrollPaneLayout} and {@link JViewport} to define scrollable objects.
+ *
+ * @return The preferred size
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ //If the layout orientation is not VERTICAL, then this will
+ //return the value from getPreferredSize. The current ListUI is
+ //expected to override getPreferredSize to return an appropriate value.
+ if (getLayoutOrientation() != VERTICAL)
+ return getPreferredSize();
+
+ int size = getModel().getSize();
+
+ // Trivial case: if fixedCellWidth and fixedCellHeight were set
+ // just use them
+ if (fixedCellHeight != -1 && fixedCellWidth != -1)
+ return new Dimension(fixedCellWidth, size * fixedCellHeight);
+
+ // If the model is empty we use 16 * the number of visible rows
+ // for the height and either fixedCellWidth (if set) or 256
+ // for the width
+ if (size == 0)
+ {
+ if (fixedCellWidth == -1)
+ return new Dimension(256, 16 * getVisibleRowCount());
+ else
+ return new Dimension(fixedCellWidth, 16 * getVisibleRowCount());
+ }
+
+ // Calculate the width: if fixedCellWidth was set use that, otherwise
+ // use the preferredWidth
+ int prefWidth;
+ if (fixedCellWidth != -1)
+ prefWidth = fixedCellWidth;
+ else
+ prefWidth = getPreferredSize().width;
+
+ // Calculate the height: if fixedCellHeight was set use that, otherwise
+ // use the height of the first row multiplied by the number of visible
+ // rows
+ int prefHeight;
+ if (fixedCellHeight != -1)
+ prefHeight = fixedCellHeight;
+ else
+ prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height;
+
+ return new Dimension (prefWidth, prefHeight);
+ }
+
+ /**
+ *
Return the number of pixels the list must scroll in order to move a
+ * "unit" of the list into the provided visible rectangle. When the
+ * provided direction is positive, the call describes a "downwards"
+ * scroll, which will be exposing a cell at a greater index in
+ * the list than those elements currently showing. Then the provided
+ * direction is negative, the call describes an "upwards" scroll, which
+ * will be exposing a cell at a lesser index in the list than
+ * those elements currently showing.
+ *
+ *
If the provided orientation is HORIZONTAL, the above
+ * comments refer to "rightwards" for positive direction, and "leftwards"
+ * for negative.
+ *
+ *
+ * @param visibleRect The rectangle to scroll an element into
+ * @param orientation One of the numeric consants VERTICAL
+ * or HORIZONTAL
+ * @param direction An integer indicating the scroll direction: positive means
+ * forwards (down, right), negative means backwards (up, left)
+ *
+ * @return The scrollable unit increment, in pixels
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int unit = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ int row = getFirstVisibleIndex();
+ if (row == -1)
+ unit = 0;
+ else if (direction > 0)
+ {
+ // Scrolling down.
+ Rectangle bounds = getCellBounds(row, row);
+ if (bounds != null)
+ unit = bounds.height - (visibleRect.y - bounds.y);
+ else
+ unit = 0;
+ }
+ else
+ {
+ // Scrolling up.
+ Rectangle bounds = getCellBounds(row, row);
+ // First row.
+ if (row == 0 && bounds.y == visibleRect.y)
+ unit = 0; // No need to scroll.
+ else if (bounds.y == visibleRect.y)
+ {
+ // Scroll to previous row.
+ Point loc = bounds.getLocation();
+ loc.y--;
+ int prev = locationToIndex(loc);
+ Rectangle prevR = getCellBounds(prev, prev);
+ if (prevR == null || prevR.y >= bounds.y)
+ unit = 0; // For multicolumn lists.
+ else
+ unit = prevR.height;
+ }
+ else
+ unit = visibleRect.y - bounds.y;
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL)
+ {
+ // Horizontal scrolling.
+ int i = locationToIndex(visibleRect.getLocation());
+ if (i != -1)
+ {
+ Rectangle b = getCellBounds(i, i);
+ if (b != null)
+ {
+ if (b.x != visibleRect.x)
+ {
+ if (direction < 0)
+ unit = Math.abs(b.x - visibleRect.x);
+ else
+ unit = b.width + b.x - visibleRect.x;
+ }
+ else
+ unit = b.width;
+ }
+ }
+ }
+
+ if (unit == -1)
+ {
+ // This fallback seems to be used by the RI for the degenerate cases
+ // not covered above.
+ Font f = getFont();
+ unit = f != null ? f.getSize() : 1;
+ }
+ return unit;
+ }
+
+ /**
+ *
Return the number of pixels the list must scroll in order to move a
+ * "block" of the list into the provided visible rectangle. When the
+ * provided direction is positive, the call describes a "downwards"
+ * scroll, which will be exposing a cell at a greater index in
+ * the list than those elements currently showing. Then the provided
+ * direction is negative, the call describes an "upwards" scroll, which
+ * will be exposing a cell at a lesser index in the list than
+ * those elements currently showing.
+ *
+ *
If the provided orientation is HORIZONTAL, the above
+ * comments refer to "rightwards" for positive direction, and "leftwards"
+ * for negative.
+ *
+ *
+ * @param visibleRect The rectangle to scroll an element into
+ * @param orientation One of the numeric consants VERTICAL
+ * or HORIZONTAL
+ * @param direction An integer indicating the scroll direction: positive means
+ * forwards (down, right), negative means backwards (up, left)
+ *
+ * @return The scrollable unit increment, in pixels
+ */
+ public int getScrollableBlockIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int block = -1;
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ // Default block scroll. Special cases are handled below for
+ // better usability.
+ block = visibleRect.height;
+ if (direction > 0)
+ {
+ // Scroll down.
+ // Scroll so that after scrolling the last line aligns with
+ // the lower boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y + visibleRect.height - 1);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.y - visibleRect.y;
+ if (block == 0&& last < getModel().getSize() - 1)
+ block = lastR.height;
+ }
+ }
+ }
+ else
+ {
+ // Scroll up.
+ // Scroll so that after scrolling the first line aligns with
+ // the upper boundary of the visible area.
+ Point p = new Point(visibleRect.x,
+ visibleRect.y - visibleRect.height);
+ int newFirst = locationToIndex(p);
+ if (newFirst != -1)
+ {
+ int first = getFirstVisibleIndex();
+ if (first == -1)
+ first = locationToIndex(visibleRect.getLocation());
+ Rectangle newFirstR = getCellBounds(newFirst, newFirst);
+ Rectangle firstR = getCellBounds(first, first);
+ if (newFirstR != null && firstR != null)
+ {
+ // Search first item that would left the current first
+ // item visible when scrolled to.
+ while (newFirstR.y + visibleRect.height
+ < firstR.y + firstR.height
+ && newFirstR.y < firstR.y)
+ {
+ newFirst++;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ }
+ block = visibleRect.y - newFirstR.y;
+ if (block <= 0 && newFirstR.y > 0)
+ {
+ newFirst--;
+ newFirstR = getCellBounds(newFirst, newFirst);
+ if (newFirstR != null)
+ block = visibleRect.y - newFirstR.y;
+ }
+ }
+ }
+ }
+ }
+ else if (orientation == SwingConstants.HORIZONTAL
+ && getLayoutOrientation() != VERTICAL)
+ {
+ // Default block increment. Special cases are handled below for
+ // better usability.
+ block = visibleRect.width;
+ if (direction > 0)
+ {
+ // Scroll right.
+ Point p = new Point(visibleRect.x + visibleRect.width + 1,
+ visibleRect.y);
+ int last = locationToIndex(p);
+ if (last != -1)
+ {
+ Rectangle lastR = getCellBounds(last, last);
+ if (lastR != null)
+ {
+ block = lastR.x - visibleRect.x;
+ if (block < 0)
+ block += lastR.width;
+ else if (block == 0 && last < getModel().getSize() - 1)
+ block = lastR.width;
+ }
+ }
+ }
+ else
+ {
+ // Scroll left.
+ Point p = new Point(visibleRect.x - visibleRect.width,
+ visibleRect.y);
+ int first = locationToIndex(p);
+ if (first != -1)
+ {
+ Rectangle firstR = getCellBounds(first, first);
+ if (firstR != null)
+ {
+ if (firstR.x < visibleRect.x - visibleRect.width)
+ {
+ if (firstR.x + firstR.width > visibleRect.x)
+ block = visibleRect.x - firstR.x;
+ else
+ block = visibleRect.x - firstR.x - firstR.width;
+ }
+ else
+ block = visibleRect.x - firstR.x;
+ }
+ }
+ }
+ }
+
+ return block;
+ }
+
+ /**
+ * Gets the value of the scrollableTracksViewportWidth property.
+ *
+ * @return true if the viewport is larger (horizontally)
+ * than the list and the list should be expanded to fit the viewport;
+ * false if the viewport is smaller than the list and the
+ * list should scroll (horizontally) within the viewport
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ Component parent = getParent();
+ boolean retVal = false;
+ if (parent instanceof JViewport)
+ {
+ JViewport viewport = (JViewport) parent;
+ Dimension pref = getPreferredSize();
+ if (viewport.getSize().width > pref.width)
+ retVal = true;
+ if ((getLayoutOrientation() == HORIZONTAL_WRAP)
+ && (getVisibleRowCount() <= 0))
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Gets the value of the scrollableTracksViewportWidth property.
+ *
+ * @return true if the viewport is larger (vertically)
+ * than the list and the list should be expanded to fit the viewport;
+ * false if the viewport is smaller than the list and the
+ * list should scroll (vertically) within the viewport
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ Component parent = getParent();
+ boolean retVal = false;
+ if (parent instanceof JViewport)
+ {
+ JViewport viewport = (JViewport) parent;
+ Dimension pref = getPreferredSize();
+ if (viewport.getSize().height > pref.height)
+ retVal = true;
+ if ((getLayoutOrientation() == VERTICAL_WRAP)
+ && (getVisibleRowCount() <= 0))
+ retVal = true;
+ }
+ return retVal;
+ }
+
+ /**
+ * Returns the index of the anchor item in the current selection, or
+ * -1 if there is no anchor item.
+ *
+ * @return The item index.
+ */
+ public int getAnchorSelectionIndex()
+ {
+ return selectionModel.getAnchorSelectionIndex();
+ }
+
+ /**
+ * Returns the index of the lead item in the current selection, or
+ * -1 if there is no lead item.
+ *
+ * @return The item index.
+ */
+ public int getLeadSelectionIndex()
+ {
+ return selectionModel.getLeadSelectionIndex();
+ }
+
+ /**
+ * Returns the lowest item index in the current selection, or -1
+ * if there is no selection.
+ *
+ * @return The index.
+ *
+ * @see #getMaxSelectionIndex()
+ */
+ public int getMinSelectionIndex()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Returns the highest item index in the current selection, or
+ * -1 if there is no selection.
+ *
+ * @return The index.
+ *
+ * @see #getMinSelectionIndex()
+ */
+ public int getMaxSelectionIndex()
+ {
+ return selectionModel.getMaxSelectionIndex();
+ }
+
+ /**
+ * Clears the current selection.
+ */
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ }
+
+ /**
+ * Sets the current selection to the items in the specified range (inclusive).
+ * Note that anchor can be less than, equal to, or greater than
+ * lead.
+ *
+ * @param anchor the index of the anchor item.
+ * @param lead the index of the anchor item.
+ */
+ public void setSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.setSelectionInterval(anchor, lead);
+ }
+
+ /**
+ * Adds the specified interval to the current selection. Note that
+ * anchor can be less than, equal to, or greater than
+ * lead.
+ *
+ * @param anchor the index of the anchor item.
+ * @param lead the index of the lead item.
+ */
+ public void addSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.addSelectionInterval(anchor, lead);
+ }
+
+ /**
+ * Removes the specified interval from the current selection. Note that
+ * index0 can be less than, equal to, or greater than
+ * index1.
+ *
+ * @param index0 an index for one end of the range.
+ * @param index1 an index for the other end of the range.
+ */
+ public void removeSelectionInterval(int index0, int index1)
+ {
+ selectionModel.removeSelectionInterval(index0, index1);
+ }
+
+ /**
+ * Returns the valueIsAdjusting flag from the list's selection
+ * model.
+ *
+ * @return the value
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return selectionModel.getValueIsAdjusting();
+ }
+
+ /**
+ * Sets the valueIsAdjusting flag in the list's selection
+ * model.
+ *
+ * @param isAdjusting the new value
+ */
+ public void setValueIsAdjusting(boolean isAdjusting)
+ {
+ selectionModel.setValueIsAdjusting(isAdjusting);
+ }
+
+ /**
+ * Return the value of the dragEnabled property.
+ *
+ * @return the value
+ *
+ * @since 1.4
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Set the dragEnabled property.
+ *
+ * @param enabled new value
+ *
+ * @since 1.4
+ */
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ /**
+ * Returns the layout orientation, which will be one of {@link #VERTICAL},
+ * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value
+ * is {@link #VERTICAL}.
+ *
+ * @return the orientation.
+ *
+ * @see #setLayoutOrientation(int)
+ * @since 1.4
+ */
+ public int getLayoutOrientation()
+ {
+ return layoutOrientation;
+ }
+
+ /**
+ * Sets the layout orientation (this is a bound property with the name
+ * 'layoutOrientation'). Valid orientations are {@link #VERTICAL},
+ * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}.
+ *
+ * @param orientation the orientation.
+ *
+ * @throws IllegalArgumentException if orientation is not one
+ * of the specified values.
+ * @since 1.4
+ * @see #getLayoutOrientation()
+ */
+ public void setLayoutOrientation(int orientation)
+ {
+ if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP)
+ throw new IllegalArgumentException();
+ if (layoutOrientation == orientation)
+ return;
+
+ int old = layoutOrientation;
+ layoutOrientation = orientation;
+ firePropertyChange("layoutOrientation", old, orientation);
+ }
+
+ /**
+ * Returns the bounds of the rectangle that encloses both list cells
+ * with index0 and index1.
+ *
+ * @param index0 the index of the first cell
+ * @param index1 the index of the second cell
+ *
+ * @return the bounds of the rectangle that encloses both list cells
+ * with index0 and index1, null if one of the indices is
+ * not valid
+ */
+ public Rectangle getCellBounds(int index0, int index1)
+ {
+ ListUI ui = getUI();
+ Rectangle bounds = null;
+ if (ui != null)
+ {
+ bounds = ui.getCellBounds(this, index0, index1);
+ }
+ // When the UI is null, this method also returns null in the RI.
+ return bounds;
+ }
+
+ /**
+ * Returns the index of the next list element (beginning at
+ * startIndex and moving in the specified direction through the
+ * list, looping around if necessary) that starts with prefix
+ * (ignoring case).
+ *
+ * @param prefix the prefix to search for in the cell values
+ * @param startIndex the index where to start searching from
+ * @param direction the search direction, either {@link Position.Bias#Forward}
+ * or {@link Position.Bias#Backward} (null is interpreted
+ * as {@link Position.Bias#Backward}.
+ *
+ * @return the index of the found element or -1 if no such element has
+ * been found
+ *
+ * @throws IllegalArgumentException if prefix is null or
+ * startIndex is not valid
+ *
+ * @since 1.4
+ */
+ public int getNextMatch(String prefix, int startIndex,
+ Position.Bias direction)
+ {
+ if (prefix == null)
+ throw new IllegalArgumentException("The argument 'prefix' must not be"
+ + " null.");
+ if (startIndex < 0)
+ throw new IllegalArgumentException("The argument 'startIndex' must not"
+ + " be less than zero.");
+
+ int size = model.getSize();
+ if (startIndex >= model.getSize())
+ throw new IllegalArgumentException("The argument 'startIndex' must not"
+ + " be greater than the number of"
+ + " elements in the ListModel.");
+
+ int result = -1;
+ int current = startIndex;
+ int delta = -1;
+ int itemCount = model.getSize();
+ boolean finished = false;
+ prefix = prefix.toUpperCase();
+
+ if (direction == Position.Bias.Forward)
+ delta = 1;
+ while (!finished)
+ {
+ String itemStr = model.getElementAt(current).toString().toUpperCase();
+ if (itemStr.startsWith(prefix))
+ return current;
+ current = (current + delta);
+ if (current == -1)
+ current += itemCount;
+ else
+ current = current % itemCount;
+ finished = current == startIndex;
+ }
+ return result;
+ }
+
+ /**
+ * Returns a string describing the attributes for the JList
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JList.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",fixedCellHeight=").append(getFixedCellHeight());
+ sb.append(",fixedCellWidth=").append(getFixedCellWidth());
+ sb.append(",selectionBackground=");
+ if (getSelectionBackground() != null)
+ sb.append(getSelectionBackground());
+ sb.append(",selectionForeground=");
+ if (getSelectionForeground() != null)
+ sb.append(getSelectionForeground());
+ sb.append(",visibleRowCount=").append(getVisibleRowCount());
+ sb.append(",layoutOrientation=").append(getLayoutOrientation());
+ return sb.toString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenu.java b/libjava/classpath/javax/swing/JMenu.java
new file mode 100644
index 000000000..e715ff98d
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenu.java
@@ -0,0 +1,1290 @@
+/* JMenu.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.GraphicsConfiguration;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.event.KeyEvent;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuEvent;
+import javax.swing.event.MenuListener;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * This class represents a menu that can be added to a menu bar or
+ * can be a submenu in some other menu. When JMenu is selected it
+ * displays JPopupMenu containing its menu items.
+ *
+ *
+ * JMenu's fires MenuEvents when this menu's selection changes. If this menu
+ * is selected, then fireMenuSelectedEvent() is invoked. In case when menu is
+ * deselected or cancelled, then fireMenuDeselectedEvent() or
+ * fireMenuCancelledEvent() is invoked, respectivelly.
+ *
+ */
+public class JMenu extends JMenuItem implements Accessible, MenuElement
+{
+ /**
+ * Receives notifications when the JMenu's ButtonModel is changed and
+ * fires menuSelected or menuDeselected events when appropriate.
+ */
+ private class MenuChangeListener
+ implements ChangeListener
+ {
+ /**
+ * Indicates the last selected state.
+ */
+ private boolean selected;
+
+ /**
+ * Receives notification when the JMenu's ButtonModel changes.
+ */
+ public void stateChanged(ChangeEvent ev)
+ {
+ ButtonModel m = (ButtonModel) ev.getSource();
+ boolean s = m.isSelected();
+ if (s != selected)
+ {
+ if (s)
+ fireMenuSelected();
+ else
+ fireMenuDeselected();
+ selected = s;
+ }
+ }
+ }
+
+ private static final long serialVersionUID = 4227225638931828014L;
+
+ /** A Popup menu associated with this menu, which pops up when menu is selected */
+ private JPopupMenu popupMenu = null;
+
+ /** Whenever menu is selected or deselected the MenuEvent is fired to
+ menu's registered listeners. */
+ private MenuEvent menuEvent = new MenuEvent(this);
+
+ /*Amount of time, in milliseconds, that should pass before popupMenu
+ associated with this menu appears or disappers */
+ private int delay;
+
+ /* PopupListener */
+ protected WinListener popupListener;
+
+ /**
+ * Location at which popup menu associated with this menu will be
+ * displayed
+ */
+ private Point menuLocation;
+
+ /**
+ * The ChangeListener for the ButtonModel.
+ *
+ * @see MenuChangeListener
+ */
+ private ChangeListener menuChangeListener;
+
+ /**
+ * Creates a new JMenu object.
+ */
+ public JMenu()
+ {
+ super();
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new JMenu with the specified label.
+ *
+ * @param text label for this menu
+ */
+ public JMenu(String text)
+ {
+ super(text);
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new JMenu object.
+ *
+ * @param action Action that is used to create menu item tha will be
+ * added to the menu.
+ */
+ public JMenu(Action action)
+ {
+ super(action);
+ createActionChangeListener(this);
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ setOpaque(false);
+ }
+
+ /**
+ * Creates a new JMenu with specified label and an option
+ * for this menu to be tear-off menu.
+ *
+ * @param text label for this menu
+ * @param tearoff true if this menu should be tear-off and false otherwise
+ */
+ public JMenu(String text, boolean tearoff)
+ {
+ // FIXME: tearoff not implemented
+ this(text);
+ }
+
+ /**
+ * Adds specified menu item to this menu
+ *
+ * @param item Menu item to add to this menu
+ *
+ * @return Menu item that was added
+ */
+ public JMenuItem add(JMenuItem item)
+ {
+ return getPopupMenu().add(item);
+ }
+
+ /**
+ * Adds specified component to this menu.
+ *
+ * @param component Component to add to this menu
+ *
+ * @return Component that was added
+ */
+ public Component add(Component component)
+ {
+ getPopupMenu().insert(component, -1);
+ return component;
+ }
+
+ /**
+ * Adds specified component to this menu at the given index
+ *
+ * @param component Component to add
+ * @param index Position of this menu item in the menu
+ *
+ * @return Component that was added
+ */
+ public Component add(Component component, int index)
+ {
+ return getPopupMenu().add(component, index);
+ }
+
+ /**
+ * Adds JMenuItem constructed with the specified label to this menu
+ *
+ * @param text label for the menu item that will be added
+ *
+ * @return Menu Item that was added to this menu
+ */
+ public JMenuItem add(String text)
+ {
+ return add(new JMenuItem(text));
+ }
+
+ /**
+ * Adds JMenuItem constructed using properties from specified action.
+ *
+ * @param action action to construct the menu item with
+ *
+ * @return Menu Item that was added to this menu
+ */
+ public JMenuItem add(Action action)
+ {
+ JMenuItem i = createActionComponent(action);
+ i.setAction(action);
+ add(i);
+ return i;
+ }
+
+ /**
+ * Removes given menu item from this menu. Nothing happens if
+ * this menu doesn't contain specified menu item.
+ *
+ * @param item Menu Item which needs to be removed
+ */
+ public void remove(JMenuItem item)
+ {
+ getPopupMenu().remove(item);
+ }
+
+ /**
+ * Removes component at the specified index from this menu
+ *
+ * @param index Position of the component that needs to be removed in the menu
+ */
+ public void remove(int index)
+ {
+ if (index < 0 || (index > 0 && getMenuComponentCount() == 0))
+ throw new IllegalArgumentException();
+
+ if (getMenuComponentCount() > 0)
+ popupMenu.remove(index);
+ }
+
+ /**
+ * Removes given component from this menu.
+ *
+ * @param component Component to remove
+ */
+ public void remove(Component component)
+ {
+ int index = getPopupMenu().getComponentIndex(component);
+ if (index >= 0)
+ getPopupMenu().remove(index);
+ }
+
+ /**
+ * Removes all menu items from the menu
+ */
+ public void removeAll()
+ {
+ if (popupMenu != null)
+ popupMenu.removeAll();
+ }
+
+ /**
+ * Creates JMenuItem with the specified text and inserts it in the
+ * at the specified index
+ *
+ * @param text label for the new menu item
+ * @param index index at which to insert newly created menu item.
+ */
+ public void insert(String text, int index)
+ {
+ this.insert(new JMenuItem(text), index);
+ }
+
+ /**
+ * Creates JMenuItem with the specified text and inserts it in the
+ * at the specified index. IllegalArgumentException is thrown
+ * if index is less than 0
+ *
+ * @param item menu item to insert
+ * @param index index at which to insert menu item.
+ * @return Menu item that was added to the menu
+ */
+ public JMenuItem insert(JMenuItem item, int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than zero");
+
+ getPopupMenu().insert(item, index);
+ return item;
+ }
+
+ /**
+ * Creates JMenuItem with the associated action and inserts it to the menu
+ * at the specified index. IllegalArgumentException is thrown
+ * if index is less than 0
+ *
+ * @param action Action for the new menu item
+ * @param index index at which to insert newly created menu item.
+ * @return Menu item that was added to the menu
+ */
+ public JMenuItem insert(Action action, int index)
+ {
+ JMenuItem item = new JMenuItem(action);
+ this.insert(item, index);
+
+ return item;
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuItemUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menu.
+ *
+ * @return The Look and Feel classID. "MenuUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuUI";
+ }
+
+ /**
+ * Sets model for this menu.
+ *
+ * @param model model to set
+ */
+ public void setModel(ButtonModel model)
+ {
+ ButtonModel oldModel = getModel();
+ if (oldModel != null && menuChangeListener != null)
+ oldModel.removeChangeListener(menuChangeListener);
+
+ super.setModel(model);
+
+ if (model != null)
+ {
+ if (menuChangeListener == null)
+ menuChangeListener = new MenuChangeListener();
+ model.addChangeListener(menuChangeListener);
+ }
+ }
+
+ /**
+ * Returns true if the menu is selected and false otherwise
+ *
+ * @return true if the menu is selected and false otherwise
+ */
+ public boolean isSelected()
+ {
+ return super.isSelected();
+ }
+
+ /**
+ * Changes this menu selected state if selected is true and false otherwise
+ * This method fires menuEvents to menu's registered listeners.
+ *
+ * @param selected true if the menu should be selected and false otherwise
+ */
+ public void setSelected(boolean selected)
+ {
+ ButtonModel m = getModel();
+ if (selected != m.isSelected())
+ m.setSelected(selected);
+ }
+
+ /**
+ * Checks if PopupMenu associated with this menu is visible
+ *
+ * @return true if the popup associated with this menu is currently visible
+ * on the screen and false otherwise.
+ */
+ public boolean isPopupMenuVisible()
+ {
+ return getPopupMenu().isVisible();
+ }
+
+ /**
+ * Sets popup menu visibility
+ *
+ * @param popup true if popup should be visible and false otherwise
+ */
+ public void setPopupMenuVisible(boolean popup)
+ {
+ if (popup != isPopupMenuVisible() && (isEnabled() || ! popup))
+ {
+ if (popup && isShowing())
+ {
+ // Set location as determined by getPopupLocation().
+ Point loc = menuLocation == null ? getPopupMenuOrigin()
+ : menuLocation;
+ getPopupMenu().show(this, loc.x, loc.y);
+ }
+ else
+ getPopupMenu().setVisible(false);
+ }
+ }
+
+ /**
+ * Returns origin point of the popup menu. This takes the screen bounds
+ * into account and places the popup where it fits best.
+ *
+ * @return the origin of the popup menu
+ */
+ protected Point getPopupMenuOrigin()
+ {
+ // The menu's screen location and size.
+ Point screenLoc = getLocationOnScreen();
+ Dimension size = getSize();
+
+ // Determine the popup's size.
+ JPopupMenu popup = getPopupMenu();
+ Dimension popupSize = popup.getSize();
+ if (popupSize.width == 0 || popupSize.height == 0)
+ popupSize = popup.getPreferredSize();
+
+ // Determine screen bounds.
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ Rectangle screenBounds = new Rectangle(tk.getScreenSize());
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice gd = ge.getDefaultScreenDevice();
+ GraphicsConfiguration gc = gd.getDefaultConfiguration();
+ Insets screenInsets = tk.getScreenInsets(gc);
+ screenBounds.x -= screenInsets.left;
+ screenBounds.width -= screenInsets.left + screenInsets.right;
+ screenBounds.y -= screenInsets.top;
+ screenBounds.height -= screenInsets.top + screenInsets.bottom;
+ screenLoc.x -= screenInsets.left;
+ screenLoc.y -= screenInsets.top;
+
+ Point point = new Point();
+ if (isTopLevelMenu())
+ {
+ // If menu in the menu bar.
+ int xOffset = UIManager.getInt("Menu.menuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.menuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = xOffset;
+ // Check if it fits, otherwise place popup wherever it fits.
+ if (screenLoc.x + point.x + popupSize.width
+ > screenBounds.width + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup to the left.
+ point.x = size.width - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the left if there's not enough room.
+ point.x = xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = size.height + yOffset;
+ if (screenLoc.y + point.y + popupSize.height >= screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Position above if there's not enough room below.
+ point.y = - yOffset - popupSize.height;
+ }
+ else
+ {
+ // If submenu.
+ int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX");
+ int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY");
+ // Determine X location.
+ if (getComponentOrientation().isLeftToRight())
+ {
+ // Prefer popup to the right.
+ point.x = size.width + xOffset;
+ if (screenLoc.x + point.x + popupSize.width
+ >= screenBounds.x + screenBounds.width
+ && screenBounds.width - size.width
+ < 2 * (screenLoc.x - screenBounds.x))
+ // Position to the left if there's not enough room on the right.
+ point.x = - xOffset - popupSize.width;
+ }
+ else
+ {
+ // Prefer popup on the left side.
+ point.x = - xOffset - popupSize.width;
+ if (screenLoc.x + point.x < screenBounds.x
+ && screenBounds.width - size.width
+ > 2 * (screenLoc.x - screenBounds.x))
+ // Popup to the right if there's not enough room.
+ point.x = size.width + xOffset;
+ }
+ // Determine Y location. Prefer popping down.
+ point.y = yOffset;
+ if (screenLoc.y + point.y + popupSize.height
+ >= screenBounds.y + screenBounds.height
+ && screenBounds.height - size.height
+ < 2 * (screenLoc.y - screenBounds.y))
+ // Pop up if there's not enough room below.
+ point.y = size.height - yOffset - popupSize.height;
+ }
+ return point;
+ }
+
+ /**
+ * Returns delay property.
+ *
+ * @return delay property, indicating number of milliseconds before
+ * popup menu associated with the menu appears or disappears after
+ * menu was selected or deselected respectively
+ */
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ /**
+ * Sets delay property for this menu. If given time for the delay
+ * property is negative, then IllegalArgumentException is thrown
+ *
+ * @param delay number of milliseconds before
+ * popup menu associated with the menu appears or disappears after
+ * menu was selected or deselected respectively
+ */
+ public void setDelay(int delay)
+ {
+ if (delay < 0)
+ throw new IllegalArgumentException("delay less than 0");
+ this.delay = delay;
+ }
+
+ /**
+ * Sets location at which popup menu should be displayed
+ * The location given is relative to this menu item
+ *
+ * @param x x-coordinate of the menu location
+ * @param y y-coordinate of the menu location
+ */
+ public void setMenuLocation(int x, int y)
+ {
+ menuLocation = new Point(x, y);
+ if (popupMenu != null)
+ popupMenu.setLocation(x, y);
+ }
+
+ /**
+ * Creates and returns JMenuItem associated with the given action
+ *
+ * @param action Action to use for creation of JMenuItem
+ *
+ * @return JMenuItem that was creted with given action
+ */
+ protected JMenuItem createActionComponent(Action action)
+ {
+ return new JMenuItem(action);
+ }
+
+ /**
+ * Creates ActionChangeListener to listen for PropertyChangeEvents occuring
+ * in the action that is associated with this menu
+ *
+ * @param item menu that contains action to listen to
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionChangeListener(JMenuItem item)
+ {
+ return new ActionChangedListener(item);
+ }
+
+ /**
+ * Adds separator to the end of the menu items in the menu.
+ */
+ public void addSeparator()
+ {
+ getPopupMenu().addSeparator();
+ }
+
+ /**
+ * Inserts separator in the menu at the specified index.
+ *
+ * @param index Index at which separator should be inserted
+ */
+ public void insertSeparator(int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than 0");
+
+ getPopupMenu().insert(new JPopupMenu.Separator(), index);
+ }
+
+ /**
+ * Returns menu item located at the specified index in the menu
+ *
+ * @param index Index at which to look for the menu item
+ *
+ * @return menu item located at the specified index in the menu
+ */
+ public JMenuItem getItem(int index)
+ {
+ if (index < 0)
+ throw new IllegalArgumentException("index less than 0");
+
+ if (getItemCount() == 0)
+ return null;
+
+ Component c = popupMenu.getComponentAtIndex(index);
+
+ if (c instanceof JMenuItem)
+ return (JMenuItem) c;
+ else
+ return null;
+ }
+
+ /**
+ * Returns number of items in the menu including separators.
+ *
+ * @return number of items in the menu
+ *
+ * @see #getMenuComponentCount()
+ */
+ public int getItemCount()
+ {
+ return getMenuComponentCount();
+ }
+
+ /**
+ * Checks if this menu is a tear-off menu.
+ *
+ * @return true if this menu is a tear-off menu and false otherwise
+ */
+ public boolean isTearOff()
+ {
+ // NOT YET IMPLEMENTED
+ throw new Error("The method isTearOff() has not yet been implemented.");
+ }
+
+ /**
+ * Returns number of menu components in this menu
+ *
+ * @return number of menu components in this menu
+ */
+ public int getMenuComponentCount()
+ {
+ return getPopupMenu().getComponentCount();
+ }
+
+ /**
+ * Returns menu component located at the givent index
+ * in the menu
+ *
+ * @param index index at which to get the menu component in the menu
+ *
+ * @return Menu Component located in the menu at the specified index
+ */
+ public Component getMenuComponent(int index)
+ {
+ if (getPopupMenu() == null || getMenuComponentCount() == 0)
+ return null;
+
+ return popupMenu.getComponentAtIndex(index);
+ }
+
+ /**
+ * Return components belonging to this menu
+ *
+ * @return components belonging to this menu
+ */
+ public Component[] getMenuComponents()
+ {
+ return getPopupMenu().getComponents();
+ }
+
+ /**
+ * Checks if this menu is a top level menu. The menu is top
+ * level menu if it is inside the menu bar. While if the menu
+ * inside some other menu, it is considered to be a pull-right menu.
+ *
+ * @return true if this menu is top level menu, and false otherwise
+ */
+ public boolean isTopLevelMenu()
+ {
+ return getParent() instanceof JMenuBar;
+ }
+
+ /**
+ * Checks if given component exists in this menu. The submenus of
+ * this menu are checked as well
+ *
+ * @param component Component to look for
+ *
+ * @return true if the given component exists in this menu, and false otherwise
+ */
+ public boolean isMenuComponent(Component component)
+ {
+ return false;
+ }
+
+ /**
+ * Returns popup menu associated with the menu.
+ *
+ * @return popup menu associated with the menu.
+ */
+ public JPopupMenu getPopupMenu()
+ {
+ if (popupMenu == null)
+ {
+ popupMenu = new JPopupMenu();
+ popupMenu.setInvoker(this);
+ }
+ return popupMenu;
+ }
+
+ /**
+ * Adds MenuListener to the menu
+ *
+ * @param listener MenuListener to add
+ */
+ public void addMenuListener(MenuListener listener)
+ {
+ listenerList.add(MenuListener.class, listener);
+ }
+
+ /**
+ * Removes MenuListener from the menu
+ *
+ * @param listener MenuListener to remove
+ */
+ public void removeMenuListener(MenuListener listener)
+ {
+ listenerList.remove(MenuListener.class, listener);
+ }
+
+ /**
+ * Returns all registered MenuListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuListener[] getMenuListeners()
+ {
+ return (MenuListener[]) listenerList.getListeners(MenuListener.class);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuSelected() method of MenuListeners is called to indicated that the menu
+ * was selected.
+ */
+ protected void fireMenuSelected()
+ {
+ MenuListener[] listeners = getMenuListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].menuSelected(menuEvent);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuDeselected() method of MenuListeners is called to indicated that the menu
+ * was deselected.
+ */
+ protected void fireMenuDeselected()
+ {
+ EventListener[] ll = listenerList.getListeners(MenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuListener) ll[i]).menuDeselected(menuEvent);
+ }
+
+ /**
+ * This method fires MenuEvents to all menu's MenuListeners. In this case
+ * menuSelected() method of MenuListeners is called to indicated that the menu
+ * was cancelled. The menu is cancelled when it's popup menu is close without selection.
+ */
+ protected void fireMenuCanceled()
+ {
+ EventListener[] ll = listenerList.getListeners(MenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuListener) ll[i]).menuCanceled(menuEvent);
+ }
+
+ /**
+ * Creates WinListener that listens to the menu;s popup menu.
+ *
+ * @param popup JPopupMenu to listen to
+ *
+ * @return The WinListener
+ */
+ protected WinListener createWinListener(JPopupMenu popup)
+ {
+ return new WinListener(popup);
+ }
+
+ /**
+ * Method of the MenuElementInterface. It reacts to the selection
+ * changes in the menu. If this menu was selected, then it
+ * displayes popup menu associated with it and if this menu was
+ * deselected it hides the popup menu.
+ *
+ * @param changed true if the menu was selected and false otherwise
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ // if this menu selection is true, then activate this menu and
+ // display popup associated with this menu
+ setSelected(changed);
+ }
+
+ /**
+ * Method of MenuElement interface. Returns sub components of
+ * this menu.
+ *
+ * @return array containing popupMenu that is associated with this menu
+ */
+ public MenuElement[] getSubElements()
+ {
+ return new MenuElement[] { popupMenu };
+ }
+
+ /**
+ * @return Returns reference to itself
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * This method is overriden with empty implementation, s.t the
+ * accelerator couldn't be set for the menu. The mnemonic should
+ * be used for the menu instead.
+ *
+ * @param keystroke accelerator for this menu
+ */
+ public void setAccelerator(KeyStroke keystroke)
+ {
+ throw new Error("setAccelerator() is not defined for JMenu. Use setMnemonic() instead.");
+ }
+
+ /**
+ * This method process KeyEvent occuring when the menu is visible
+ *
+ * @param event The KeyEvent
+ */
+ protected void processKeyEvent(KeyEvent event)
+ {
+ MenuSelectionManager.defaultManager().processKeyEvent(event);
+ }
+
+ /**
+ * Programatically performs click
+ *
+ * @param time Number of milliseconds for which this menu stays pressed
+ */
+ public void doClick(int time)
+ {
+ getModel().setArmed(true);
+ getModel().setPressed(true);
+ try
+ {
+ java.lang.Thread.sleep(time);
+ }
+ catch (java.lang.InterruptedException e)
+ {
+ // probably harmless
+ }
+
+ getModel().setPressed(false);
+ getModel().setArmed(false);
+ popupMenu.show(this, this.getWidth(), 0);
+ }
+
+ /**
+ * A string that describes this JMenu. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenu
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJMenu();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Implements support for assisitive technologies for JMenu.
+ */
+ protected class AccessibleJMenu extends AccessibleJMenuItem
+ implements AccessibleSelection
+ {
+ private static final long serialVersionUID = -8131864021059524309L;
+
+ protected AccessibleJMenu()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the number of accessible children of this object.
+ *
+ * @return the number of accessible children of this object
+ */
+ public int getAccessibleChildrenCount()
+ {
+ Component[] children = getMenuComponents();
+ int count = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i] instanceof Accessible)
+ count++;
+ }
+ return count;
+ }
+
+ /**
+ * Returns the accessible child with the specified index.
+ *
+ * @param index the index of the child to fetch
+ *
+ * @return the accessible child with the specified index
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ Component[] children = getMenuComponents();
+ int count = 0;
+ Accessible found = null;
+ for (int i = 0; i < children.length; i++)
+ {
+ if (children[i] instanceof Accessible)
+ {
+ if (count == index)
+ {
+ found = (Accessible) children[i];
+ break;
+ }
+ count++;
+ }
+ }
+ return found;
+ }
+
+ /**
+ * Returns the accessible selection of this object. AccessibleJMenus handle
+ * their selection themselves, so we always return this here.
+ *
+ * @return the accessible selection of this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the selected accessible child with the specified
+ * index.
+ *
+ * @param index the index of the accessible selected child to return
+ *
+ * @return the selected accessible child with the specified
+ * index
+ */
+ public Accessible getAccessibleSelection(int index)
+ {
+ Accessible selected = null;
+ // Only one item can be selected, which must therefore have index == 0.
+ if (index == 0)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == JMenu.this)
+ {
+ // This JMenu is selected, find and return the next
+ // JMenuItem in the path.
+ do
+ {
+ if (me[i] instanceof Accessible)
+ {
+ selected = (Accessible) me[i];
+ break;
+ }
+ i++;
+ } while (i < me.length);
+ }
+ if (selected != null)
+ break;
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns true if the accessible child with the specified
+ * index is selected, false otherwise.
+ *
+ * @param index the index of the accessible child to check
+ *
+ * @return true if the accessible child with the specified
+ * index is selected, false otherwise
+ */
+ public boolean isAccessibleChildSelected(int index)
+ {
+ boolean selected = false;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ Accessible toBeFound = getAccessibleChild(index);
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == toBeFound)
+ {
+ selected = true;
+ break;
+ }
+ }
+ }
+ return selected;
+ }
+
+ /**
+ * Returns the accessible role of this object, which is
+ * {@link AccessibleRole#MENU} for the AccessibleJMenu.
+ *
+ * @return the accessible role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU;
+ }
+
+ /**
+ * Returns the number of selected accessible children. This will be
+ * 0 if no item is selected, or 1 if an item
+ * is selected. AccessibleJMenu can have maximum 1 selected item.
+ *
+ * @return the number of selected accessible children
+ */
+ public int getAccessibleSelectionCount()
+ {
+ int count = 0;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] me = msm.getSelectedPath();
+ if (me != null)
+ {
+ for (int i = 0; i < me.length; i++)
+ {
+ if (me[i] == JMenu.this)
+ {
+ if (i + 1 < me.length)
+ {
+ count = 1;
+ break;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ /**
+ * Selects the accessible child with the specified index.
+ *
+ * @param index the index of the accessible child to select
+ */
+ public void addAccessibleSelection(int index)
+ {
+ Accessible child = getAccessibleChild(index);
+ if (child != null && child instanceof JMenuItem)
+ {
+ JMenuItem mi = (JMenuItem) child;
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ msm.setSelectedPath(createPath(JMenu.this));
+ }
+ }
+
+ /**
+ * Removes the item with the specified index from the selection.
+ *
+ * @param index the index of the selected item to remove from the selection
+ */
+ public void removeAccessibleSelection(int index)
+ {
+ Accessible child = getAccessibleChild(index);
+ if (child != null)
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] oldSelection = msm.getSelectedPath();
+ for (int i = 0; i < oldSelection.length; i++)
+ {
+ if (oldSelection[i] == child)
+ {
+ // Found the specified child in the selection. Remove it
+ // from the selection.
+ MenuElement[] newSel = new MenuElement[i - 1];
+ System.arraycopy(oldSelection, 0, newSel, 0, i - 1);
+ msm.setSelectedPath(newSel);
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes all possibly selected accessible children of this object from
+ * the selection.
+ */
+ public void clearAccessibleSelection()
+ {
+ MenuSelectionManager msm = MenuSelectionManager.defaultManager();
+ MenuElement[] oldSelection = msm.getSelectedPath();
+ for (int i = 0; i < oldSelection.length; i++)
+ {
+ if (oldSelection[i] == JMenu.this)
+ {
+ // Found this menu in the selection. Remove all children from
+ // the selection.
+ MenuElement[] newSel = new MenuElement[i];
+ System.arraycopy(oldSelection, 0, newSel, 0, i);
+ msm.setSelectedPath(newSel);
+ break;
+ }
+ }
+ }
+
+ /**
+ * AccessibleJMenu don't support multiple selection, so this method
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // Nothing to do here.
+ }
+ }
+
+ protected class WinListener extends WindowAdapter implements Serializable
+ {
+ private static final long serialVersionUID = -6415815570638474823L;
+
+ /**
+ * Creates a new WinListener.
+ *
+ * @param popup the popup menu which is observed
+ */
+ public WinListener(JPopupMenu popup)
+ {
+ // TODO: What should we do with the popup argument?
+ }
+
+ /**
+ * Receives notification when the popup menu is closing and deselects
+ * the menu.
+ *
+ * @param event the window event
+ */
+ public void windowClosing(WindowEvent event)
+ {
+ setSelected(false);
+ }
+ }
+
+ /**
+ * This class listens to PropertyChangeEvents occuring in menu's action
+ */
+ private class ActionChangedListener implements PropertyChangeListener
+ {
+ /** menu item associated with the action */
+ private JMenuItem menuItem;
+
+ /** Creates new ActionChangedListener and adds it to menuItem's action */
+ public ActionChangedListener(JMenuItem menuItem)
+ {
+ this.menuItem = menuItem;
+
+ Action a = menuItem.getAction();
+ if (a != null)
+ a.addPropertyChangeListener(this);
+ }
+
+ /**This method is invoked when some change occures in menuItem's action*/
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // FIXME: Need to implement
+ }
+ }
+
+ /**
+ * Creates an array to be feeded as argument to
+ * {@link MenuSelectionManager#setSelectedPath(MenuElement[])} for the
+ * specified element. This has the effect of selecting the specified element
+ * and all its parents.
+ *
+ * @param leaf the leaf element for which to create the selected path
+ *
+ * @return the selected path array
+ */
+ MenuElement[] createPath(JMenu leaf)
+ {
+ ArrayList path = new ArrayList();
+ MenuElement[] array = null;
+ Component current = leaf.getPopupMenu();
+ while (true)
+ {
+ if (current instanceof JPopupMenu)
+ {
+ JPopupMenu popupMenu = (JPopupMenu) current;
+ path.add(0, popupMenu);
+ current = popupMenu.getInvoker();
+ }
+ else if (current instanceof JMenu)
+ {
+ JMenu menu = (JMenu) current;
+ path.add(0, menu);
+ current = menu.getParent();
+ }
+ else if (current instanceof JMenuBar)
+ {
+ JMenuBar menuBar = (JMenuBar) current;
+ path.add(0, menuBar);
+ array = new MenuElement[path.size()];
+ array = (MenuElement[]) path.toArray(array);
+ break;
+ }
+ }
+ return array;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenuBar.java b/libjava/classpath/javax/swing/JMenuBar.java
new file mode 100644
index 000000000..1c5a448a0
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenuBar.java
@@ -0,0 +1,692 @@
+/* JMenuBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.MenuBarUI;
+
+import javax.swing.border.Border;
+
+/**
+ * JMenuBar is a container for menu's. For a menu bar to be seen on the
+ * screen, at least one menu should be added to it. Just like adding
+ * components to container, one can use add() to add menu's to the menu bar.
+ * Menu's will be displayed in the menu bar in the order they were added.
+ * The JMenuBar uses selectionModel to keep track of selected menu index.
+ * JMenuBar's selectionModel will fire ChangeEvents to its registered
+ * listeners when the selected index changes.
+ */
+public class JMenuBar extends JComponent implements Accessible, MenuElement
+{
+ /**
+ * Provides accessibility support for JMenuBar.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJMenuBar extends AccessibleJComponent
+ implements AccessibleSelection
+ {
+
+ /**
+ * Returns the number of selected items in the menu bar. Possible values
+ * are 0 if nothing is selected, or 1 if one
+ * item is selected.
+ *
+ * @return the number of selected items in the menu bar
+ */
+ public int getAccessibleSelectionCount()
+ {
+ int count = 0;
+ if (getSelectionModel().getSelectedIndex() != -1)
+ count = 1;
+ return count;
+ }
+
+ /**
+ * Returns the selected with index i menu, or
+ * null if the specified menu is not selected.
+ *
+ * @param i the index of the menu to return
+ *
+ * @return the selected with index i menu, or
+ * null if the specified menu is not selected
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ if (getSelectionModel().getSelectedIndex() != i)
+ return null;
+ return getMenu(i);
+ }
+
+ /**
+ * Returns true if the specified menu is selected,
+ * false otherwise.
+ *
+ * @param i the index of the menu to check
+ *
+ *@return true if the specified menu is selected,
+ * false otherwise
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ return getSelectionModel().getSelectedIndex() == i;
+ }
+
+ /**
+ * Selects the menu with index i. If another menu is already
+ * selected, this will be deselected.
+ *
+ * @param i the menu to be selected
+ */
+ public void addAccessibleSelection(int i)
+ {
+ getSelectionModel().setSelectedIndex(i);
+ }
+
+ /**
+ * Deselects the menu with index i.
+ *
+ * @param i the menu index to be deselected
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (getSelectionModel().getSelectedIndex() == i)
+ getSelectionModel().clearSelection();
+ }
+
+ /**
+ * Deselects all possibly selected menus.
+ */
+ public void clearAccessibleSelection()
+ {
+ getSelectionModel().clearSelection();
+ }
+
+ /**
+ * In menu bars it is not possible to select all items, so this method
+ * does nothing.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // In menu bars it is not possible to select all items, so this method
+ // does nothing.
+ }
+
+ /**
+ * Returns the accessible role of JMenuBar, which is
+ * {@link AccessibleRole#MENU_BAR}.
+ *
+ * @return the accessible role of JMenuBar, which is
+ * {@link AccessibleRole#MENU_BAR}
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU_BAR;
+ }
+
+ /**
+ * Returns the AccessibleSelection for this object. This
+ * method returns this, since the
+ * AccessibleJMenuBar manages its selection itself.
+ *
+ * @return the AccessibleSelection for this object
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the state of this AccessibleJMenuBar.
+ *
+ * @return the state of this AccessibleJMenuBar.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet stateSet = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added to the super state set.
+ return stateSet;
+ }
+ }
+
+ private static final long serialVersionUID = -8191026883931977036L;
+
+ /** JMenuBar's model. It keeps track of selected menu's index */
+ private transient SingleSelectionModel selectionModel;
+
+ /* borderPainted property indicating if the menuBar's border will be painted*/
+ private boolean borderPainted;
+
+ /* margin between menu bar's border and its menues*/
+ private Insets margin;
+
+ /**
+ * Creates a new JMenuBar object.
+ */
+ public JMenuBar()
+ {
+ selectionModel = new DefaultSingleSelectionModel();
+ borderPainted = true;
+ updateUI();
+ }
+
+ /**
+ * Adds menu to the menu bar
+ *
+ * @param c menu to add
+ *
+ * @return reference to the added menu
+ */
+ public JMenu add(JMenu c)
+ {
+ c.setAlignmentX(Component.LEFT_ALIGNMENT);
+ super.add(c);
+ return c;
+ }
+
+ /**
+ * This method overrides addNotify() in the Container to register
+ * this menu bar with the current keyboard manager.
+ */
+ public void addNotify()
+ {
+ super.addNotify();
+ KeyboardManager.getManager().registerJMenuBar(this);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJMenuBar();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns reference to this menu bar
+ *
+ * @return reference to this menu bar
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Returns component at the specified index.
+ *
+ * @param i index of the component to get
+ *
+ * @return component at the specified index. Null is returned if
+ * component at the specified index doesn't exist.
+ * @deprecated Replaced by getComponent(int)
+ */
+ public Component getComponentAtIndex(int i)
+ {
+ return getComponent(i);
+ }
+
+ /**
+ * Returns index of the specified component
+ *
+ * @param c Component to search for
+ *
+ * @return index of the specified component. -1 is returned if
+ * specified component doesnt' exist in the menu bar.
+ */
+ public int getComponentIndex(Component c)
+ {
+ Component[] comps = getComponents();
+
+ int index = -1;
+
+ for (int i = 0; i < comps.length; i++)
+ {
+ if (comps[i].equals(c))
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * This method is not implemented and will throw an {@link Error} if called.
+ *
+ * @return This method never returns anything, it throws an exception.
+ */
+ public JMenu getHelpMenu()
+ {
+ // the following error matches the behaviour of the reference
+ // implementation...
+ throw new Error("getHelpMenu() is not implemented");
+ }
+
+ /**
+ * Returns the margin between the menu bar's border and its menus. If the
+ * margin is null, this method returns
+ * new Insets(0, 0, 0, 0).
+ *
+ * @return The margin (never null).
+ *
+ * @see #setMargin(Insets)
+ */
+ public Insets getMargin()
+ {
+ if (margin == null)
+ return new Insets(0, 0, 0, 0);
+ else
+ return margin;
+ }
+
+ /**
+ * Return menu at the specified index. If component at the
+ * specified index is not a menu, then null is returned.
+ *
+ * @param index index to look for the menu
+ *
+ * @return menu at specified index, or null if menu doesn't exist
+ * at the specified index.
+ */
+ public JMenu getMenu(int index)
+ {
+ if (getComponentAtIndex(index) instanceof JMenu)
+ return (JMenu) getComponentAtIndex(index);
+ else
+ return null;
+ }
+
+ /**
+ * Returns number of menu's in this menu bar
+ *
+ * @return number of menu's in this menu bar
+ */
+ public int getMenuCount()
+ {
+ return getComponentCount();
+ }
+
+ /**
+ * Returns selection model for this menu bar. SelectionModel
+ * keeps track of the selected menu in the menu bar. Whenever
+ * selected property of selectionModel changes, the ChangeEvent
+ * will be fired its ChangeListeners.
+ *
+ * @return selection model for this menu bar.
+ */
+ public SingleSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Method of MenuElement interface. It returns subcomponents
+ * of the menu bar, which are all the menues that it contains.
+ *
+ * @return MenuElement[] array containing menues in this menu bar
+ */
+ public MenuElement[] getSubElements()
+ {
+ MenuElement[] subElements = new MenuElement[getComponentCount()];
+
+ int j = 0;
+ boolean doResize = false;
+ MenuElement menu;
+ for (int i = 0; i < getComponentCount(); i++)
+ {
+ menu = getMenu(i);
+ if (menu != null)
+ {
+ subElements[j++] = (MenuElement) menu;
+ }
+ else
+ doResize = true;
+ }
+
+ if (! doResize)
+ return subElements;
+ else
+ {
+ MenuElement[] subElements2 = new MenuElement[j];
+ for (int i = 0; i < j; i++)
+ subElements2[i] = subElements[i];
+
+ return subElements2;
+ }
+ }
+
+ /**
+ * Set the "UI" property of the menu bar, which is a look and feel class
+ * responsible for handling the menuBar's input events and painting it.
+ *
+ * @return The current "UI" property
+ */
+ public MenuBarUI getUI()
+ {
+ return (MenuBarUI) ui;
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menu bar.
+ *
+ * @return The Look and Feel classID. "MenuBarUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuBarUI";
+ }
+
+ /**
+ * Returns true if menu bar paints its border and false otherwise
+ *
+ * @return true if menu bar paints its border and false otherwise
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Returns true if some menu in menu bar is selected.
+ *
+ * @return true if some menu in menu bar is selected and false otherwise
+ */
+ public boolean isSelected()
+ {
+ return selectionModel.isSelected();
+ }
+
+ /**
+ * This method does nothing by default. This method is need for the
+ * MenuElement interface to be implemented.
+ *
+ * @param isIncluded true if menuBar is included in the selection
+ * and false otherwise
+ */
+ public void menuSelectionChanged(boolean isIncluded)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * Paints border of the menu bar, if its borderPainted property is set to
+ * true.
+ *
+ * @param g The graphics context with which to paint the border
+ */
+ protected void paintBorder(Graphics g)
+ {
+ if (borderPainted)
+ {
+ Border border = getBorder();
+ if (border != null)
+ getBorder().paintBorder(this, g, 0, 0, getSize(null).width,
+ getSize(null).height);
+ }
+ }
+
+ /**
+ * A string that describes this JMenuBar. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenuBar
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(getMargin());
+ sb.append(",paintBorder=").append(isBorderPainted());
+ return sb.toString();
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param e event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processKeyEvent(KeyEvent e, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * This method overrides JComponent.processKeyBinding to allow the
+ * JMenuBar to check all the child components (recursiveley) to see
+ * if they'll consume the event.
+ *
+ * @param ks the KeyStroke for the event
+ * @param e the KeyEvent for the event
+ * @param condition the focus condition for the binding
+ * @param pressed true if the key is pressed
+ */
+ protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition,
+ boolean pressed)
+ {
+ // See if the regular JComponent behavior consumes the event
+ if (super.processKeyBinding(ks, e, condition, pressed))
+ return true;
+
+ // If not, have to recursively check all the child menu elements to see
+ // if they want it
+ MenuElement[] children = getSubElements();
+ for (int i = 0; i < children.length; i++)
+ if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * This is a helper method to recursively check the children of this
+ * JMenuBar to see if they will consume a key event via key bindings.
+ * This is used for menu accelerators.
+ * @param menuElement the menuElement to check (and check all its children)
+ * @param ks the KeyStroke for the event
+ * @param e the KeyEvent that may be consumed
+ * @param condition the focus condition for the binding
+ * @param pressed true if the key was pressed
+ * @return true menuElement or one of its children consume
+ * the event (processKeyBinding returns true for menuElement or one of
+ * its children).
+ */
+ static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks,
+ KeyEvent e, int condition,
+ boolean pressed)
+ {
+ if (menuElement == null)
+ return false;
+
+ // First check the menuElement itself, if it's a JComponent
+ if (menuElement instanceof JComponent
+ && ((JComponent) menuElement).processKeyBinding(ks, e, condition,
+ pressed))
+ return true;
+
+ // If that didn't consume it, check all the children recursively
+ MenuElement[] children = menuElement.getSubElements();
+ for (int i = 0; i < children.length; i++)
+ if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Do nothing - needed for implementation of MenuElement interface
+ }
+
+ /**
+ * This method overrides removeNotify() in the Container to
+ * unregister this menu bar from the current keyboard manager.
+ */
+ public void removeNotify()
+ {
+ KeyboardManager.getManager().unregisterJMenuBar(this);
+ super.removeNotify();
+ }
+
+ /**
+ * Sets painting status of the border. If 'b' is true then menu bar's
+ * border will be painted, and it will not be painted otherwise.
+ *
+ * @param b indicates if menu bar's border should be painted.
+ */
+ public void setBorderPainted(boolean b)
+ {
+ if (b != borderPainted)
+ {
+ boolean old = borderPainted;
+ borderPainted = b;
+ firePropertyChange("borderPainted", old, b);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Sets help menu for this menu bar
+ *
+ * @param menu help menu
+ *
+ * @specnote The specification states that this method is not yet implemented
+ * and should throw an exception.
+ */
+ public void setHelpMenu(JMenu menu)
+ {
+ // We throw an Error here, just as Sun's JDK does.
+ throw new Error("setHelpMenu() not yet implemented.");
+ }
+
+ /**
+ * Sets the margin between the menu bar's border and its menus (this is a
+ * bound property with the name 'margin').
+ *
+ * @param m the margin (null permitted).
+ *
+ * @see #getMargin()
+ */
+ public void setMargin(Insets m)
+ {
+ if (m != margin)
+ {
+ Insets oldMargin = margin;
+ margin = m;
+ firePropertyChange("margin", oldMargin, margin);
+ }
+ }
+
+ /**
+ * Changes menu bar's selection to the specified menu.
+ * This method updates selected index of menu bar's selection model,
+ * which results in a model firing change event.
+ *
+ * @param sel menu to select
+ */
+ public void setSelected(Component sel)
+ {
+ int index = getComponentIndex(sel);
+ selectionModel.setSelectedIndex(index);
+ }
+
+ /**
+ * Sets menuBar's selection model to the one specified
+ *
+ * @param model SingleSelectionModel that needs to be set for this menu bar
+ */
+ public void setSelectionModel(SingleSelectionModel model)
+ {
+ if (selectionModel != model)
+ {
+ SingleSelectionModel oldModel = selectionModel;
+ selectionModel = model;
+ firePropertyChange("model", oldModel, selectionModel);
+ }
+ }
+
+ /**
+ * Set the "UI" property of the menu bar, which is a look and feel class
+ * responsible for handling menuBar's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(MenuBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Set the "UI" property to a class constructed, via the {@link
+ * UIManager}, from the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuBarUI) UIManager.getUI(this));
+ }
+}
diff --git a/libjava/classpath/javax/swing/JMenuItem.java b/libjava/classpath/javax/swing/JMenuItem.java
new file mode 100644
index 000000000..fea3166a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JMenuItem.java
@@ -0,0 +1,809 @@
+/* JMenuItem.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.MenuDragMouseEvent;
+import javax.swing.event.MenuDragMouseListener;
+import javax.swing.event.MenuKeyEvent;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.plaf.MenuItemUI;
+
+/**
+ * JMenuItem represents element in the menu. It inherits most of
+ * its functionality from AbstractButton, however its behavior somewhat
+ * varies from it. JMenuItem fire different kinds of events.
+ * PropertyChangeEvents are fired when menuItems properties are modified;
+ * ChangeEvents are fired when menuItem's state changes and actionEvents are
+ * fired when menu item is selected. In addition to this events menuItem also
+ * fire MenuDragMouseEvent and MenuKeyEvents when mouse is dragged over
+ * the menu item or associated key with menu item is invoked respectively.
+ */
+public class JMenuItem extends AbstractButton implements Accessible,
+ MenuElement
+{
+ private static final long serialVersionUID = -1681004643499461044L;
+
+ /** Combination of keyboard keys that can be used to activate this menu item */
+ private KeyStroke accelerator;
+
+ /**
+ * Indicates if we are currently dragging the mouse.
+ */
+ private boolean isDragging;
+
+ /**
+ * Creates a new JMenuItem object.
+ */
+ public JMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JMenuItem with the given icon.
+ *
+ * @param icon Icon that will be displayed on the menu item
+ */
+ public JMenuItem(Icon icon)
+ {
+ // FIXME: The requestedFocusEnabled property should
+ // be set to false, when only icon is set for menu item.
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JMenuItem with the given label.
+ *
+ * @param text label for the menu item
+ */
+ public JMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JMenuItem associated with the specified action.
+ *
+ * @param action action for this menu item
+ */
+ public JMenuItem(Action action)
+ {
+ super();
+ super.setAction(action);
+ setModel(new DefaultButtonModel());
+ init(null, null);
+ if (action != null)
+ {
+ String name = (String) action.getValue(Action.NAME);
+ if (name != null)
+ setName(name);
+
+ KeyStroke accel = (KeyStroke) action.getValue(Action.ACCELERATOR_KEY);
+ if (accel != null)
+ setAccelerator(accel);
+
+ Integer mnemonic = (Integer) action.getValue(Action.MNEMONIC_KEY);
+ if (mnemonic != null)
+ setMnemonic(mnemonic.intValue());
+
+ String command = (String) action.getValue(Action.ACTION_COMMAND_KEY);
+ if (command != null)
+ setActionCommand(command);
+ }
+ }
+
+ /**
+ * Creates a new JMenuItem with specified text and icon.
+ * Text is displayed to the left of icon by default.
+ *
+ * @param text label for this menu item
+ * @param icon icon that will be displayed on this menu item
+ */
+ public JMenuItem(String text, Icon icon)
+ {
+ super();
+ setModel(new DefaultButtonModel());
+ init(text, icon);
+ }
+
+ /**
+ * Creates a new JMenuItem object.
+ *
+ * @param text label for this menu item
+ * @param mnemonic - Single key that can be used with a
+ * look-and-feel meta key to activate this menu item. However
+ * menu item should be visible on the screen when mnemonic is used.
+ */
+ public JMenuItem(String text, int mnemonic)
+ {
+ this(text, null);
+ setMnemonic(mnemonic);
+ }
+
+ /**
+ * Initializes this menu item
+ *
+ * @param text label for this menu item
+ * @param icon icon to be displayed for this menu item
+ */
+ protected void init(String text, Icon icon)
+ {
+ super.init(text, icon);
+
+ // Initializes properties for this menu item, that are different
+ // from Abstract button properties.
+ /* NOTE: According to java specifications paint_border should be set to false,
+ since menu item should not have a border. However running few java programs
+ it seems that menu items and menues can have a border. Commenting
+ out statement below for now. */
+ //borderPainted = false;
+ focusPainted = false;
+ horizontalAlignment = JButton.LEADING;
+ horizontalTextPosition = JButton.TRAILING;
+ }
+
+ /**
+ * Set the "UI" property of the menu item, which is a look and feel class
+ * responsible for handling menuItem's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(MenuItemUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((MenuItemUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "MenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return "MenuItemUI";
+ }
+
+ /**
+ * Returns true if button's model is armed and false otherwise. The
+ * button model is armed if menu item has focus or it is selected.
+ *
+ * @return $boolean$ true if button's model is armed and false otherwise
+ */
+ public boolean isArmed()
+ {
+ return getModel().isArmed();
+ }
+
+ /**
+ * Sets menuItem's "ARMED" property
+ *
+ * @param armed DOCUMENT ME!
+ */
+ public void setArmed(boolean armed)
+ {
+ getModel().setArmed(armed);
+ }
+
+ /**
+ * Enable or disable menu item. When menu item is disabled,
+ * its text and icon are grayed out if they exist.
+ *
+ * @param enabled if true enable menu item, and disable otherwise.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ super.setEnabled(enabled);
+ }
+
+ /**
+ * Return accelerator for this menu item.
+ *
+ * @return $KeyStroke$ accelerator for this menu item.
+ */
+ public KeyStroke getAccelerator()
+ {
+ return accelerator;
+ }
+
+ /**
+ * Sets the key combination which invokes the menu item's action
+ * listeners without navigating the menu hierarchy. Note that when the
+ * keyboard accelerator is typed, it will work whether or not the
+ * menu is currently displayed.
+ *
+ * @param keystroke accelerator for this menu item.
+ */
+ public void setAccelerator(KeyStroke keystroke)
+ {
+ KeyStroke old = this.accelerator;
+ this.accelerator = keystroke;
+ firePropertyChange ("accelerator", old, keystroke);
+ }
+
+ /**
+ * Configures menu items' properties from properties of the specified action.
+ * This method overrides configurePropertiesFromAction from AbstractButton
+ * to also set accelerator property.
+ *
+ * @param action action to configure properties from
+ */
+ protected void configurePropertiesFromAction(Action action)
+ {
+ super.configurePropertiesFromAction(action);
+
+ if (! (this instanceof JMenu) && action != null)
+ {
+ setAccelerator((KeyStroke) (action.getValue(Action.ACCELERATOR_KEY)));
+ if (accelerator != null)
+ super.registerKeyboardAction(action, accelerator,
+ JComponent.WHEN_IN_FOCUSED_WINDOW);
+ }
+ }
+
+ /**
+ * Creates PropertyChangeListener to listen for the changes in action
+ * properties.
+ *
+ * @param action action to listen to for property changes
+ *
+ * @return $PropertyChangeListener$ Listener that listens to changes in
+ * action properties.
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ Action act = (Action) (e.getSource());
+ configurePropertiesFromAction(act);
+ }
+ };
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager.
+ *
+ * @param ev event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processMouseEvent(MouseEvent ev, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ MenuDragMouseEvent e = new MenuDragMouseEvent(ev.getComponent(),
+ ev.getID(), ev.getWhen(),
+ ev.getModifiers(), ev.getX(),
+ ev.getY(),
+ ev.getClickCount(),
+ ev.isPopupTrigger(), path,
+ manager);
+ processMenuDragMouseEvent(e);
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ MenuKeyEvent e = new MenuKeyEvent(event.getComponent(), event.getID(),
+ event.getWhen(), event.getModifiers(),
+ event.getKeyCode(), event.getKeyChar(),
+ path, manager);
+ processMenuKeyEvent(e);
+
+ // Consume original key event, if the menu key event has been consumed.
+ if (e.isConsumed())
+ event.consume();
+ }
+
+ /**
+ * This method fires MenuDragMouseEvents to registered listeners.
+ * Different types of MenuDragMouseEvents are fired depending
+ * on the observed mouse event.
+ *
+ * @param event Mouse
+ */
+ public void processMenuDragMouseEvent(MenuDragMouseEvent event)
+ {
+ switch (event.getID())
+ {
+ case MouseEvent.MOUSE_ENTERED:
+ isDragging = false;
+ fireMenuDragMouseEntered(event);
+ break;
+ case MouseEvent.MOUSE_EXITED:
+ isDragging = false;
+ fireMenuDragMouseExited(event);
+ break;
+ case MouseEvent.MOUSE_DRAGGED:
+ isDragging = true;
+ fireMenuDragMouseDragged(event);
+ break;
+ case MouseEvent.MOUSE_RELEASED:
+ if (isDragging)
+ fireMenuDragMouseReleased(event);
+ break;
+ }
+ }
+
+ /**
+ * This method fires MenuKeyEvent to registered listeners.
+ * Different types of MenuKeyEvents are fired depending
+ * on the observed key event.
+ *
+ * @param event DOCUMENT ME!
+ */
+ public void processMenuKeyEvent(MenuKeyEvent event)
+ {
+ switch (event.getID())
+ {
+ case KeyEvent.KEY_PRESSED:
+ fireMenuKeyPressed(event);
+ break;
+ case KeyEvent.KEY_RELEASED:
+ fireMenuKeyReleased(event);
+ break;
+ case KeyEvent.KEY_TYPED:
+ fireMenuKeyTyped(event);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse entered menuItem while it was dragged
+ */
+ protected void fireMenuDragMouseEntered(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseEntered(event);
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse has exited menu item, while it was dragged
+ */
+ protected void fireMenuDragMouseExited(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseExited(event);
+ }
+
+ /**
+ * Fires MenuDragMouseEvent to all of the menuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse is being dragged over the menuItem
+ */
+ protected void fireMenuDragMouseDragged(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseDragged(event);
+ }
+
+ /**
+ * This method fires a MenuDragMouseEvent to all the MenuItem's MouseInputListeners.
+ *
+ * @param event The event signifying that mouse was released while it was dragged over the menuItem
+ */
+ protected void fireMenuDragMouseReleased(MenuDragMouseEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuDragMouseListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuDragMouseListener) ll[i]).menuDragMouseReleased(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was pressed
+ */
+ protected void fireMenuKeyPressed(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyPressed(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was released
+ */
+ protected void fireMenuKeyReleased(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyTyped(event);
+ }
+
+ /**
+ * This method fires a MenuKeyEvent to all the MenuItem's MenuKeyListeners.
+ *
+ * @param event The event signifying that key associated with this menu was typed.
+ * The key is typed when it was pressed and then released
+ */
+ protected void fireMenuKeyTyped(MenuKeyEvent event)
+ {
+ EventListener[] ll = listenerList.getListeners(MenuKeyListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((MenuKeyListener) ll[i]).menuKeyTyped(event);
+ }
+
+ /**
+ * Method of the MenuElement interface.
+ * This method is invoked by MenuSelectionManager when selection of
+ * this menu item has changed. If this menu item was selected then
+ * arm it's model, and disarm the model otherwise. The menu item
+ * is considered to be selected, and thus highlighted when its model
+ * is armed.
+ *
+ * @param changed indicates selection status of this menu item. If changed is
+ * true then menu item is selected and deselected otherwise.
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ Component parent = this.getParent();
+ if (changed)
+ {
+ model.setArmed(true);
+
+ if (parent != null && parent instanceof JPopupMenu)
+ ((JPopupMenu) parent).setSelected(this);
+ }
+ else
+ {
+ model.setArmed(false);
+
+ if (parent != null && parent instanceof JPopupMenu)
+ ((JPopupMenu) parent).getSelectionModel().clearSelection();
+ }
+ }
+
+ /**
+ * Method of the MenuElement interface.
+ *
+ * @return $MenuElement[]$ Returns array of sub-components for this menu
+ * item. By default menuItem doesn't have any subcomponents and so
+ * empty array is returned instead.
+ */
+ public MenuElement[] getSubElements()
+ {
+ return new MenuElement[0];
+ }
+
+ /**
+ * Returns reference to the component that will paint this menu item.
+ *
+ * @return $Component$ Component that will paint this menu item.
+ * Simply returns reference to this menu item.
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Adds a MenuDragMouseListener to this menu item. When mouse
+ * is dragged over the menu item the MenuDragMouseEvents will be
+ * fired, and these listeners will be called.
+ *
+ * @param listener The new listener to add
+ */
+ public void addMenuDragMouseListener(MenuDragMouseListener listener)
+ {
+ listenerList.add(MenuDragMouseListener.class, listener);
+ }
+
+ /**
+ * Removes a MenuDragMouseListener from the menuItem's listener list.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeMenuDragMouseListener(MenuDragMouseListener listener)
+ {
+ listenerList.remove(MenuDragMouseListener.class, listener);
+ }
+
+ /**
+ * Returns all added MenuDragMouseListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuDragMouseListener[] getMenuDragMouseListeners()
+ {
+ return (MenuDragMouseListener[]) listenerList.getListeners(MenuDragMouseListener.class);
+ }
+
+ /**
+ * Adds an MenuKeyListener to this menu item. This listener will be
+ * invoked when MenuKeyEvents will be fired by this menu item.
+ *
+ * @param listener The new listener to add
+ */
+ public void addMenuKeyListener(MenuKeyListener listener)
+ {
+ listenerList.add(MenuKeyListener.class, listener);
+ }
+
+ /**
+ * Removes an MenuKeyListener from the menuItem's listener list.
+ *
+ * @param listener The listener to remove
+ */
+ public void removeMenuKeyListener(MenuKeyListener listener)
+ {
+ listenerList.remove(MenuKeyListener.class, listener);
+ }
+
+ /**
+ * Returns all added MenuKeyListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public MenuKeyListener[] getMenuKeyListeners()
+ {
+ return (MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class);
+ }
+
+ /**
+ * Returns a string describing the attributes for the JMenuItem
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JMenuItem.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JMenuItem component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJMenuItem ctx = new AccessibleJMenuItem();
+ addChangeListener(ctx);
+ accessibleContext = ctx;
+ }
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the JMenuItem
+ * component.
+ *
+ * @see JMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJMenuItem extends AccessibleAbstractButton
+ implements ChangeListener
+ {
+ private static final long serialVersionUID = 6748924232082076534L;
+
+ private boolean armed;
+ private boolean focusOwner;
+ private boolean pressed;
+ private boolean selected;
+
+ /**
+ * Creates a new AccessibleJMenuItem instance.
+ */
+ AccessibleJMenuItem()
+ {
+ //super(component);
+ }
+
+ /**
+ * Receives notification when the menu item's state changes and fires
+ * appropriate property change events to registered listeners.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ // This is fired in all cases.
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ Boolean.FALSE, Boolean.TRUE);
+
+ ButtonModel model = getModel();
+
+ // Handle the armed property.
+ if (model.isArmed())
+ {
+ if (! armed)
+ {
+ armed = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.ARMED, null);
+ }
+ }
+ else
+ {
+ if (armed)
+ {
+ armed = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.ARMED);
+ }
+ }
+
+ // Handle the pressed property.
+ if (model.isPressed())
+ {
+ if (! pressed)
+ {
+ pressed = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.PRESSED, null);
+ }
+ }
+ else
+ {
+ if (pressed)
+ {
+ pressed = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.PRESSED);
+ }
+ }
+
+ // Handle the selected property.
+ if (model.isSelected())
+ {
+ if (! selected)
+ {
+ selected = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.SELECTED, null);
+ }
+ }
+ else
+ {
+ if (selected)
+ {
+ selected = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.SELECTED);
+ }
+ }
+
+ // Handle the focusOwner property.
+ if (isFocusOwner())
+ {
+ if (! focusOwner)
+ {
+ focusOwner = true;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ AccessibleState.FOCUSED, null);
+ }
+ }
+ else
+ {
+ if (focusOwner)
+ {
+ focusOwner = false;
+ firePropertyChange(AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
+ null, AccessibleState.FOCUSED);
+ }
+ }
+ }
+
+ /**
+ * Returns the accessible role for the JMenuItem component.
+ *
+ * @return {@link AccessibleRole#MENU_ITEM}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.MENU_ITEM;
+ }
+ }
+
+ /**
+ * Returns true if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return true if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this)
+ == null;
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JOptionPane.java b/libjava/classpath/javax/swing/JOptionPane.java
new file mode 100644
index 000000000..8c765ed74
--- /dev/null
+++ b/libjava/classpath/javax/swing/JOptionPane.java
@@ -0,0 +1,1637 @@
+/* JOptionPane.java
+ Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+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.
+ */
+public class JOptionPane extends JComponent implements Accessible
+{
+ /**
+ * Provides the accessibility features for the JOptionPane
+ * component.
+ */
+ protected class AccessibleJOptionPane extends JComponent.AccessibleJComponent
+ {
+ private static final long serialVersionUID = 686071432213084821L;
+
+ /**
+ * Creates a new AccessibleJOptionPane instance.
+ */
+ protected AccessibleJOptionPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role of this object, which is always
+ * {@link AccessibleRole#OPTION_PANE}.
+ *
+ * @return the accessible role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.OPTION_PANE;
+ }
+ }
+
+ private static final long serialVersionUID = 5231143276678566796L;
+
+ /** The value returned when cancel option is selected. */
+ public static final int CANCEL_OPTION = 2;
+
+ /** The value returned when the dialog is closed without a selection. */
+ public static final int CLOSED_OPTION = -1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int DEFAULT_OPTION = -1;
+
+ /** The value returned when the no option is selected. */
+ public static final int NO_OPTION = 1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int OK_CANCEL_OPTION = 2;
+
+ /** The value returned when the ok option is selected. */
+ public static final int OK_OPTION = 0;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int YES_NO_CANCEL_OPTION = 1;
+
+ /** An option used in confirmation dialog methods. */
+ public static final int YES_NO_OPTION = 0;
+
+ /** The value returned when the yes option is selected. */
+ public static final int YES_OPTION = 0;
+
+ /** Identifier for the error message type. */
+ public static final int ERROR_MESSAGE = 0;
+
+ /** Identifier for the information message type. */
+ public static final int INFORMATION_MESSAGE = 1;
+
+ /** Identifier for the plain message type. */
+ public static final int PLAIN_MESSAGE = -1;
+
+ /** Identifier for the question message type. */
+ public static final int QUESTION_MESSAGE = 3;
+
+ /** Identifier for the warning message type. */
+ public static final int WARNING_MESSAGE = 2;
+
+ /**
+ * The identifier for the propertyChangeEvent when the icon property
+ * changes.
+ */
+ public static final String ICON_PROPERTY = "icon";
+
+ /**
+ * The identifier for the propertyChangeEvent when the initialSelectionValue
+ * property changes.
+ */
+ public static final String INITIAL_SELECTION_VALUE_PROPERTY = "initialSelectionValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the initialValue property
+ * changes.
+ */
+ public static final String INITIAL_VALUE_PROPERTY = "initialValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the inputValue property
+ * changes.
+ */
+ public static final String INPUT_VALUE_PROPERTY = "inputValue";
+
+ /**
+ * The identifier for the propertyChangeEvent when the message property
+ * changes.
+ */
+ public static final String MESSAGE_PROPERTY = "message";
+
+ /**
+ * The identifier for the propertyChangeEvent when the messageType property
+ * changes.
+ */
+ public static final String MESSAGE_TYPE_PROPERTY = "messageType";
+
+ /**
+ * The identifier for the propertyChangeEvent when the optionType property
+ * changes.
+ */
+ public static final String OPTION_TYPE_PROPERTY = "optionType";
+
+ /**
+ * The identifier for the propertyChangeEvent when the options property
+ * changes.
+ */
+ public static final String OPTIONS_PROPERTY = "options";
+
+ /**
+ * The identifier for the propertyChangeEvent when the selectionValues
+ * property changes.
+ */
+ public static final String SELECTION_VALUES_PROPERTY = "selectionValues";
+
+ /**
+ * The identifier for the propertyChangeEvent when the value property
+ * changes.
+ */
+ public static final String VALUE_PROPERTY = "value";
+
+ /**
+ * The identifier for the propertyChangeEvent when the wantsInput property
+ * changes.
+ */
+ public static final String WANTS_INPUT_PROPERTY = "wantsInput";
+
+ /** The value returned when the inputValue is uninitialized. */
+ public static final Object UNINITIALIZED_VALUE = "uninitializedValue";
+
+ /** The icon displayed in the dialog/internal frame. */
+ protected Icon icon;
+
+ /** The initial selected value in the input component. */
+ protected Object initialSelectionValue;
+
+ /** The object that is initially selected for options. */
+ protected Object initialValue;
+
+ /** The value the user inputs. */
+ protected Object inputValue = UNINITIALIZED_VALUE;
+
+ /** The message displayed in the dialog/internal frame. */
+ protected Object message;
+
+ /** The type of message displayed. */
+ protected int messageType = PLAIN_MESSAGE;
+
+ /**
+ * The options (usually buttons) aligned at the bottom for the user to
+ * select.
+ */
+ protected Object[] options;
+
+ /** The type of options to display. */
+ protected int optionType = DEFAULT_OPTION;
+
+ /** The input values the user can select. */
+ protected Object[] selectionValues;
+
+ /** The value returned by selecting an option. */
+ protected Object value = UNINITIALIZED_VALUE;
+
+ /** Whether the Dialog/InternalFrame needs input. */
+ protected boolean wantsInput;
+
+ /** The common frame used when no parent is provided. */
+ private static Frame privFrame = (Frame) SwingUtilities.getOwnerFrame(null);
+
+ /**
+ * Creates a new JOptionPane object using a message of "JOptionPane
+ * message", using the PLAIN_MESSAGE type and DEFAULT_OPTION.
+ */
+ public JOptionPane()
+ {
+ this("JOptionPane message", PLAIN_MESSAGE, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message using the
+ * PLAIN_MESSAGE type and DEFAULT_OPTION.
+ *
+ * @param message The message to display.
+ */
+ public JOptionPane(Object message)
+ {
+ this(message, PLAIN_MESSAGE, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message and messageType
+ * and DEFAULT_OPTION.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ */
+ public JOptionPane(Object message, int messageType)
+ {
+ this(message, messageType, DEFAULT_OPTION, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType and
+ * optionType.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ */
+ public JOptionPane(Object message, int messageType, int optionType)
+ {
+ this(message, messageType, optionType, null, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType and icon.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ */
+ public JOptionPane(Object message, int messageType, int optionType, Icon icon)
+ {
+ this(message, messageType, optionType, icon, null, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType, icon and options.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ * @param options The options given.
+ */
+ public JOptionPane(Object message, int messageType, int optionType,
+ Icon icon, Object[] options)
+ {
+ this(message, messageType, optionType, icon, options, null);
+ }
+
+ /**
+ * Creates a new JOptionPane object using the given message, messageType,
+ * optionType, icon, options and initialValue. The initialValue will be
+ * focused initially.
+ *
+ * @param message The message to display.
+ * @param messageType The type of message.
+ * @param optionType The type of options.
+ * @param icon The icon to display.
+ * @param options The options given.
+ * @param initialValue The component to focus on initially.
+ *
+ * @throws IllegalArgumentException If the messageType or optionType are not
+ * legal values.
+ */
+ public JOptionPane(Object message, int messageType, int optionType,
+ Icon icon, Object[] options, Object initialValue)
+ {
+ this.message = message;
+ if (! validMessageType(messageType))
+ throw new IllegalArgumentException("Message Type not legal value.");
+ this.messageType = messageType;
+ if (! validOptionType(optionType))
+ throw new IllegalArgumentException("Option Type not legal value.");
+ this.optionType = optionType;
+ this.icon = icon;
+ this.options = options;
+ this.initialValue = initialValue;
+
+ setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
+
+ updateUI();
+ }
+
+ /**
+ * This method creates a new JDialog that is either centered around the
+ * parent's frame or centered on the screen (if the parent is null). The
+ * JDialog will not be resizable and will be modal. Once the JDialog is
+ * disposed, the inputValue and value properties will be set by the
+ * optionPane.
+ *
+ * @param parentComponent The parent of the Dialog.
+ * @param title The title in the bar of the JDialog.
+ *
+ * @return A new JDialog based on the JOptionPane configuration.
+ */
+ public JDialog createDialog(Component parentComponent, String title)
+ {
+ Frame toUse = getFrameForComponent(parentComponent);
+ 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.
+ *
+ * @specnote The specification says that the internal frame is placed
+ * in the nearest JDesktopPane that is found in
+ * parent's ancestors. The behaviour of the JDK
+ * is that it actually looks up the nearest
+ * JLayeredPane in parent's ancestors.
+ * So do we.
+ */
+ public JInternalFrame createInternalFrame(Component parentComponent,
+ String title)
+ throws RuntimeException
+ {
+ // Try to find a JDesktopPane.
+ JLayeredPane toUse = getDesktopPaneForComponent(parentComponent);
+ // If we don't have a JDesktopPane, we try to find a JLayeredPane.
+ if (toUse == null)
+ toUse = JLayeredPane.getLayeredPaneAbove(parentComponent);
+ // If this still fails, we throw a RuntimeException.
+ if (toUse == null)
+ throw new RuntimeException
+ ("parentComponent does not have a valid parent");
+
+ JInternalFrame frame = new JInternalFrame(title);
+
+ inputValue = UNINITIALIZED_VALUE;
+ value = UNINITIALIZED_VALUE;
+
+ frame.setContentPane(this);
+ frame.setClosable(true);
+
+ toUse.add(frame);
+ frame.setLayer(JLayeredPane.MODAL_LAYER);
+
+ frame.pack();
+ frame.setVisible(true);
+
+ return frame;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JOptionPane component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJOptionPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJOptionPane();
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the JDesktopPane for the given parentComponent or
+ * null if none can be found.
+ *
+ * @param parentComponent The component to look in.
+ *
+ * @return The JDesktopPane for the given component or null if none can be
+ * found.
+ */
+ public static JDesktopPane getDesktopPaneForComponent(Component parentComponent)
+ {
+ return (JDesktopPane) SwingUtilities.getAncestorOfClass(JDesktopPane.class,
+ parentComponent);
+ }
+
+ /**
+ * This method returns the Frame for the given parentComponent or null if
+ * none can be found.
+ *
+ * @param parentComponent The component to look in.
+ *
+ * @return The Frame for the given component or null if none can be found.
+ */
+ public static Frame getFrameForComponent(Component parentComponent)
+ {
+ return (Frame) SwingUtilities.getAncestorOfClass(Frame.class,
+ parentComponent);
+ }
+
+ /**
+ * This method returns the icon displayed.
+ *
+ * @return The icon displayed.
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * This method returns the value initially selected from the list of values
+ * the user can input.
+ *
+ * @return The initial selection value.
+ */
+ public Object getInitialSelectionValue()
+ {
+ return initialSelectionValue;
+ }
+
+ /**
+ * This method returns the value that is focused from the list of options.
+ *
+ * @return The initial value from options.
+ */
+ public Object getInitialValue()
+ {
+ return initialValue;
+ }
+
+ /**
+ * This method returns the value that the user input.
+ *
+ * @return The user's input value.
+ */
+ public Object getInputValue()
+ {
+ if (getValue().equals(new Integer(CANCEL_OPTION)))
+ setInputValue(null);
+ return inputValue;
+ }
+
+ /**
+ * This method returns the maximum characters per line. By default, this is
+ * Integer.MAX_VALUE.
+ *
+ * @return The maximum characters per line.
+ */
+ public int getMaxCharactersPerLineCount()
+ {
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * This method returns the message displayed.
+ *
+ * @return The message displayed.
+ */
+ public Object getMessage()
+ {
+ return message;
+ }
+
+ /**
+ * This method returns the message type.
+ *
+ * @return The message type.
+ */
+ public int getMessageType()
+ {
+ return messageType;
+ }
+
+ /**
+ * This method returns the options.
+ *
+ * @return The options.
+ */
+ public Object[] getOptions()
+ {
+ return options;
+ }
+
+ /**
+ * This method returns the option type.
+ *
+ * @return The option type.
+ */
+ public int getOptionType()
+ {
+ return optionType;
+ }
+
+ /**
+ * This method returns the Frame used by JOptionPane dialog's that have no
+ * parent.
+ *
+ * @return The Frame used by dialogs that have no parent.
+ */
+ public static Frame getRootFrame()
+ {
+ return privFrame;
+ }
+
+ /**
+ * This method returns the selection values.
+ *
+ * @return The selection values.
+ */
+ public Object[] getSelectionValues()
+ {
+ return selectionValues;
+ }
+
+ /**
+ * This method returns the UI used by the JOptionPane.
+ *
+ * @return The UI used by the JOptionPane.
+ */
+ public OptionPaneUI getUI()
+ {
+ return (OptionPaneUI) ui;
+ }
+
+ /**
+ * This method returns an identifier to determine which UI class will act as
+ * the UI.
+ *
+ * @return The UI identifier.
+ */
+ public String getUIClassID()
+ {
+ return "OptionPaneUI";
+ }
+
+ /**
+ * This method returns the value that the user selected out of options.
+ *
+ * @return The value that the user selected out of options.
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * This method returns whether this JOptionPane wants input.
+ *
+ * @return Whether this JOptionPane wants input.
+ */
+ public boolean getWantsInput()
+ {
+ return wantsInput;
+ }
+
+ /**
+ * This method returns a String that describes this JOptionPane.
+ *
+ * @return A String that describes this JOptionPane.
+ */
+ protected String paramString()
+ {
+ return "JOptionPane";
+ }
+
+ /**
+ * This method requests focus for the initial value.
+ */
+ public void selectInitialValue()
+ {
+ if (ui != null)
+ ((OptionPaneUI) ui).selectInitialValue(this);
+ }
+
+ /**
+ * This method changes the icon property.
+ *
+ * @param newIcon The new icon to use.
+ */
+ public void setIcon(Icon newIcon)
+ {
+ if (icon != newIcon)
+ {
+ Icon old = icon;
+ icon = newIcon;
+ firePropertyChange(ICON_PROPERTY, old, icon);
+ }
+ }
+
+ /**
+ * This method changes the initial selection property.
+ *
+ * @param newValue The new initial selection.
+ */
+ public void setInitialSelectionValue(Object newValue)
+ {
+ if (initialSelectionValue != newValue)
+ {
+ Object old = initialSelectionValue;
+ initialSelectionValue = newValue;
+ firePropertyChange(INITIAL_SELECTION_VALUE_PROPERTY, old,
+ initialSelectionValue);
+ }
+ }
+
+ /**
+ * This method changes the initial value property.
+ *
+ * @param newValue The new initial value.
+ */
+ public void setInitialValue(Object newValue)
+ {
+ if (initialValue != newValue)
+ {
+ Object old = initialValue;
+ initialValue = newValue;
+ firePropertyChange(INITIAL_VALUE_PROPERTY, old, initialValue);
+ }
+ }
+
+ /**
+ * This method changes the inputValue property.
+ *
+ * @param newValue The new inputValue.
+ */
+ public void setInputValue(Object newValue)
+ {
+ if (inputValue != newValue)
+ {
+ Object old = inputValue;
+ inputValue = newValue;
+ firePropertyChange(INPUT_VALUE_PROPERTY, old, inputValue);
+ }
+ }
+
+ /**
+ * This method changes the message property.
+ *
+ * @param newMessage The new message.
+ */
+ public void setMessage(Object newMessage)
+ {
+ if (message != newMessage)
+ {
+ Object old = message;
+ message = newMessage;
+ firePropertyChange(MESSAGE_PROPERTY, old, message);
+ }
+ }
+
+ /**
+ * This method changes the messageType property.
+ *
+ * @param newType The new messageType.
+ *
+ * @throws IllegalArgumentException If the messageType is not valid.
+ */
+ public void setMessageType(int newType)
+ {
+ if (! validMessageType(newType))
+ throw new IllegalArgumentException("Message Type not legal value.");
+ if (newType != messageType)
+ {
+ int old = messageType;
+ messageType = newType;
+ firePropertyChange(MESSAGE_TYPE_PROPERTY, old, messageType);
+ }
+ }
+
+ /**
+ * This method changes the options property.
+ *
+ * @param newOptions The new options.
+ */
+ public void setOptions(Object[] newOptions)
+ {
+ if (options != newOptions)
+ {
+ Object[] old = options;
+ options = newOptions;
+ firePropertyChange(OPTIONS_PROPERTY, old, options);
+ }
+ }
+
+ /**
+ * This method changes the optionType property.
+ *
+ * @param newType The new optionType.
+ *
+ * @throws IllegalArgumentException If the optionType is not valid.
+ */
+ public void setOptionType(int newType)
+ {
+ if (! validOptionType(newType))
+ throw new IllegalArgumentException("Option Type not legal value.");
+ if (newType != optionType)
+ {
+ int old = optionType;
+ optionType = newType;
+ firePropertyChange(OPTION_TYPE_PROPERTY, old, optionType);
+ }
+ }
+
+ /**
+ * This method changes the Frame used for JOptionPane dialogs that have no
+ * parent.
+ *
+ * @param newRootFrame The Frame to use for dialogs that have no parent.
+ */
+ public static void setRootFrame(Frame newRootFrame)
+ {
+ privFrame = newRootFrame;
+ }
+
+ /**
+ * This method changes the selectionValues property.
+ *
+ * @param newValues The new selectionValues.
+ */
+ public void setSelectionValues(Object[] newValues)
+ {
+ if (newValues != selectionValues)
+ {
+ if (newValues != null)
+ wantsInput = true;
+ Object[] old = selectionValues;
+ selectionValues = newValues;
+ firePropertyChange(SELECTION_VALUES_PROPERTY, old, selectionValues);
+ }
+ }
+
+ /**
+ * This method sets the UI used with the JOptionPane.
+ *
+ * @param ui The UI used with the JOptionPane.
+ */
+ public void setUI(OptionPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets the value has been selected out of options.
+ *
+ * @param newValue The value that has been selected out of options.
+ */
+ public void setValue(Object newValue)
+ {
+ if (value != newValue)
+ {
+ Object old = value;
+ value = newValue;
+ firePropertyChange(VALUE_PROPERTY, old, value);
+ }
+ }
+
+ /**
+ * This method changes the wantsInput property.
+ *
+ * @param newValue Whether this JOptionPane requires input.
+ */
+ public void setWantsInput(boolean newValue)
+ {
+ if (wantsInput != newValue)
+ {
+ boolean old = wantsInput;
+ wantsInput = newValue;
+ firePropertyChange(WANTS_INPUT_PROPERTY, old, wantsInput);
+ }
+ }
+
+ /**
+ * This method shows a confirmation dialog with the title "Select an Option"
+ * and displays the given message. The parent frame will be the same as the
+ * parent frame of the given parentComponent. This method returns the
+ * option chosen by the user.
+ *
+ * @param parentComponent The parentComponent to find a frame in.
+ * @param message The message to display.
+ *
+ * @return The option that was selected.
+ */
+ public static int showConfirmDialog(Component parentComponent, Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ JDialog dialog = pane.createDialog(parentComponent, "Select an Option");
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message,
+ * optionType and title. The frame that owns the dialog will be the same
+ * frame that holds the given parentComponent. This method returns the
+ * option that was chosen.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ *
+ * @return The option that was chosen.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType)
+ {
+ JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message, title,
+ * messageType and optionType. The frame owner will be the same frame as
+ * the one that holds the given parentComponent. This method returns the
+ * option selected by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messageType.
+ *
+ * @return The selected option.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows a confirmation dialog with the given message, title,
+ * optionType, messageType and icon. The frame owner will be the same as
+ * the one that holds the given parentComponent. This method returns the
+ * option selected by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messsageType.
+ * @param icon The icon displayed.
+ *
+ * @return The selected option.
+ */
+ public static int showConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method will show a QUESTION_MESSAGE input dialog with the given
+ * message. No selectionValues is set so the Look and Feel will usually
+ * give the user a TextField to fill out. The frame owner will be the same
+ * frame that holds the given parentComponent. This method will return the
+ * value entered by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ *
+ * @return The value entered by the user.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method will show a QUESTION_MESSAGE type input dialog with the given
+ * message and initialSelectionValue. Since there is no selectionValues
+ * set, the Look and Feel will usually give a TextField to fill out. The
+ * frame owner will be the same as the one that holds the given
+ * parentComponent. This method will return the value entered by the user.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message to display.
+ * @param initialSelectionValue The initially selected value.
+ *
+ * @return The value the user input.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method displays a new input dialog with the given message, title and
+ * messageType. Since no selectionValues value is given, the Look and Feel
+ * will usually give the user a TextField to input data to. This method
+ * returns the value the user inputs.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message to display.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ *
+ * @return The value the user input.
+ */
+ public static String showInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an input dialog with the given message, title,
+ * messageType, icon, selectionValues, and initialSelectionValue. This
+ * method returns the value that the user selects.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ * @param selectionValues The list of values to select from.
+ * @param initialSelectionValue The initially selected value.
+ *
+ * @return The user selected value.
+ */
+ public static Object showInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon,
+ Object[] selectionValues,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ pane.setIcon(icon);
+ pane.setSelectionValues(selectionValues);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ return pane.getInputValue();
+ }
+
+ /**
+ * This method shows a QUESTION_MESSAGE type input dialog. Since no
+ * selectionValues is set, the Look and Feel will usually give the user a
+ * TextField to input data to. This method returns the value the user
+ * inputs.
+ *
+ * @param message The message to display.
+ *
+ * @return The user selected value.
+ */
+ public static String showInputDialog(Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ JDialog dialog = pane.createDialog(null, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows a QUESTION_MESSAGE type input dialog. Since no
+ * selectionValues is set, the Look and Feel will usually give the user a
+ * TextField to input data to. The input component will be initialized with
+ * the initialSelectionValue. This method returns the value the user
+ * inputs.
+ *
+ * @param message The message to display.
+ * @param initialSelectionValue The initialSelectionValue.
+ *
+ * @return The user selected value.
+ */
+ public static String showInputDialog(Object message,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, QUESTION_MESSAGE);
+ pane.setWantsInput(true);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JDialog dialog = pane.createDialog(null, null);
+ dialog.show();
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message.
+ * The internal frame dialog will be placed in the first JDesktopPane
+ * ancestor of the given parentComponent. This method will return the value
+ * selected.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ *
+ * @return The value selected.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * optionType and title. The internal frame dialog will be placed in the
+ * first JDesktopPane ancestor of the given parentComponent. This method
+ * will return the selected value.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param optionType The option type.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType)
+ {
+ JOptionPane pane = new JOptionPane(message, PLAIN_MESSAGE, optionType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * title, optionTypes and icon for the given message type. The internal
+ * confirmation dialog will be placed in the first instance of
+ * JDesktopPane ancestor of the given parentComponent.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title of the dialog.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal confirmation dialog with the given message,
+ * title, option type, message type, and icon. The internal frame dialog
+ * will be placed in the first JDesktopPane ancestor that is found in the
+ * given parentComponent. This method returns the selected value.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ *
+ * @return The selected value.
+ */
+ public static int showInternalConfirmDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message. The
+ * internal frame dialog will be placed in the first JDesktopPane ancestor
+ * of the given parent component. This method returns the value input by
+ * the user.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ *
+ * @return The user selected value.
+ */
+ public static String showInternalInputDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ pane.setWantsInput(true);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message, title
+ * and message type. The internal input dialog will be placed in the first
+ * JDesktopPane ancestor found in the given parent component. This method
+ * will return the input value given by the user.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ *
+ * @return The user input value.
+ */
+ public static String showInternalInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ return (String) pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal input dialog with the given message, title
+ * message type, icon, selection value list and initial selection value.
+ * The internal frame dialog will be placed in the first JDesktopPane
+ * ancestor found in the given parent component. This method returns the
+ * input value from the user.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ * @param selectionValues The selection value list.
+ * @param initialSelectionValue The initial selection value.
+ *
+ * @return The user input value.
+ */
+ public static Object showInternalInputDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon,
+ Object[] selectionValues,
+ Object initialSelectionValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setWantsInput(true);
+ pane.setIcon(icon);
+ pane.setSelectionValues(selectionValues);
+ pane.setInitialSelectionValue(initialSelectionValue);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ return pane.getInputValue();
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message. The
+ * internal frame dialog will be placed in the first JDesktopPane ancestor
+ * found in the given parent component.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, null);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message,
+ * title and message type. The internal message dialog is placed in the
+ * first JDesktopPane ancestor found in the given parent component.
+ *
+ * @param parentComponent The parent component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method shows an internal message dialog with the given message,
+ * title, message type and icon. The internal message dialog is placed in
+ * the first JDesktopPane ancestor found in the given parent component.
+ *
+ * @param parentComponent The component to find a JDesktopPane in.
+ * @param message The message to display.
+ * @param title The title to display.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ */
+ public static void showInternalMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setIcon(icon);
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+ }
+
+ /**
+ * This method displays an internal option dialog with the given message,
+ * title, option type, message type, icon, option list, and initial option
+ * value. The internal option dialog is placed in the first JDesktopPane
+ * ancestor found in the parent component. This method returns the option
+ * selected.
+ *
+ * @param parentComponent The parent to find a JDesktopPane in.
+ * @param message The message displayed.
+ * @param title The title displayed.
+ * @param optionType The option type.
+ * @param messageType The message type.
+ * @param icon The icon to display.
+ * @param options The array of options.
+ * @param initialValue The initial value selected.
+ *
+ * @return The option that was selected.
+ */
+ public static int showInternalOptionDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon, Object[] options,
+ Object initialValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon,
+ options, initialValue);
+
+ JInternalFrame frame = pane.createInternalFrame(parentComponent, title);
+
+ startModal(frame);
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method shows an INFORMATION_MESSAGE type message dialog.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message)
+ {
+ JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE);
+ JDialog dialog = pane.createDialog(parentComponent, null);
+ dialog.show();
+ }
+
+ /**
+ * This method shows a message dialog with the given message, title and
+ * messageType.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+ }
+
+ /**
+ * This method shows a message dialog with the given message, title,
+ * messageType and icon.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ */
+ public static void showMessageDialog(Component parentComponent,
+ Object message, String title,
+ int messageType, Icon icon)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType);
+ pane.setIcon(icon);
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+ }
+
+ /**
+ * This method shows an option dialog with the given message, title,
+ * optionType, messageType, icon, options and initialValue. This method
+ * returns the option that was selected.
+ *
+ * @param parentComponent The component to find a frame in.
+ * @param message The message displayed.
+ * @param title The title of the dialog.
+ * @param optionType The optionType.
+ * @param messageType The messageType.
+ * @param icon The icon displayed.
+ * @param options The options to choose from.
+ * @param initialValue The initial value.
+ *
+ * @return The selected option.
+ */
+ public static int showOptionDialog(Component parentComponent,
+ Object message, String title,
+ int optionType, int messageType,
+ Icon icon, Object[] options,
+ Object initialValue)
+ {
+ JOptionPane pane = new JOptionPane(message, messageType, optionType, icon,
+ options, initialValue);
+
+ JDialog dialog = pane.createDialog(parentComponent, title);
+ dialog.show();
+
+ if (pane.getValue() instanceof Integer)
+ return ((Integer) pane.getValue()).intValue();
+ return -1;
+ }
+
+ /**
+ * This method resets the UI to the Look and Feel default.
+ */
+ public void updateUI()
+ {
+ setUI((OptionPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns true if the key is a valid messageType.
+ *
+ * @param key The key to check.
+ *
+ * @return True if key is valid.
+ */
+ private boolean validMessageType(int key)
+ {
+ switch (key)
+ {
+ case ERROR_MESSAGE:
+ case INFORMATION_MESSAGE:
+ case PLAIN_MESSAGE:
+ case QUESTION_MESSAGE:
+ case WARNING_MESSAGE:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method returns true if the key is a valid optionType.
+ *
+ * @param key The key to check.
+ *
+ * @return True if key is valid.
+ */
+ private boolean validOptionType(int key)
+ {
+ switch (key)
+ {
+ case DEFAULT_OPTION:
+ case OK_CANCEL_OPTION:
+ case YES_NO_CANCEL_OPTION:
+ case YES_NO_OPTION:
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This helper method makes the JInternalFrame wait until it is notified by
+ * an InternalFrameClosing event. This method also adds the given
+ * JOptionPane to the JInternalFrame and sizes it according to the
+ * JInternalFrame's preferred size.
+ *
+ * @param f The JInternalFrame to make modal.
+ */
+ private static void startModal(JInternalFrame f)
+ {
+ // We need to add an additional glasspane-like component directly
+ // below the frame, which intercepts all mouse events that are not
+ // directed at the frame itself.
+ JPanel modalInterceptor = new JPanel();
+ modalInterceptor.setOpaque(false);
+ JLayeredPane lp = JLayeredPane.getLayeredPaneAbove(f);
+ lp.setLayer(modalInterceptor, JLayeredPane.MODAL_LAYER.intValue());
+ modalInterceptor.setBounds(0, 0, lp.getWidth(), lp.getHeight());
+ modalInterceptor.addMouseListener(new MouseAdapter(){});
+ modalInterceptor.addMouseMotionListener(new MouseMotionAdapter(){});
+ lp.add(modalInterceptor);
+ f.toFront();
+
+ // We need to explicitly dispatch events when we are blocking the event
+ // dispatch thread.
+ EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue();
+ try
+ {
+ while (! f.isClosed())
+ {
+ if (EventQueue.isDispatchThread())
+ {
+ // The getNextEventMethod() issues wait() when no
+ // event is available, so we don't need do explicitly wait().
+ AWTEvent ev = queue.getNextEvent();
+ // This mimics EventQueue.dispatchEvent(). We can't use
+ // EventQueue.dispatchEvent() directly, because it is
+ // protected, unfortunately.
+ if (ev instanceof ActiveEvent)
+ ((ActiveEvent) ev).dispatch();
+ else if (ev.getSource() instanceof Component)
+ ((Component) ev.getSource()).dispatchEvent(ev);
+ else if (ev.getSource() instanceof MenuComponent)
+ ((MenuComponent) ev.getSource()).dispatchEvent(ev);
+ // Other events are ignored as per spec in
+ // EventQueue.dispatchEvent
+ }
+ else
+ {
+ // Give other threads a chance to become active.
+ Thread.yield();
+ }
+ }
+ }
+ catch (InterruptedException ex)
+ {
+ // If we get interrupted, then leave the modal state.
+ }
+ finally
+ {
+ // Clean up the modal interceptor.
+ lp.remove(modalInterceptor);
+
+ // Remove the internal frame from its parent, so it is no longer
+ // lurking around and clogging memory.
+ Container parent = f.getParent();
+ if (parent != null)
+ parent.remove(f);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPanel.java b/libjava/classpath/javax/swing/JPanel.java
new file mode 100644
index 000000000..f2ff95690
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPanel.java
@@ -0,0 +1,203 @@
+/* JPanel.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.FlowLayout;
+import java.awt.LayoutManager;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.PanelUI;
+
+/**
+ * An instance of JPanel can be added to a panel, frame etc
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JPanel extends JComponent implements Accessible
+{
+ /**
+ * Provides accessibility support for JPanel.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+ protected class AccessibleJPanel extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of AccessibleJPanel.
+ */
+ protected AccessibleJPanel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for JPanel, which is
+ * {@link AccessibleRole#PANEL}.
+ *
+ * @return the accessible role for JPanel
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PANEL;
+ }
+ }
+
+ /**
+ * Creates a new panel with a new instance of {@link FlowLayout} as the
+ * layout manager and double-buffering enabled.
+ */
+ public JPanel()
+ {
+ this(new FlowLayout(), true);
+ }
+
+ /**
+ * Creates a new panel with double-buffering enabled or disabled as
+ * specified. The default layout manager is an instance of
+ * {@link FlowLayout}.
+ *
+ * @param isDoubleBuffered a flag that controls whether or not
+ * double-buffering is enabled.
+ */
+ public JPanel(boolean isDoubleBuffered)
+ {
+ this(new FlowLayout(), isDoubleBuffered);
+ }
+
+ /**
+ * Creates a new panel with the specified layout manager. Double-buffering
+ * is enabled by default.
+ *
+ * @param layout the layout manager (null permitted).
+ */
+ public JPanel(LayoutManager layout)
+ {
+ this(layout, true);
+ }
+
+ /**
+ * Creates a new panel with the specified layout manager and
+ * double-buffering.
+ *
+ * @param layout the layout manager (null permitted).
+ * @param isDoubleBuffered a flag that controls whether or not
+ * double-buffering is enabled.
+ */
+ public JPanel(LayoutManager layout, boolean isDoubleBuffered)
+ {
+ setLayout(layout);
+ setOpaque(true);
+ setDoubleBuffered(isDoubleBuffered);
+ updateUI();
+ }
+
+ /**
+ * Returns the suffix ("PanelUI" in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a JPanel.
+ *
+ * @return "PanelUI".
+ */
+ public String getUIClassID()
+ {
+ return "PanelUI";
+ }
+
+ /**
+ * Sets the UI delegate for the JPanel component.
+ *
+ * @param ui the UI delegate.
+ *
+ * @since 1.4
+ * @see #getUI()
+ */
+ public void setUI(PanelUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Returns the UI delegate for the JPanel component.
+ *
+ * @return The UI delegate.
+ *
+ * @since 1.4
+ * @see #setUI(PanelUI)
+ */
+ public PanelUI getUI()
+ {
+ return (PanelUI) ui;
+ }
+
+ /**
+ * Sets this panel's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((PanelUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JPanel component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJPanel}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPanel();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JPanel.
+ *
+ * @return A string describing the attributes of this JPanel
+ * (never null).
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPasswordField.java b/libjava/classpath/javax/swing/JPasswordField.java
new file mode 100644
index 000000000..e73993f62
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPasswordField.java
@@ -0,0 +1,297 @@
+/* JPasswordField.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+
+/**
+ * class JPasswordField
+ *
+ * @author Andrew Selkirk
+ * @author Lillian Angel
+ * @version 1.0
+ */
+public class JPasswordField extends JTextField
+{
+ /**
+ * AccessibleJPasswordField
+ */
+ protected class AccessibleJPasswordField extends AccessibleJTextField
+ {
+ private static final long serialVersionUID = -8477039424200681086L;
+
+ /**
+ * Constructor AccessibleJPasswordField
+ */
+ protected AccessibleJPasswordField()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * getAccessibleRole
+ *
+ * @return AccessibleRole
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PASSWORD_TEXT;
+ }
+ }
+
+ /**
+ * echoChar. Default is 0.
+ */
+ private char echoChar = 0;
+
+ /**
+ * Creates a JPasswordField object.
+ */
+ public JPasswordField()
+ {
+ this(null, null, 0);
+ }
+
+ /**
+ * Creates a JPasswordField object.
+ *
+ * @param text the initial text
+ */
+ public JPasswordField(String text)
+ {
+ this(null, text, 0);
+ }
+
+ /**
+ * Creates a JPasswordField object.
+ *
+ * @param columns the number of columns
+ */
+ public JPasswordField(int columns)
+ {
+ this(null, null, columns);
+ }
+
+ /**
+ * Creates a JPasswordField object.
+ *
+ * @param text the initial text
+ * @param columns the number of columns
+ */
+ public JPasswordField(String text, int columns)
+ {
+ this(null, text, columns);
+ }
+
+ /**
+ * Creates a JPasswordField object.
+ *
+ * @param document the document to use
+ * @param text the initial text
+ * @param columns the number of columns
+ */
+ public JPasswordField(Document document, String text, int columns)
+ {
+ super(document, text, columns);
+ }
+
+ /**
+ * writeObject
+ *
+ * @param stream the stream to write to
+ *
+ * @exception IOException if an error occurs
+ */
+ private void writeObject(ObjectOutputStream stream) throws IOException
+ {
+ // TODO: Implement me.
+ }
+
+ /**
+ * Returns the UIClassID
+ *
+ * @return the string "PasswordFieldUI"
+ */
+ public String getUIClassID()
+ {
+ return "PasswordFieldUI";
+ }
+
+ /**
+ * getEchoChar
+ *
+ * @return the echo char
+ */
+ public char getEchoChar()
+ {
+ return echoChar;
+ }
+
+ /**
+ * setEchoChar
+ *
+ * @param echo the echo char
+ */
+ public void setEchoChar(char echo)
+ {
+ this.echoChar = echo;
+ }
+
+ /**
+ * Returns true if this JPasswordField has a character set for echoing.
+ * A character is considered to be set if the echo character is not 0.
+ *
+ * @return true if the echo char is set,
+ * false otherwise.
+ */
+ public boolean echoCharIsSet()
+ {
+ return echoChar != 0;
+ }
+
+ /**
+ * Copies the selected text into the clipboard. This operation is not
+ * allowed in a password input field.
+ */
+ public void copy()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Cuts the selected text and puts it into the clipboard. This operation
+ * is not allowed in a password input field.
+ */
+ public void cut()
+ {
+ UIManager.getLookAndFeel().provideErrorFeedback(this);
+ }
+
+ /**
+ * Returns the text contained in this TextComponent. If the
+ * underlying document is null, will give a NullPointerException.
+ *
+ * @return String
+ *
+ * @deprecated
+ */
+ public String getText()
+ {
+ try
+ {
+ return getDocument().getText(0, getDocument().getLength());
+ }
+ catch (BadLocationException ble)
+ {
+ // This should never happen.
+ throw new AssertionError(ble);
+ }
+ }
+
+ /**
+ * Fetches a portion of the text represented by the component.
+ * Returns an empty string if length is 0. If the
+ * underlying document is null, will give a NullPointerException.
+ *
+ * @param offset TODO
+ * @param length TODO
+ *
+ * @return String
+ *
+ * @exception BadLocationException TODO
+ *
+ * @deprecated
+ */
+ public String getText(int offset, int length) throws BadLocationException
+ {
+ return getDocument().getText(offset, length);
+ }
+
+ /**
+ * Returns the text contained in this TextComponent. If the underlying
+ * document is null, will give a NullPointerException.
+ * For stronger security, it is recommended that the returned character
+ * array be cleared after use by setting each character to zero.
+ *
+ * @return char[]
+ */
+ public char[] getPassword()
+ {
+ return getText().toCharArray();
+ }
+
+ /**
+ * Returns a string representation of this JPasswordField. This method is
+ * intended to be used only for debugging purposes,
+ * and the content and format of the returned string may vary between
+ * implementations. The returned string may be empty but may not be null.
+ *
+ * @return String
+ */
+ protected String paramString()
+ {
+ try
+ {
+ return getText();
+ }
+ catch (NullPointerException npe)
+ {
+ return "";
+ }
+ }
+
+ /**
+ * getAccessibleContext
+ *
+ * @return the AccessibleContext object
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPasswordField();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JPopupMenu.java b/libjava/classpath/javax/swing/JPopupMenu.java
new file mode 100644
index 000000000..8f16aa2a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/JPopupMenu.java
@@ -0,0 +1,947 @@
+/* JPopupMenu.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.ArrayList;
+import java.util.EventListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.event.MenuKeyListener;
+import javax.swing.event.PopupMenuEvent;
+import javax.swing.event.PopupMenuListener;
+import javax.swing.plaf.PopupMenuUI;
+
+/**
+ * JPopupMenu is a container that is used to display popup menu's menu
+ * items. By default JPopupMenu is a lightweight container, however if it
+ * is the case that JPopupMenu's bounds are outside of main window, then
+ * heawyweight container will be used to display menu items. It is also
+ * possible to change JPopupMenu's default behavior and set JPopupMenu
+ * to always use heavyweight container.
+ *
+ * JPopupMenu can be displayed anywhere; it is a floating free popup menu.
+ * However before JPopupMenu is diplayed, its invoker property should be set.
+ * JPopupMenu's invoker is a component relative to which popup menu is
+ * displayed.
+ *
+ * JPopupMenu fires PopupMenuEvents to its registered listeners. Whenever
+ * JPopupMenu becomes visible on the screen then PopupMenuEvent indicating
+ * that popup menu became visible will be fired. In the case when
+ * JPopupMenu becomes invisible or cancelled without selection, then
+ * popupMenuBecomeInvisible() or popupMenuCancelled() methods of
+ * PopupMenuListeners will be invoked.
+ *
+ * JPopupMenu also fires PropertyChangeEvents when its bound properties
+ * change.In addittion to inheritted bound properties, JPopupMenu has
+ * 'visible' bound property. When JPopupMenu becomes visible/invisible on
+ * the screen it fires PropertyChangeEvents to its registered
+ * PropertyChangeListeners.
+ */
+public class JPopupMenu extends JComponent implements Accessible, MenuElement
+{
+ private static final long serialVersionUID = -8336996630009646009L;
+
+ /* indicates if popup's menu border should be painted*/
+ private boolean borderPainted = true;
+
+ /** Flag indicating whether lightweight, mediumweight or heavyweight popup
+ is used to display menu items.
+
+ These are the possible cases:
+
+ 1. if DefaultLightWeightPopupEnabled true
+ (i) use lightweight container if popup feets inside top-level window
+ (ii) only use heavyweight container (JDialog) if popup doesn't fit.
+
+ 2. if DefaultLightWeightPopupEnabled false
+ (i) if popup fits, use awt.Panel (mediumWeight)
+ (ii) if popup doesn't fit, use JDialog (heavyWeight)
+ */
+ private static boolean DefaultLightWeightPopupEnabled = true;
+
+ /* Component that invokes popup menu. */
+ transient Component invoker;
+
+ /* Label for this popup menu. It is not used in most of the look and feel themes. */
+ private String label;
+
+ /*Amount of space between menuItem's in JPopupMenu and JPopupMenu's border */
+ private Insets margin;
+
+ /** Indicates whether ligthWeight container can be used to display popup
+ menu. This flag is the same as DefaultLightWeightPopupEnabled, but setting
+ this flag can change popup menu after creation of the object */
+ private boolean lightWeightPopupEnabled;
+
+ /** SelectionModel that keeps track of menu selection. */
+ protected SingleSelectionModel selectionModel;
+
+ /* Popup that is used to display JPopupMenu */
+ private transient Popup popup;
+
+ /**
+ * Location of the popup, X coordinate.
+ */
+ private int popupLocationX;
+
+ /**
+ * Location of the popup, Y coordinate.
+ */
+ private int popupLocationY;
+
+ /* Field indicating if popup menu is visible or not */
+ private boolean visible = false;
+
+ /**
+ * Creates a new JPopupMenu object.
+ */
+ public JPopupMenu()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new JPopupMenu with specified label
+ *
+ * @param label Label for popup menu.
+ */
+ public JPopupMenu(String label)
+ {
+ lightWeightPopupEnabled = getDefaultLightWeightPopupEnabled();
+ setLabel(label);
+ setSelectionModel(new DefaultSingleSelectionModel());
+ super.setVisible(false);
+ updateUI();
+ }
+
+ /**
+ * Adds given menu item to the popup menu
+ *
+ * @param item menu item to add to the popup menu
+ *
+ * @return menu item that was added to the popup menu
+ */
+ public JMenuItem add(JMenuItem item)
+ {
+ this.insert(item, -1);
+ return item;
+ }
+
+ /**
+ * Constructs menu item with a specified label and adds it to
+ * popup menu
+ *
+ * @param text label for the menu item to be added
+ *
+ * @return constructed menu item that was added to the popup menu
+ */
+ public JMenuItem add(String text)
+ {
+ JMenuItem item = new JMenuItem(text);
+ return add(item);
+ }
+
+ /**
+ * Constructs menu item associated with the specified action
+ * and adds it to the popup menu
+ *
+ * @param action Action for the new menu item
+ *
+ * @return menu item that was added to the menu
+ */
+ public JMenuItem add(Action action)
+ {
+ JMenuItem item = createActionComponent(action);
+
+ if (action != null)
+ action.addPropertyChangeListener(createActionChangeListener(item));
+
+ return add(item);
+ }
+
+ /**
+ * Revomes component at the given index from the menu.
+ *
+ * @param index index of the component that will be removed in the menu
+ */
+ public void remove(int index)
+ {
+ super.remove(index);
+ revalidate();
+ }
+
+ /**
+ * Create menu item associated with the given action
+ * and inserts it into the popup menu at the specified index
+ *
+ * @param action Action for the new menu item
+ * @param index index in the popup menu at which to insert new menu item.
+ */
+ public void insert(Action action, int index)
+ {
+ JMenuItem item = new JMenuItem(action);
+ this.insert(item, index);
+ }
+
+ /**
+ * Insert given component to the popup menu at the
+ * specified index
+ *
+ * @param component Component to insert
+ * @param index Index at which to insert given component
+ */
+ public void insert(Component component, int index)
+ {
+ super.add(component, index);
+ }
+
+ /**
+ * Returns flag indicating if newly created JPopupMenu will use
+ * heavyweight or lightweight container to display its menu items
+ *
+ * @return true if JPopupMenu will use lightweight container to display
+ * menu items by default, and false otherwise.
+ */
+ public static boolean getDefaultLightWeightPopupEnabled()
+ {
+ return DefaultLightWeightPopupEnabled;
+ }
+
+ /**
+ * Sets whether JPopupMenu should use ligthWeight container to
+ * display it menu items by default
+ *
+ * @param enabled true if JPopupMenu should use lightweight container
+ * for displaying its menu items, and false otherwise.
+ */
+ public static void setDefaultLightWeightPopupEnabled(boolean enabled)
+ {
+ DefaultLightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * This method returns the UI used to display the JPopupMenu.
+ *
+ * @return The UI used to display the JPopupMenu.
+ */
+ public PopupMenuUI getUI()
+ {
+ return (PopupMenuUI) ui;
+ }
+
+ /**
+ * Set the "UI" property of the menu item, which is a look and feel class
+ * responsible for handling popupMenu's input events and painting it.
+ *
+ * @param ui The new "UI" property
+ */
+ public void setUI(PopupMenuUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method sets this menuItem's UI to the UIManager's default for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((PopupMenuUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "PopupMenuUI"
+ */
+ public String getUIClassID()
+ {
+ return "PopupMenuUI";
+ }
+
+ /**
+ * Returns selectionModel used by this popup menu to keep
+ * track of the selection.
+ *
+ * @return popup menu's selection model
+ */
+ public SingleSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ /**
+ * Sets selection model for this popup menu
+ *
+ * @param model new selection model of this popup menu
+ */
+ public void setSelectionModel(SingleSelectionModel model)
+ {
+ selectionModel = model;
+ }
+
+ /**
+ * Creates new menu item associated with a given action.
+ *
+ * @param action Action used to create new menu item
+ *
+ * @return new created menu item associated with a given action.
+ */
+ protected JMenuItem createActionComponent(Action action)
+ {
+ return new JMenuItem(action);
+ }
+
+ /**
+ * Creates PropertyChangeListener that listens to PropertyChangeEvents
+ * occuring in the Action associated with given menu item in this popup menu.
+ *
+ * @param item MenuItem
+ *
+ * @return The PropertyChangeListener
+ */
+ protected PropertyChangeListener createActionChangeListener(JMenuItem item)
+ {
+ return new ActionChangeListener();
+ }
+
+ /**
+ * Returns true if this popup menu will display its menu item in
+ * a lightweight container and false otherwise.
+ *
+ * @return true if this popup menu will display its menu items
+ * in a lightweight container and false otherwise.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param enabled DOCUMENT ME!
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ }
+
+ /**
+ * Returns label for this popup menu
+ *
+ * @return label for this popup menu
+ */
+ public String getLabel()
+ {
+ return label;
+ }
+
+ /**
+ * Sets label for this popup menu. This method fires PropertyChangeEvent
+ * when the label property is changed. Please note that most
+ * of the Look & Feel will ignore this property.
+ *
+ * @param label label for this popup menu
+ */
+ public void setLabel(String label)
+ {
+ if (label != this.label)
+ {
+ String oldLabel = this.label;
+ this.label = label;
+ firePropertyChange("label", oldLabel, label);
+ }
+ }
+
+ /**
+ * Adds separator to this popup menu
+ */
+ public void addSeparator()
+ {
+ // insert separator at the end of the list of menu items
+ this.insert(new Separator(), -1);
+ }
+
+ /**
+ * Adds a MenuKeyListener to the popup.
+ *
+ * @param l - the listener to add.
+ */
+ public void addMenuKeyListener(MenuKeyListener l)
+ {
+ listenerList.add(MenuKeyListener.class, l);
+ }
+
+ /**
+ * Removes a MenuKeyListener from the popup.
+ *
+ * @param l - the listener to remove.
+ */
+ public void removeMenuKeyListener(MenuKeyListener l)
+ {
+ listenerList.remove(MenuKeyListener.class, l);
+ }
+
+ /**
+ * Returns array of getMenuKeyListeners that are listening to JPopupMenu.
+ *
+ * @return array of getMenuKeyListeners that are listening to JPopupMenu
+ */
+ public MenuKeyListener[] getMenuKeyListeners()
+ {
+ return ((MenuKeyListener[]) listenerList.getListeners(MenuKeyListener.class));
+ }
+
+ /**
+ * Adds popupMenuListener to listen for PopupMenuEvents fired
+ * by the JPopupMenu
+ *
+ * @param listener PopupMenuListener to add to JPopupMenu
+ */
+ public void addPopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.add(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Removes PopupMenuListener from JPopupMenu's list of listeners
+ *
+ * @param listener PopupMenuListener which needs to be removed
+ */
+ public void removePopupMenuListener(PopupMenuListener listener)
+ {
+ listenerList.remove(PopupMenuListener.class, listener);
+ }
+
+ /**
+ * Returns array of PopupMenuListeners that are listening to JPopupMenu
+ *
+ * @return Array of PopupMenuListeners that are listening to JPopupMenu
+ */
+ public PopupMenuListener[] getPopupMenuListeners()
+ {
+ return ((PopupMenuListener[]) listenerList.getListeners(PopupMenuListener.class));
+ }
+
+ /**
+ * This method calls popupMenuWillBecomeVisible() of popup menu's
+ * PopupMenuListeners. This method is invoked just before popup menu
+ * will appear on the screen.
+ */
+ protected void firePopupMenuWillBecomeVisible()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuWillBecomeVisible(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This method calls popupMenuWillBecomeInvisible() of popup
+ * menu's PopupMenuListeners. This method is invoked just before popup
+ * menu will disappear from the screen
+ */
+ protected void firePopupMenuWillBecomeInvisible()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuWillBecomeInvisible(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This method calls popupMenuCanceled() of popup menu's PopupMenuListeners.
+ * This method is invoked just before popup menu is cancelled. This happens
+ * when popup menu is closed without selecting any of its menu items. This
+ * usually happens when the top-level window is resized or moved.
+ */
+ protected void firePopupMenuCanceled()
+ {
+ EventListener[] ll = listenerList.getListeners(PopupMenuListener.class);
+
+ for (int i = 0; i < ll.length; i++)
+ ((PopupMenuListener) ll[i]).popupMenuCanceled(new PopupMenuEvent(this));
+ }
+
+ /**
+ * This methods sets popup menu's size to its' preferred size. If the
+ * popup menu's size is previously set it will be ignored.
+ */
+ public void pack()
+ {
+ // Hook up this call so that it gets executed on the event thread in order
+ // to avoid synchronization problems when calling the layout manager.
+ if (! SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ show();
+ }
+ });
+ }
+
+ setSize(getPreferredSize());
+ }
+
+ /**
+ * Return visibility of the popup menu
+ *
+ * @return true if popup menu is visible on the screen and false otherwise.
+ */
+ public boolean isVisible()
+ {
+ return visible;
+ }
+
+ /**
+ * Sets visibility property of this popup menu. If the property is
+ * set to true then popup menu will be dispayed and popup menu will
+ * hide itself if visible property is set to false.
+ *
+ * @param visible true if popup menu will become visible and false otherwise.
+ */
+ public void setVisible(final boolean visible)
+ {
+ // Hook up this call so that it gets executed on the event thread in order
+ // to avoid synchronization problems when calling the layout manager.
+ if (! SwingUtilities.isEventDispatchThread())
+ {
+ SwingUtilities.invokeLater(new Runnable()
+ {
+ public void run()
+ {
+ setVisible(visible);
+ }
+ });
+ }
+
+ if (visible == isVisible())
+ return;
+
+ boolean old = isVisible();
+ this.visible = visible;
+ if (old != isVisible())
+ {
+ if (visible)
+ {
+ if (invoker != null && !(invoker instanceof JMenu))
+ {
+ MenuElement[] menuEls;
+ if (getSubElements().length > 0)
+ {
+ menuEls = new MenuElement[2];
+ menuEls[0] = this;
+ menuEls[1] = getSubElements()[0];
+ }
+ else
+ {
+ menuEls = new MenuElement[1];
+ menuEls[0] = this;
+ }
+ MenuSelectionManager.defaultManager().setSelectedPath(menuEls);
+ }
+ firePopupMenuWillBecomeVisible();
+ PopupFactory pf = PopupFactory.getSharedInstance();
+ pack();
+ popup = pf.getPopup(invoker, this, popupLocationX, popupLocationY);
+ popup.show();
+ }
+ else
+ {
+ getSelectionModel().clearSelection();
+ firePopupMenuWillBecomeInvisible();
+ popup.hide();
+ }
+ firePropertyChange("visible", old, isVisible());
+ }
+ }
+
+ /**
+ * Sets location of the popup menu.
+ *
+ * @param x X coordinate of the popup menu's location
+ * @param y Y coordinate of the popup menu's location
+ */
+ public void setLocation(int x, int y)
+ {
+ popupLocationX = x;
+ popupLocationY = y;
+ // Handle the case when the popup is already showing. In this case we need
+ // to fetch a new popup from PopupFactory and use this. See the general
+ // contract of the PopupFactory.
+ }
+
+ /**
+ * Returns popup menu's invoker.
+ *
+ * @return popup menu's invoker
+ */
+ public Component getInvoker()
+ {
+ return invoker;
+ }
+
+ /**
+ * Sets popup menu's invoker.
+ *
+ * @param component The new invoker of this popup menu
+ */
+ public void setInvoker(Component component)
+ {
+ invoker = component;
+ }
+
+ /**
+ * This method displays JPopupMenu on the screen at the specified
+ * location. Note that x and y coordinates given to this method
+ * should be expressed in terms of the popup menus' invoker.
+ *
+ * @param component Invoker for this popup menu
+ * @param x x-coordinate of the popup menu relative to the specified invoker
+ * @param y y-coordiate of the popup menu relative to the specified invoker
+ */
+ public void show(Component component, int x, int y)
+ {
+ if (component.isShowing())
+ {
+ setInvoker(component);
+ Point p = new Point(x, y);
+ SwingUtilities.convertPointToScreen(p, component);
+ setLocation(p.x, p.y);
+ setVisible(true);
+ }
+ }
+
+ /**
+ * Returns component located at the specified index in the popup menu
+ *
+ * @param index index of the component to return
+ *
+ * @return component located at the specified index in the popup menu
+ *
+ * @deprecated Replaced by getComponent(int)
+ */
+ public Component getComponentAtIndex(int index)
+ {
+ return getComponent(index);
+ }
+
+ /**
+ * Returns index of the specified component in the popup menu
+ *
+ * @param component Component to look for
+ *
+ * @return index of the specified component in the popup menu
+ */
+ public int getComponentIndex(Component component)
+ {
+ Component[] items = getComponents();
+
+ for (int i = 0; i < items.length; i++)
+ {
+ if (items[i].equals(component))
+ return i;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Sets size of the popup
+ *
+ * @param size Dimensions representing new size of the popup menu
+ */
+ public void setPopupSize(Dimension size)
+ {
+ super.setSize(size);
+ }
+
+ /**
+ * Sets size of the popup menu
+ *
+ * @param width width for the new size
+ * @param height height for the new size
+ */
+ public void setPopupSize(int width, int height)
+ {
+ super.setSize(width, height);
+ }
+
+ /**
+ * Selects specified component in this popup menu.
+ *
+ * @param selected component to select
+ */
+ public void setSelected(Component selected)
+ {
+ int index = getComponentIndex(selected);
+ selectionModel.setSelectedIndex(index);
+ }
+
+ /**
+ * Checks if this popup menu paints its border.
+ *
+ * @return true if this popup menu paints its border and false otherwise.
+ */
+ public boolean isBorderPainted()
+ {
+ return borderPainted;
+ }
+
+ /**
+ * Sets if the border of the popup menu should be
+ * painter or not.
+ *
+ * @param painted true if the border should be painted and false otherwise
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ borderPainted = painted;
+ }
+
+ /**
+ * Returns margin for this popup menu.
+ *
+ * @return margin for this popup menu.
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ }
+
+ /**
+ * A string that describes this JPopupMenu. Normally only used
+ * for debugging.
+ *
+ * @return A string describing this JMenuItem
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(super.paramString());
+ sb.append(",label=");
+ if (getLabel() != null)
+ sb.append(getLabel());
+ sb.append(",lightWeightPopupEnabled=").append(isLightWeightPopupEnabled());
+ sb.append(",margin=");
+ if (getMargin() != null)
+ sb.append(margin);
+ sb.append(",paintBorder=").append(isBorderPainted());
+ return sb.toString();
+ }
+
+ /**
+ * Process mouse events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ */
+ public void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Empty Implementation. This method is needed for the implementation
+ // of MenuElement interface
+ }
+
+ /**
+ * Process key events forwarded from MenuSelectionManager. This method
+ * doesn't do anything. It is here to conform to the MenuElement interface.
+ *
+ * @param event event forwarded from MenuSelectionManager
+ * @param path path to the menu element from which event was generated
+ * @param manager MenuSelectionManager for the current menu hierarchy
+ *
+ */
+ public void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager)
+ {
+ // Empty Implementation. This method is needed for the implementation
+ // of MenuElement interface
+ }
+
+ /**
+ * Method of MenuElement Interface. It is invoked when
+ * popupMenu's selection has changed
+ *
+ * @param changed true if this popupMenu is part of current menu
+ * hierarchy and false otherwise.
+ */
+ public void menuSelectionChanged(boolean changed)
+ {
+ if (invoker instanceof JMenu)
+ {
+ // We need to special case this since the JMenu calculates the
+ // position etc of the popup.
+ JMenu menu = (JMenu) invoker;
+ menu.setPopupMenuVisible(changed);
+ }
+ else if (! changed)
+ setVisible(false);
+ }
+
+ /**
+ * Return subcomonents of this popup menu. This method returns only
+ * components that implement the MenuElement interface.
+ *
+ * @return array of menu items belonging to this popup menu
+ */
+ public MenuElement[] getSubElements()
+ {
+ Component[] items = getComponents();
+ ArrayList subElements = new ArrayList();
+
+ for (int i = 0; i < items.length; i++)
+ if (items[i] instanceof MenuElement)
+ subElements.add(items[i]);
+
+ return (MenuElement[])
+ subElements.toArray(new MenuElement[subElements.size()]);
+ }
+
+ /**
+ * Method of the MenuElement interface. Returns reference to itself.
+ *
+ * @return Returns reference to itself
+ */
+ public Component getComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Checks if observing mouse event should trigger popup
+ * menu to show on the screen.
+ *
+ * @param event MouseEvent to check
+ *
+ * @return true if the observing mouse event is popup trigger and false otherwise
+ */
+ public boolean isPopupTrigger(MouseEvent event)
+ {
+ return ((PopupMenuUI) getUI()).isPopupTrigger(event);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJPopupMenu();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This is the separator that can be used in popup menu.
+ */
+ public static class Separator extends JSeparator
+ {
+ public Separator()
+ {
+ super();
+ }
+
+ public String getUIClassID()
+ {
+ return "PopupMenuSeparatorUI";
+ }
+ }
+
+ /**
+ * Returns true if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return true if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+
+ protected class AccessibleJPopupMenu extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 7423261328879849768L;
+
+ protected AccessibleJPopupMenu()
+ {
+ // Nothing to do here.
+ }
+
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.POPUP_MENU;
+ }
+ }
+
+ /* This class resizes popup menu and repaints popup menu appropriately if one
+ of item's action has changed */
+ private class ActionChangeListener implements PropertyChangeListener
+ {
+ public void propertyChange(PropertyChangeEvent evt)
+ {
+ // We used to have a revalidate() and repaint() call here. However I think
+ // this is not needed. Instead, a new Popup has to be fetched from the
+ // PopupFactory and used here.
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JProgressBar.java b/libjava/classpath/javax/swing/JProgressBar.java
new file mode 100644
index 000000000..d24a380b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/JProgressBar.java
@@ -0,0 +1,861 @@
+/* JProgressBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Graphics;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ProgressBarUI;
+
+/**
+ * A component that displays a visual indicator of the progress of a task. The
+ * component has two modes: determinate and indeterminate. In determinate mode,
+ * the JProgressBar fills a percentage of its bar based on its
+ * current value. In indeterminate mode, it creates box and bounces it between
+ * its bounds.
+ *
+ * This component has the following properties:
+ *
+ *
+ *
Property
Stored in
Bound?
+ *
borderPainted
progressBar
yes
+ *
changeListeners
progressBar
no
+ *
indeterminate
progressBar
yes
+ *
maximum
model
no
+ *
minimum
model
no
+ *
model
progressBar
no
+ *
orientation
progressBar
yes
+ *
percentComplete
progressBar
no
+ *
string
progressBar
yes
+ *
stringPainted
progressBar
yes
+ *
value
model
no
+ *
+ */
+public class JProgressBar extends JComponent implements SwingConstants,
+ Accessible
+{
+ /**
+ * Provides the accessibility features for the JProgressBar
+ * component.
+ */
+ protected class AccessibleJProgressBar extends AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -2938130009392721813L;
+
+ /**
+ * Creates a new AccessibleJProgressBar instance.
+ */
+ protected AccessibleJProgressBar()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JProgressBar}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JProgressBar.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JProgressBar.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the JProgressBar component.
+ *
+ * @return {@link AccessibleRole#PROGRESS_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PROGRESS_BAR;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JProgressBar} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JProgressBar} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is null, this method
+ * does nothing and returns false.
+ *
+ * @param value the new progress bar value (null permitted).
+ *
+ * @return true if the slider value is updated, and
+ * false otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JProgressBar} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JProgressBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JProgressBar} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum());
+ }
+ }
+
+ private static final long serialVersionUID = 1980046021813598781L;
+
+ /**
+ * A flag that determines the mode (true for indeterminate,
+ * false for determinate).
+ */
+ private transient boolean indeterminate = false;
+
+ /**
+ * The orientation of the JProgressBar
+ * ({@link SwingConstants#HORIZONTAL} or {@link SwingConstants#VERTICAL}).
+ * Defaults to {@link SwingConstants#HORIZONTAL}.
+ * @see #setOrientation(int)
+ */
+ protected int orientation;
+
+ /**
+ * A flag the controls whether or not the component's border is painted.
+ * The default is true.
+ * @see #setBorderPainted(boolean)
+ */
+ protected boolean paintBorder = true;
+
+ /**
+ * The model defining the bounds and current value for the progress bar.
+ * @see #setModel(BoundedRangeModel)
+ */
+ protected BoundedRangeModel model;
+
+ /**
+ * A custom string for display in the progress bar. If this is
+ * null, a default string will be generated.
+ * @see #setString(String)
+ */
+ protected String progressString;
+
+ /**
+ * A flag that controls whether a string is displayed within the progress
+ * bar.
+ * @see #setStringPainted(boolean)
+ */
+ protected boolean paintString = false;
+
+ /**
+ * A single change event reused for all events.
+ * @see #fireStateChanged()
+ */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * The listener that is registered with the model. */
+ protected ChangeListener changeListener;
+
+ /**
+ * Creates a new JProgressBar with default attributes. The
+ * following defaults are used:
+ *
+ *
+ *
value: 0;
+ *
minimum: 0;
+ *
maximum: 100;
+ *
orientation: {@link SwingConstants#HORIZONTAL}.
+ *
+ */
+ public JProgressBar()
+ {
+ this(HORIZONTAL, 0, 100);
+ }
+
+ /**
+ * Creates a new JProgressBar with the specified
+ * orientation. The following defaults are used:
+ *
+ *
+ *
value: 0;
+ *
minimum: 0;
+ *
maximum: 100;
+ *
+ *
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the specified values.
+ */
+ public JProgressBar(int orientation)
+ {
+ this(orientation, 0, 100);
+ }
+
+ /**
+ * Creates a new JProgressBar with the specified value range.
+ * The following defaults are used:
+ *
+ *
+ *
value: minimum;
+ *
orientation: {@link SwingConstants#HORIZONTAL}.
+ *
+ *
+ * @param minimum the lower bound of the value range.
+ * @param maximum the upper bound of the value range.
+ */
+ public JProgressBar(int minimum, int maximum)
+ {
+ this(HORIZONTAL, minimum, maximum);
+ }
+
+ /**
+ * Creates a new JProgressBar with the specified range and
+ * orientation. The following defaults are used:
+ *
+ *
+ *
value: minimum;
+ *
+ *
+ * @param minimum the lower bound of the value range.
+ * @param maximum the upper bound of the value range.
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the specified values.
+ */
+ public JProgressBar(int orientation, int minimum, int maximum)
+ {
+ model = new DefaultBoundedRangeModel(minimum, 0, minimum, maximum);
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Creates a new JProgressBar with the specified model. The
+ * following defaults are used:
+ *
+ *
+ *
orientation: {@link SwingConstants#HORIZONTAL}.
+ *
+ *
+ * @param model the model (null not permitted).
+ */
+ public JProgressBar(BoundedRangeModel model)
+ {
+ this.model = model;
+ changeListener = createChangeListener();
+ if (model != null)
+ model.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Returns the current value for the JProgressBar. This value
+ * is fetched from the model.
+ *
+ * @return The current value.
+ *
+ * @see #setValue(int)
+ */
+ public int getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * Sets the current value for the JProgressBar. The value is
+ * stored in the component's model (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a ChangeEvent
+ * to this component's registered listeners.
+ *
+ * If value is outside the range minimum to
+ * maximum, it will be set to the nearest of those boundary
+ * values.
+ *
+ * @param value the new value.
+ *
+ * @see #getValue()
+ */
+ public void setValue(int value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * Paints the component's border, but only if {@link #isBorderPainted()}
+ * returns true.
+ *
+ * @param graphics the graphics object to paint with.
+ *
+ * @see #setBorderPainted(boolean)
+ */
+ protected void paintBorder(Graphics graphics)
+ {
+ Border border = getBorder();
+ if (paintBorder && border != null)
+ border.paintBorder(this, graphics, 0, 0, getWidth(), getHeight());
+ }
+
+ /**
+ * Returns the orientation of the JProgressBar component, which
+ * is either {@link SwingConstants#HORIZONTAL} or
+ * {@link SwingConstants#VERTICAL}. The default orientation is
+ * HORIZONTAL.
+ *
+ * @return The orientation.
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for this JProgressBar component and,
+ * if the value changes, sends a {@link PropertyChangeEvent} (with the
+ * property name "orientation") to all registered listeners.
+ *
+ * @param orientation the orientation ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not
+ * one of the listed values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != VERTICAL && orientation != HORIZONTAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ if (this.orientation != orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not the string returned by
+ * {@link #getString()} is displayed by the JProgressBar
+ * component.
+ *
+ * @return true if the string should be displayed, and
+ * false otherwise.
+ *
+ * @see #setStringPainted(boolean)
+ */
+ public boolean isStringPainted()
+ {
+ return paintString;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the string returned by
+ * {@link #getString()} is displayed by the JProgressBar
+ * component. If the flag value changes, a {@link PropertyChangeEvent} (with
+ * the property name "stringPainted") is sent to all registered
+ * listeners.
+ *
+ * @param painted the new flag value.
+ *
+ * @see #isStringPainted()
+ * @see #setString(String)
+ */
+ public void setStringPainted(boolean painted)
+ {
+ if (paintString != painted)
+ {
+ boolean oldPainted = paintString;
+ paintString = painted;
+ firePropertyChange("stringPainted", oldPainted, paintString);
+ }
+ }
+
+ /**
+ * Returns the string that is painted on the JProgressBar if
+ * {@link #isStringPainted()} returns true. If no string has
+ * been explicitly set, this method will return a string displaying the
+ * value of {@link #getPercentComplete()}.
+ *
+ * @return The string.
+ *
+ * @see #setString(String)
+ * @see #setStringPainted(boolean)
+ */
+ public String getString()
+ {
+ if (progressString != null)
+ return progressString;
+ else
+ return (int) (getPercentComplete() * 100) + "%";
+ }
+
+ /**
+ * Sets the string to display within the progress bar and, if the new value
+ * is different to the old value, sends a {@link PropertyChangeEvent} (with
+ * the property name "string") to all registered listeners. If
+ * the string is set to null, {@link #getString()} will return
+ * a default string.
+ *
+ * @param string the string (null permitted).
+ *
+ * @see #getString()
+ * @see #setStringPainted(boolean)
+ */
+ public void setString(String string)
+ {
+ if (((string == null || progressString == null) &&
+ string != progressString) || (string != null &&
+ ! string.equals(progressString)))
+ {
+ String oldString = progressString;
+ progressString = string;
+ firePropertyChange("string", oldString, progressString);
+ }
+ }
+
+ /**
+ * Returns the current value expressed as a percentage. This is calculated
+ * as (value - min) / (max - min).
+ *
+ * @return The percentage (a value in the range 0.0 to 1.0).
+ */
+ public double getPercentComplete()
+ {
+ if (getMaximum() == getMinimum())
+ return 1.0;
+ else
+ return (double) (model.getValue() - model.getMinimum())
+ / (model.getMaximum() - model.getMinimum());
+ }
+
+ /**
+ * Returns a flag that controls whether or not the component's border is
+ * painted. The default value is true.
+ *
+ * @return true if the component's border should be painted,
+ * and false otherwise.
+ *
+ * @see #setBorderPainted(boolean)
+ */
+ public boolean isBorderPainted()
+ {
+ return paintBorder;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the component's border is
+ * painted. If the flag value is changed, this method sends a
+ * {@link PropertyChangeEvent} (with the property name "borderPainted") to
+ * all registered listeners.
+ *
+ * @param painted the new flag value.
+ *
+ * @see #isBorderPainted()
+ * @see #paintBorder
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ if (painted != paintBorder)
+ {
+ boolean oldPainted = paintBorder;
+ paintBorder = painted;
+ firePropertyChange("borderPainted", oldPainted, paintBorder);
+ }
+ }
+
+ /**
+ * Returns the UI delegate for this JProgressBar.
+ *
+ * @return The UI delegate.
+ */
+ public ProgressBarUI getUI()
+ {
+ return (ProgressBarUI) ui;
+ }
+
+ /**
+ * Sets the UI delegate for this component.
+ *
+ * @param ui the new UI delegate.
+ */
+ public void setUI(ProgressBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this JProgressBar's UI delegate to the default
+ * (obtained from the {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ProgressBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix ("ProgressBarUI" in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a JProgressBar.
+ *
+ * @return "ProgressBarUI".
+ */
+ public String getUIClassID()
+ {
+ return "ProgressBarUI";
+ }
+
+ /**
+ * Creates a new {@link ChangeListener} that calls
+ * {@link #fireStateChanged()} whenever it receives a {@link ChangeEvent}
+ * (typically from the component's model). This listener is
+ * registered with the progress bar's model, so that changes made to the
+ * model directly will automatically result in the progress bar's listeners
+ * being notified also.
+ *
+ * @return A new listener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent ce)
+ {
+ fireStateChanged();
+ }
+ };
+ }
+
+ /**
+ * Registers a listener with this component so that it will receive
+ * notification of component state changes.
+ *
+ * @param listener the listener.
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * component state changes.
+ *
+ * @param listener the listener.
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns an array of the listeners that are registered with this component.
+ * The array may be empty, but is never null.
+ *
+ * @return An array of listeners.
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to all registered listeners to indicate that
+ * the state of the JProgressBar has changed.
+ *
+ * @see #createChangeListener()
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * Returns the model for the JProgressBar.
+ *
+ * @return The model (never null).
+ *
+ * @see #setModel(BoundedRangeModel)
+ */
+ public BoundedRangeModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets the model for the JProgressBar and sends a
+ * {@link ChangeEvent} to all registered listeners.
+ *
+ * @param model the model (null not permitted).
+ *
+ * @see #getModel()
+ */
+ public void setModel(BoundedRangeModel model)
+ {
+ if (model != this.model)
+ {
+ this.model.removeChangeListener(changeListener);
+ this.model = model;
+ this.model.addChangeListener(changeListener);
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the minimum value for the JProgressBar. This defines
+ * the lower bound for the current value, and is stored in the component's
+ * model.
+ *
+ * @return The minimum value.
+ *
+ * @see #setMinimum(int)
+ */
+ public int getMinimum()
+ {
+ return model.getMinimum();
+ }
+
+ /**
+ * Sets the minimum value for the JProgressBar. The value is
+ * stored in the component's model (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a ChangeEvent
+ * to this component's registered listeners.
+ *
+ * @param minimum the minimum value.
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(int minimum)
+ {
+ model.setMinimum(minimum);
+ }
+
+ /**
+ * Returns the maximum value for the JProgressBar. This defines
+ * the upper bound for the current value, and is stored in the component's
+ * model.
+ *
+ * @return The maximum value.
+ *
+ * @see #setMaximum(int)
+ */
+ public int getMaximum()
+ {
+ return model.getMaximum();
+ }
+
+ /**
+ * Sets the maximum value for the JProgressBar. The value is
+ * stored in the component's model (see {@link #getModel()}).
+ * If the new value is different to the old value, a {@link ChangeEvent} is
+ * sent to the model's registered listeners. In turn, this triggers a call
+ * to {@link #fireStateChanged()} which will send a ChangeEvent
+ * to this component's registered listeners.
+ *
+ * @param maximum the maximum value.
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(int maximum)
+ {
+ model.setMaximum(maximum);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JProgressBar.
+ *
+ * @return A string describing the attributes of this
+ * JProgressBar (never null).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",paintBorder=").append(isBorderPainted());
+ sb.append(",paintString=").append(isStringPainted());
+ sb.append(",progressString=");
+ if (progressString != null)
+ sb.append(progressString);
+ sb.append(",indeterminateString=").append(isIndeterminate());
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Sets the flag that controls the mode for this JProgressBar
+ * (true for indeterminate mode, and false for
+ * determinate mode). If the flag value changes, this method sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * "indeterminate") to all registered listeners.
+ *
+ * If the JProgressBar is determinate, it paints a percentage
+ * of the bar described by its value. If it is indeterminate, it simply
+ * bounces a box between the ends of the bar; the value of the
+ * JProgressBar is ignored.
+ *
+ * @param flag the new flag value.
+ *
+ * @see #isIndeterminate()
+ * @since 1.4
+ */
+ public void setIndeterminate(boolean flag)
+ {
+ if (indeterminate != flag)
+ {
+ indeterminate = flag;
+ firePropertyChange("indeterminate", !flag, indeterminate);
+ }
+ }
+
+ /**
+ * Returns a flag that indicates the mode for this JProgressBar
+ * (true for indeterminate mode, and false for
+ * determinate mode).
+ *
+ * @return A flag indicating the mode for the JProgressBar.
+ *
+ * @see #setIndeterminate(boolean)
+ * @since 1.4
+ */
+ public boolean isIndeterminate()
+ {
+ return indeterminate;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JProgressBar component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJProgressBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJProgressBar();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JRadioButton.java b/libjava/classpath/javax/swing/JRadioButton.java
new file mode 100644
index 000000000..ae42f2c01
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRadioButton.java
@@ -0,0 +1,256 @@
+/* JRadioButton.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ButtonUI;
+
+/**
+ * The JRadioButton component provides a visually selectable
+ * button with mutually exclusive behaviour within a ButtonGroup.
+ * A series of radio buttons can be used to provide options to the user,
+ * where the user can only select one of the available options. The state
+ * of the button is provided by the superclass, JToggleButton.
+ * JRadioButton adds the additional behaviour, that if two
+ * or more radio buttons are grouped together, the selection of one implies
+ * the deselection of the other buttons within the group.
+ *
+ *
+ * Buttons are grouped by adding each instance to a ButtonGroup.
+ * The existence of such a grouping is not reflected visually, so other means
+ * should be used to denote this. For instance, the grouped buttons can be placed
+ * within the same panel, possibly with an appropriate border to denote
+ * the connection between the components.
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see JToggleButton
+ * @see ButtonGroup
+ * @since 1.2
+ */
+public class JRadioButton extends JToggleButton
+{
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = 7751949583255506856L;
+
+ /**
+ * This class provides accessibility support for the toggle button.
+ */
+ protected class AccessibleJRadioButton
+ extends AccessibleJToggleButton
+ {
+ private static final long serialVersionUID = 4850967637026120674L;
+
+ /**
+ * Constructor for the accessible toggle button.
+ */
+ protected AccessibleJRadioButton()
+ {
+ /* Call the superclass to register for events */
+ super();
+ }
+
+ /**
+ * Returns the accessible role for the toggle button.
+ *
+ * @return An instance of AccessibleRole, describing
+ * the role of the toggle button.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.RADIO_BUTTON;
+ }
+
+ }
+
+ /**
+ * Constructs an unselected radio button with no text or icon.
+ */
+ public JRadioButton()
+ {
+ this(null, null, false);
+ }
+
+ /**
+ * Constructs a radio button using the labelling, state
+ * and icon specified by the supplied action.
+ *
+ * @param a the action to use to define the properties of the button.
+ */
+ public JRadioButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Constructs an unselected radio button with the supplied icon
+ * and no text.
+ *
+ * @param icon the icon to use.
+ */
+ public JRadioButton(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied icon and state.
+ *
+ * @param icon the icon to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Constructs an unselected radio button using the supplied text
+ * and no icon.
+ *
+ * @param text the text to use.
+ */
+ public JRadioButton(String text)
+ {
+ this(text, null, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied text and state.
+ *
+ * @param text the text to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Constructs an unselected radio button with the supplied text
+ * and icon.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ */
+ public JRadioButton(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Constructs a radio button with the supplied text, icon and state.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ * @param selected if true, the radio button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JRadioButton(String text, Icon icon, boolean selected)
+ {
+ super(text, icon, selected);
+ setBorderPainted(false);
+ setHorizontalAlignment(LEADING);
+ }
+
+ /**
+ * Returns the accessible context for this JRadioButton,
+ * in the form of an instance of AccessibleJRadioButton.
+ * The context is created, if necessary.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ /* Create the context if this is the first request */
+ if (accessibleContext == null)
+ {
+ /* Create the context */
+ accessibleContext = new AccessibleJRadioButton();
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a string specifying the name of the Look and Feel UI class
+ * that renders this component.
+ *
+ * @return the Look and Feel UI class for JRadioButtons
+ * as a String.
+ */
+ public String getUIClassID()
+ {
+ return "RadioButtonUI";
+ }
+
+ /**
+ * Returns a string representation of this component for debugging use.
+ * Users should not depend on anything as regards the content or formatting
+ * of this string, except for the fact that the returned string may never be
+ * null (only empty).
+ *
+ * @return the component in String form for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ /**
+ * This method resets the radio button's UI delegate to the default UI for
+ * the current look and feel.
+ */
+ public void updateUI()
+ {
+ /*
+ I can't see any difference between this and the superclass one,
+ but Sun reimplements it... there is no RadioButtonUI class for it
+ to be cast to.
+ */
+ setUI((ButtonUI) UIManager.getUI(this));
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JRadioButtonMenuItem.java b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java
new file mode 100644
index 000000000..48fc9aeb6
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRadioButtonMenuItem.java
@@ -0,0 +1,230 @@
+/* JRadioButtonMenuItem.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+
+/**
+ * This class represents JRadioButtonMenuItem. Its behaviour is very similar
+ * to JRadioButton. Just like JRadioButton, user can check and uncheck this
+ * menu item by clicking on it. JRadioButtonMenuItem uses ToggleButtonModel
+ * to keep track of its selection. If the JRadioButtonMenuItem is included in
+ * the button group, then only one JRadioButtonMenuItem can be selected at
+ * one time.
+ */
+public class JRadioButtonMenuItem extends JMenuItem implements Accessible
+{
+ private static final long serialVersionUID = 8482658191548521743L;
+
+ /** name for the UI delegate for this radio button menu item. */
+ private static final String uiClassID = "RadioButtonMenuItemUI";
+
+ /**
+ * Creates a new JRadioButtonMenuItem object.
+ */
+ public JRadioButtonMenuItem()
+ {
+ this(null, null);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified icon
+ *
+ * @param icon Icon to be used for this menu item
+ */
+ public JRadioButtonMenuItem(Icon icon)
+ {
+ this(null, icon);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label
+ *
+ * @param text Label for this menu item
+ */
+ public JRadioButtonMenuItem(String text)
+ {
+ this(text, null);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem using specified action
+ *
+ * @param action Action for this menu item
+ */
+ public JRadioButtonMenuItem(Action action)
+ {
+ this();
+ setAction(action);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label and icon
+ *
+ * @param text Label for this menu item
+ * @param icon Icon for this menu item
+ */
+ public JRadioButtonMenuItem(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label
+ * and marked selected if 'selected' is true.
+ *
+ * @param text Text for this menu item
+ * @param selected Selected state of this menu item
+ */
+ public JRadioButtonMenuItem(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified icon
+ * and given selected state
+ *
+ * @param icon Icon for this menu item
+ * @param selected Selected state for this menu item
+ */
+ public JRadioButtonMenuItem(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Creates a new JRadioButtonMenuItem with specified label,
+ * icon and selected state.
+ *
+ * @param text Label for this menu item
+ * @param icon Icon to be use for this menu item
+ * @param selected selected state of this menu item
+ */
+ public JRadioButtonMenuItem(String text, Icon icon, boolean selected)
+ {
+ super(text, icon);
+ setModel(new JToggleButton.ToggleButtonModel());
+ model.setSelected(selected);
+ setFocusable(false);
+ }
+
+ /**
+ * This method returns a name to identify which look and feel class will be
+ * the UI delegate for the menuItem.
+ *
+ * @return The Look and Feel classID. "JRadioButtonMenuItemUI"
+ */
+ public String getUIClassID()
+ {
+ return uiClassID;
+ }
+
+ /**
+ * This method overrides JComponent.requestFocus with an empty
+ * implementation, since JRadioButtonMenuItems should not
+ * receve focus in general.
+ */
+ public void requestFocus()
+ {
+ // Should do nothing here
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * JRadioButtonMenuItem component, for use in debugging. The
+ * return value is guaranteed to be non-null, but the format of
+ * the string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * JRadioButtonMenuItem.
+ */
+ protected String paramString()
+ {
+ // calling super seems to be sufficient here...
+ return super.paramString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JRadioButtonMenuItem component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJRadioButtonMenuItem}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJRadioButtonMenuItem();
+
+ return accessibleContext;
+ }
+
+ /**
+ * Provides the accessibility features for the
+ * JRadioButtonMenuItem component.
+ *
+ * @see JRadioButtonMenuItem#getAccessibleContext()
+ */
+ protected class AccessibleJRadioButtonMenuItem extends AccessibleJMenuItem
+ {
+ private static final long serialVersionUID = 4381471510145292179L;
+
+ /**
+ * Creates a new AccessibleJRadioButtonMenuItem instance.
+ */
+ protected AccessibleJRadioButtonMenuItem()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the JRadioButtonMenuItem
+ * component.
+ *
+ * @return {@link AccessibleRole#RADIO_BUTTON}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.RADIO_BUTTON;
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JRootPane.java b/libjava/classpath/javax/swing/JRootPane.java
new file mode 100644
index 000000000..c74f4f520
--- /dev/null
+++ b/libjava/classpath/javax/swing/JRootPane.java
@@ -0,0 +1,700 @@
+/* JRootPane.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.IllegalComponentStateException;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.LayoutManager2;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.RootPaneUI;
+
+/**
+ * This class is where JComponents are added to. Unlike awt where you could
+ * just say frame.add(), with swing you need to say frame.getRootPane()
+ * (which delivers an instance of this class) and add your components to
+ * that. It is implemented by several 'layers' (pane() should be read as
+ * plane()) each on top of the others where you can add components to.
+ * (getContentPane(), getGlassPane(), getLayeredPane())
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JRootPane extends JComponent implements Accessible
+{
+ // The class used to obtain the accessible role for this object.
+ protected class AccessibleJRootPane extends AccessibleJComponent
+ {
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 1082432482784468088L;
+
+ /**
+ * Creates a new AccessibleJRootPane object.
+ */
+ protected AccessibleJRootPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.ROOT_PANE;
+ }
+ }
+
+ // Custom Layout Manager for JRootPane. It positions contentPane and
+ // menuBar withing its layeredPane.
+ protected class RootLayout implements LayoutManager2, Serializable
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -4100116998559815027L;
+
+ /**
+ * The cached layout info for the glass pane.
+ */
+ private Rectangle glassPaneBounds;
+
+ /**
+ * The cached layout info for the layered pane.
+ */
+ private Rectangle layeredPaneBounds;
+
+ /**
+ * The cached layout info for the content pane.
+ */
+ private Rectangle contentPaneBounds;
+
+ /**
+ * The cached layout info for the menu bar.
+ */
+ private Rectangle menuBarBounds;
+
+ /**
+ * Creates a new RootLayout object.
+ */
+ protected RootLayout()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ * @param constraints DOCUMENT ME!
+ */
+ public void addLayoutComponent(Component comp, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param name DOCUMENT ME!
+ * @param comp DOCUMENT ME!
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ */
+ public void invalidateLayout(Container target)
+ {
+ synchronized (this)
+ {
+ glassPaneBounds = null;
+ layeredPaneBounds = null;
+ contentPaneBounds = null;
+ menuBarBounds = null;
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ */
+ public void layoutContainer(Container c)
+ {
+ if (glassPaneBounds == null || layeredPaneBounds == null
+ || contentPaneBounds == null || menuBarBounds == null)
+ {
+ Insets i = getInsets();
+ int containerWidth = c.getBounds().width - i.left - i.right;
+ int containerHeight = c.getBounds().height - i.top - i.bottom;
+
+ // 1. the glassPane fills entire viewable region (bounds - insets).
+ // 2. the layeredPane filles entire viewable region.
+ // 3. the menuBar is positioned at the upper edge of layeredPane.
+ // 4. the contentPane fills viewable region minus menuBar, if present.
+
+
+ // +-------------------------------+
+ // | JLayeredPane |
+ // | +--------------------------+ |
+ // | | menuBar | |
+ // | +--------------------------+ |
+ // | +--------------------------+ |
+ // | |contentPane | |
+ // | | | |
+ // | | | |
+ // | | | |
+ // | +--------------------------+ |
+ // +-------------------------------+
+
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.height > containerHeight)
+ menuBarSize.height = containerHeight;
+ menuBarBounds = new Rectangle(0, 0, containerWidth,
+ menuBarSize.height);
+ contentPaneBounds = new Rectangle(0, menuBarSize.height,
+ containerWidth,
+ containerHeight - menuBarSize.height);
+ }
+ else
+ contentPaneBounds = new Rectangle(0, 0, containerWidth,
+ containerHeight);
+
+ glassPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ layeredPaneBounds = new Rectangle(i.left, i.top, containerWidth, containerHeight);
+ }
+
+ glassPane.setBounds(glassPaneBounds);
+ layeredPane.setBounds(layeredPaneBounds);
+ if (menuBar != null)
+ menuBar.setBounds(menuBarBounds);
+ getContentPane().setBounds(contentPaneBounds);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param target DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ return preferredLayoutSize(target);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param c DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Dimension preferredLayoutSize(Container c)
+ {
+ Dimension prefSize = new Dimension();
+ Insets i = getInsets();
+ prefSize = new Dimension(i.left + i.right, i.top + i.bottom);
+ Dimension contentPrefSize = getContentPane().getPreferredSize();
+ prefSize.width += contentPrefSize.width;
+ prefSize.height += contentPrefSize.height;
+ if (menuBar != null)
+ {
+ Dimension menuBarSize = menuBar.getPreferredSize();
+ if (menuBarSize.width > contentPrefSize.width)
+ prefSize.width += menuBarSize.width - contentPrefSize.width;
+ prefSize.height += menuBarSize.height;
+ }
+ return prefSize;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = 8690748000348575668L;
+
+ public static final int NONE = 0;
+ public static final int FRAME = 1;
+ public static final int PLAIN_DIALOG = 2;
+ public static final int INFORMATION_DIALOG = 3;
+ public static final int ERROR_DIALOG = 4;
+ public static final int COLOR_CHOOSER_DIALOG = 5;
+ public static final int FILE_CHOOSER_DIALOG = 6;
+ public static final int QUESTION_DIALOG = 7;
+ public static final int WARNING_DIALOG = 8;
+
+ /** DOCUMENT ME! */
+ protected Component glassPane;
+
+ /** DOCUMENT ME! */
+ protected JLayeredPane layeredPane;
+
+ /** DOCUMENT ME! */
+ protected JMenuBar menuBar;
+
+ /** DOCUMENT ME! */
+ protected Container contentPane;
+
+ protected JButton defaultButton;
+
+ /**
+ * This field is unused since JDK1.3. To override the default action you
+ * should modify the JRootPane's ActionMap.
+ *
+ * @deprecated since JDK1.3
+ *
+ * @specnote the specs indicate that the type of this field is
+ * a package private inner class
+ * javax.swing.JRootPane.DefaultAction. I assume that the closest
+ * public superclass is javax.swing.Action.
+ */
+ protected Action defaultPressAction;
+
+ /**
+ * This field is unused since JDK1.3. To override the default action you
+ * should modify the JRootPane's ActionMap.
+ *
+ * @deprecated since JDK1.3
+ *
+ * @specnote the specs indicate that the type of this field is
+ * a package private inner class
+ * javax.swing.JRootPane.DefaultAction. I assume that the closest
+ * public superclass is javax.swing.Action.
+ */
+ protected Action defaultReleaseAction;
+
+ /**
+ * @since 1.4
+ */
+ private int windowDecorationStyle = NONE;
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param m DOCUMENT ME!
+ */
+ public void setJMenuBar(JMenuBar m)
+ {
+ JLayeredPane jlPane = getLayeredPane();
+ if (menuBar != null)
+ jlPane.remove(menuBar);
+ menuBar = m;
+ if (menuBar != null)
+ jlPane.add(menuBar, JLayeredPane.FRAME_CONTENT_LAYER);
+ }
+
+ /**
+ * @deprecated Replaced by setJMenuBar()
+ */
+ public void setMenuBar(JMenuBar m)
+ {
+ setJMenuBar(m);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public JMenuBar getJMenuBar()
+ {
+ return menuBar;
+ }
+
+ /**
+ * @deprecated Replaced by getJMenuBar()
+ */
+ public JMenuBar getMenuBar()
+ {
+ return getJMenuBar();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Container getContentPane()
+ {
+ if (contentPane == null)
+ setContentPane(createContentPane());
+ return contentPane;
+ }
+
+ /**
+ * Sets the JRootPane's content pane. The content pane should typically be
+ * opaque for painting to work properly. This method also
+ * removes the old content pane from the layered pane.
+ *
+ * @param p the Container that will be the content pane
+ * @throws IllegalComponentStateException if p is null
+ */
+ public void setContentPane(Container p)
+ {
+ if (p == null)
+ throw new IllegalComponentStateException ("cannot " +
+ "have a null content pane");
+ else
+ {
+ if (contentPane != null && contentPane.getParent() == layeredPane)
+ layeredPane.remove(contentPane);
+ contentPane = p;
+ getLayeredPane().add(contentPane, JLayeredPane.FRAME_CONTENT_LAYER);
+ }
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param comp DOCUMENT ME!
+ * @param constraints DOCUMENT ME!
+ * @param index DOCUMENT ME!
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ super.addImpl(comp, constraints, index);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public Component getGlassPane()
+ {
+ if (glassPane == null)
+ setGlassPane(createGlassPane());
+ return glassPane;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param f DOCUMENT ME!
+ */
+ public void setGlassPane(Component f)
+ {
+ if (glassPane != null)
+ remove(glassPane);
+
+ glassPane = f;
+
+ glassPane.setVisible(false);
+ add(glassPane, 0);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public JLayeredPane getLayeredPane()
+ {
+ if (layeredPane == null)
+ setLayeredPane(createLayeredPane());
+ return layeredPane;
+ }
+
+ /**
+ * Set the layered pane for the root pane.
+ *
+ * @param f The JLayeredPane to be used.
+ *
+ * @throws IllegalComponentStateException if JLayeredPane
+ * parameter is null.
+ */
+ public void setLayeredPane(JLayeredPane f)
+ {
+ if (f == null)
+ throw new IllegalComponentStateException();
+
+ if (layeredPane != null)
+ remove(layeredPane);
+
+ layeredPane = f;
+ add(f, -1);
+ }
+
+ /**
+ * Creates a new JRootPane object.
+ */
+ public JRootPane()
+ {
+ setLayout(createRootLayout());
+ getGlassPane();
+ getLayeredPane();
+ getContentPane();
+ setOpaque(true);
+ updateUI();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected LayoutManager createRootLayout()
+ {
+ return new RootLayout();
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Container createContentPane()
+ {
+ JPanel p = new JPanel();
+ p.setName(this.getName() + ".contentPane");
+ p.setLayout(new BorderLayout());
+ return p;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected Component createGlassPane()
+ {
+ JPanel p = new JPanel();
+ p.setName(this.getName() + ".glassPane");
+ p.setVisible(false);
+ p.setOpaque(false);
+ return p;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ protected JLayeredPane createLayeredPane()
+ {
+ JLayeredPane l = new JLayeredPane();
+ l.setLayout(null);
+ return l;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public RootPaneUI getUI()
+ {
+ return (RootPaneUI) ui;
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param ui DOCUMENT ME!
+ */
+ public void setUI(RootPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ public void updateUI()
+ {
+ setUI((RootPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @return DOCUMENT ME!
+ */
+ public String getUIClassID()
+ {
+ return "RootPaneUI";
+ }
+
+ public JButton getDefaultButton()
+ {
+ return defaultButton;
+ }
+
+ public void setDefaultButton(JButton newButton)
+ {
+ // We only change the default button if the new button is defaultCapable
+ // or null and the old and new button are different objects.
+ if (defaultButton != newButton
+ && (newButton == null || newButton.isDefaultCapable()))
+ {
+ JButton oldButton = defaultButton;
+ defaultButton = newButton;
+ firePropertyChange("defaultButton", oldButton, newButton);
+ }
+ }
+
+ /**
+ * @since 1.4
+ */
+ public int getWindowDecorationStyle()
+ {
+ return windowDecorationStyle;
+ }
+
+ /**
+ * @since 1.4
+ */
+ public void setWindowDecorationStyle(int style)
+ {
+ if (style != NONE
+ && style != FRAME
+ && style != INFORMATION_DIALOG
+ && style != ERROR_DIALOG
+ && style != COLOR_CHOOSER_DIALOG
+ && style != FILE_CHOOSER_DIALOG
+ && style != QUESTION_DIALOG
+ && style != WARNING_DIALOG
+ && style != PLAIN_DIALOG)
+ throw new IllegalArgumentException("invalid style");
+
+ int oldStyle = windowDecorationStyle;
+ windowDecorationStyle = style;
+ firePropertyChange("windowDecorationStyle", oldStyle, style);
+ }
+
+ /**
+ * This returns true if the glassPane is not
+ * visible because then the root pane can guarantee to tile its children
+ * (the only other direct child is a JLayeredPane which must figure its
+ * optimizeDrawingEnabled state on its own).
+ *
+ * @return true if the glassPane is not
+ * visible
+ */
+ public boolean isOptimizedDrawingEnable()
+ {
+ return ! glassPane.isVisible();
+ }
+
+ /**
+ * Returns the accessible context for this JRootPane. This will be
+ * an instance of {@link AccessibleJRootPane}.
+ *
+ * @return the accessible context for this JRootPane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJRootPane();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JScrollBar.java b/libjava/classpath/javax/swing/JScrollBar.java
new file mode 100644
index 000000000..a60031670
--- /dev/null
+++ b/libjava/classpath/javax/swing/JScrollBar.java
@@ -0,0 +1,700 @@
+/* JScrollBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Adjustable;
+import java.awt.Dimension;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ScrollBarUI;
+
+/**
+ * The JScrollBar. Two buttons control how the values that the
+ * scroll bar can take. You can also drag the thumb or click the track
+ * to move the scroll bar. Typically, the JScrollBar is used with
+ * other components to translate the value of the bar to the viewable
+ * contents of the other components.
+ */
+public class JScrollBar extends JComponent implements Adjustable, Accessible
+{
+ /**
+ * Provides the accessibility features for the JScrollBar
+ * component.
+ */
+ protected class AccessibleJScrollBar extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -7758162392045586663L;
+
+ /**
+ * Creates a new AccessibleJScrollBar instance.
+ */
+ protected AccessibleJScrollBar()
+ {
+ super();
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JScrollBar}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JScrollBar.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JScrollBar.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the JScrollBar component.
+ *
+ * @return {@link AccessibleRole#SCROLL_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SCROLL_BAR;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JScrollBar} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JScrollBar} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is null, this method
+ * does nothing and returns false.
+ *
+ * @param value the new slider value (null permitted).
+ *
+ * @return true if the slider value is updated, and
+ * false otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JScrollBar} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JScrollBar} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JScrollBar} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum() - model.getExtent());
+ }
+ }
+
+ /**
+ * Listens for changes on the model and fires them to interested
+ * listeners on the JScrollBar, after re-sourcing them.
+ */
+ private class ScrollBarChangeListener
+ implements ChangeListener
+ {
+
+ public void stateChanged(ChangeEvent event)
+ {
+ Object o = event.getSource();
+ if (o instanceof BoundedRangeModel)
+ {
+ BoundedRangeModel m = (BoundedRangeModel) o;
+ fireAdjustmentValueChanged(AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED,
+ AdjustmentEvent.TRACK, m.getValue(),
+ m.getValueIsAdjusting());
+ }
+ }
+
+ }
+
+ private static final long serialVersionUID = -8195169869225066566L;
+
+ /** How much the thumb moves when moving in a block. */
+ protected int blockIncrement = 10;
+
+ /** The model that holds the scroll bar's data. */
+ protected BoundedRangeModel model;
+
+ /** The orientation of the scroll bar. */
+ protected int orientation = SwingConstants.VERTICAL;
+
+ /** How much the thumb moves when moving in a unit. */
+ protected int unitIncrement = 1;
+
+ /**
+ * This ChangeListener forwards events fired from the model and re-sources
+ * them to originate from this JScrollBar.
+ */
+ private ChangeListener sbChangeListener;
+
+ /**
+ * Creates a new horizontal JScrollBar object with a minimum
+ * of 0, a maxmium of 100, a value of 0 and an extent of 10.
+ */
+ public JScrollBar()
+ {
+ this(SwingConstants.VERTICAL, 0, 10, 0, 100);
+ }
+
+ /**
+ * Creates a new JScrollBar object with a minimum of 0, a
+ * maximum of 100, a value of 0, an extent of 10 and the given
+ * orientation.
+ *
+ * @param orientation The orientation of the JScrollBar.
+ */
+ public JScrollBar(int orientation)
+ {
+ this(orientation, 0, 10, 0, 100);
+ }
+
+ /**
+ * Creates a new JScrollBar object with the given orientation,
+ * value, min, max, and extent.
+ *
+ * @param orientation The orientation to use.
+ * @param value The value to use.
+ * @param extent The extent to use.
+ * @param min The minimum value of the scrollbar.
+ * @param max The maximum value of the scrollbar.
+ */
+ public JScrollBar(int orientation, int value, int extent, int min, int max)
+ {
+ model = new DefaultBoundedRangeModel(value, extent, min, max);
+ sbChangeListener = new ScrollBarChangeListener();
+ model.addChangeListener(sbChangeListener);
+ if (orientation != SwingConstants.HORIZONTAL
+ && orientation != SwingConstants.VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ updateUI();
+ }
+
+ /**
+ * This method sets the UI of this scrollbar to
+ * the given UI.
+ *
+ * @param ui The UI to use with this scrollbar.
+ */
+ public void setUI(ScrollBarUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method returns the UI that is being used
+ * with this scrollbar.
+ *
+ * @return The scrollbar's current UI.
+ */
+ public ScrollBarUI getUI()
+ {
+ return (ScrollBarUI) ui;
+ }
+
+ /**
+ * This method changes the UI to be the
+ * default for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ScrollBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns an identifier to
+ * choose the correct UI delegate for the
+ * scrollbar.
+ *
+ * @return The identifer to choose the UI delegate; "ScrollBarUI"
+ */
+ public String getUIClassID()
+ {
+ return "ScrollBarUI";
+ }
+
+ /**
+ * This method returns the orientation of the scrollbar.
+ *
+ * @return The orientation of the scrollbar.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * This method sets the orientation of the scrollbar.
+ *
+ * @param orientation The orientation of the scrollbar.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != SwingConstants.HORIZONTAL
+ && orientation != SwingConstants.VERTICAL)
+ throw new IllegalArgumentException("orientation must be one of HORIZONTAL or VERTICAL");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation,
+ this.orientation);
+ }
+ }
+
+ /**
+ * This method returns the model being used with
+ * the scrollbar.
+ *
+ * @return The scrollbar's model.
+ */
+ public BoundedRangeModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * This method sets the model to use with
+ * the scrollbar.
+ *
+ * @param newModel The new model to use with the scrollbar.
+ */
+ public void setModel(BoundedRangeModel newModel)
+ {
+ BoundedRangeModel oldModel = model;
+ if (oldModel != null)
+ oldModel.removeChangeListener(sbChangeListener);
+ model = newModel;
+ if (model != null)
+ model.addChangeListener(sbChangeListener);
+ firePropertyChange("model", oldModel, model);
+ }
+
+ /**
+ * This method returns how much the scrollbar's value
+ * should change for a unit increment depending on the
+ * given direction.
+ *
+ * @param direction The direction to scroll in.
+ *
+ * @return The amount the scrollbar's value will change given the direction.
+ */
+ public int getUnitIncrement(int direction)
+ {
+ return unitIncrement;
+ }
+
+ /**
+ * This method sets the unitIncrement property.
+ *
+ * @param unitIncrement The new unitIncrement.
+ */
+ public void setUnitIncrement(int unitIncrement)
+ {
+ if (unitIncrement != this.unitIncrement)
+ {
+ int oldInc = this.unitIncrement;
+ this.unitIncrement = unitIncrement;
+ firePropertyChange("unitIncrement", oldInc,
+ this.unitIncrement);
+ }
+ }
+
+ /**
+ * The method returns how much the scrollbar's value
+ * should change for a block increment depending on
+ * the given direction.
+ *
+ * @param direction The direction to scroll in.
+ *
+ * @return The amount the scrollbar's value will change given the direction.
+ */
+ public int getBlockIncrement(int direction)
+ {
+ return blockIncrement;
+ }
+
+ /**
+ * This method sets the blockIncrement property.
+ *
+ * @param blockIncrement The new blockIncrement.
+ */
+ public void setBlockIncrement(int blockIncrement)
+ {
+ if (blockIncrement != this.blockIncrement)
+ {
+ int oldInc = this.blockIncrement;
+ this.blockIncrement = blockIncrement;
+ firePropertyChange("blockIncrement", oldInc,
+ this.blockIncrement);
+ }
+ }
+
+ /**
+ * This method returns the unitIncrement.
+ *
+ * @return The unitIncrement.
+ */
+ public int getUnitIncrement()
+ {
+ return unitIncrement;
+ }
+
+ /**
+ * This method returns the blockIncrement.
+ *
+ * @return The blockIncrement.
+ */
+ public int getBlockIncrement()
+ {
+ return blockIncrement;
+ }
+
+ /**
+ * This method returns the value of the scrollbar.
+ *
+ * @return The value of the scrollbar.
+ */
+ public int getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * This method changes the value of the scrollbar.
+ *
+ * @param value The new value of the scrollbar.
+ */
+ public void setValue(int value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * This method returns the visible amount (AKA extent).
+ * The visible amount can be used by UI delegates to
+ * determine the size of the thumb.
+ *
+ * @return The visible amount (AKA extent).
+ */
+ public int getVisibleAmount()
+ {
+ return model.getExtent();
+ }
+
+ /**
+ * This method sets the visible amount (AKA extent).
+ *
+ * @param extent The visible amount (AKA extent).
+ */
+ public void setVisibleAmount(int extent)
+ {
+ model.setExtent(extent);
+ }
+
+ /**
+ * This method returns the minimum value of the scrollbar.
+ *
+ * @return The minimum value of the scrollbar.
+ */
+ public int getMinimum()
+ {
+ return model.getMinimum();
+ }
+
+ /**
+ * This method sets the minimum value of the scrollbar.
+ *
+ * @param minimum The minimum value of the scrollbar.
+ */
+ public void setMinimum(int minimum)
+ {
+ model.setMinimum(minimum);
+ }
+
+ /**
+ * This method returns the maximum value of the scrollbar.
+ *
+ * @return The maximum value of the scrollbar.
+ */
+ public int getMaximum()
+ {
+ return model.getMaximum();
+ }
+
+ /**
+ * This method sets the maximum value of the scrollbar.
+ *
+ * @param maximum The maximum value of the scrollbar.
+ */
+ public void setMaximum(int maximum)
+ {
+ model.setMaximum(maximum);
+ }
+
+ /**
+ * This method returns the model's isAjusting value.
+ *
+ * @return The model's isAdjusting value.
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return model.getValueIsAdjusting();
+ }
+
+ /**
+ * This method sets the model's isAdjusting value.
+ *
+ * @param b The new isAdjusting value.
+ */
+ public void setValueIsAdjusting(boolean b)
+ {
+ model.setValueIsAdjusting(b);
+ }
+
+ /**
+ * This method sets the value, extent, minimum and
+ * maximum.
+ *
+ * @param newValue The new value.
+ * @param newExtent The new extent.
+ * @param newMin The new minimum.
+ * @param newMax The new maximum.
+ */
+ public void setValues(int newValue, int newExtent, int newMin, int newMax)
+ {
+ model.setRangeProperties(newValue, newExtent, newMin, newMax,
+ model.getValueIsAdjusting());
+ }
+
+ /**
+ * This method adds an AdjustmentListener to the scroll bar.
+ *
+ * @param listener The listener to add.
+ */
+ public void addAdjustmentListener(AdjustmentListener listener)
+ {
+ listenerList.add(AdjustmentListener.class, listener);
+ }
+
+ /**
+ * This method removes an AdjustmentListener from the scroll bar.
+ *
+ * @param listener The listener to remove.
+ */
+ public void removeAdjustmentListener(AdjustmentListener listener)
+ {
+ listenerList.remove(AdjustmentListener.class, listener);
+ }
+
+ /**
+ * This method returns an arry of all AdjustmentListeners listening to
+ * this scroll bar.
+ *
+ * @return An array of AdjustmentListeners listening to this scroll bar.
+ */
+ public AdjustmentListener[] getAdjustmentListeners()
+ {
+ return (AdjustmentListener[]) listenerList.getListeners(AdjustmentListener.class);
+ }
+
+ /**
+ * This method is called to fired AdjustmentEvents to the listeners
+ * of this scroll bar. All AdjustmentEvents that are fired
+ * will have an ID of ADJUSTMENT_VALUE_CHANGED and a type of
+ * TRACK.
+ *
+ * @param id The ID of the adjustment event.
+ * @param type The Type of change.
+ * @param value The new value for the property that was changed..
+ */
+ protected void fireAdjustmentValueChanged(int id, int type, int value)
+ {
+ fireAdjustmentValueChanged(id, type, value, getValueIsAdjusting());
+ }
+
+ /**
+ * Helper method for firing adjustment events that can have their
+ * isAdjusting field modified.
+ *
+ * This is package private to avoid an accessor method.
+ *
+ * @param id the ID of the event
+ * @param type the type of the event
+ * @param value the value
+ * @param isAdjusting if the scrollbar is adjusting or not
+ */
+ void fireAdjustmentValueChanged(int id, int type, int value,
+ boolean isAdjusting)
+ {
+ Object[] adjustmentListeners = listenerList.getListenerList();
+ AdjustmentEvent adjustmentEvent = new AdjustmentEvent(this, id, type,
+ value, isAdjusting);
+ for (int i = adjustmentListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (adjustmentListeners[i] == AdjustmentListener.class)
+ ((AdjustmentListener) adjustmentListeners[i + 1]).adjustmentValueChanged(adjustmentEvent);
+ }
+ }
+
+ /**
+ * This method returns the minimum size for this scroll bar.
+ *
+ * @return The minimum size.
+ */
+ public Dimension getMinimumSize()
+ {
+ return ui.getMinimumSize(this);
+ }
+
+ /**
+ * This method returns the maximum size for this scroll bar.
+ *
+ * @return The maximum size.
+ */
+ public Dimension getMaximumSize()
+ {
+ return ui.getMaximumSize(this);
+ }
+
+ /**
+ * This method overrides the setEnabled in JComponent.
+ * When the scroll bar is disabled, the knob cannot
+ * be moved.
+ *
+ * @param x Whether the scrollbar is enabled.
+ */
+ public void setEnabled(boolean x)
+ {
+ // nothing special needs to be done here since we
+ // just check the enabled setting before changing the value.
+ super.setEnabled(x);
+ }
+
+ /**
+ * Returns a string describing the attributes for the JScrollBar
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JScrollBar.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",blockIncrement=").append(blockIncrement);
+ sb.append(",orientation=");
+ if (this.orientation == JScrollBar.HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",unitIncrement=").append(unitIncrement);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JScrollBar component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJScrollBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJScrollBar();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JScrollPane.java b/libjava/classpath/javax/swing/JScrollPane.java
new file mode 100644
index 000000000..6d17857d3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JScrollPane.java
@@ -0,0 +1,712 @@
+/* JScrollPane.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.ComponentOrientation;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ScrollPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A component that embeds another component and enables it to be scrolled
+ * both in horizontal and vertical direction.
+ *
+ *
+ *
Property
Stored in
Bound?
+ *
columnHeader
scrollPane
yes
+ *
columnHeaderView
columnHeader
no
+ *
componentOrientation
scrollPane
yes
+ *
horizontalScrollBar
scrollPane
yes
+ *
horizontalScrollBarPolicy
scrollPane
yes
+ *
layout
scrollPane
yes
+ *
rowHeader
scrollPane
yes
+ *
rowHeaderView
rowHeader
no
+ *
validateRoot
scrollPane
no
+ *
verticalScrollBar
scrollPane
yes
+ *
verticalScrollBarPolicy
scrollPane
yes
+ *
viewport
scrollPane
yes
+ *
viewportBorder
scrollPane
yes
+ *
viewportBorderBounds
scrollPane
no
+ *
viewportView
viewport
no
+ *
wheelScrollingEnabled
scrollPane
yes
+ *
+ */
+public class JScrollPane extends JComponent
+ implements Accessible, ScrollPaneConstants
+{
+ /**
+ * Provides accessibility support for the JScrollPane.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJScrollPane extends AccessibleJComponent
+ implements ChangeListener, PropertyChangeListener
+ {
+
+ /**
+ * The viewport of the underlying scrollpane.
+ */
+ protected JViewport viewPort;
+
+ /**
+ * Creates a new AccessibleJScrollPane object. This
+ * initializes the viewport field with the current viewport
+ * from the scrollpane associated with this
+ * AccessibleJScrollPane.
+ */
+ public AccessibleJScrollPane()
+ {
+ viewPort = getViewport();
+ viewPort.addChangeListener(this);
+ viewPort.addPropertyChangeListener(this);
+ }
+
+ /**
+ * Receives notification when the state of the viewport changes.
+ *
+ * @param event the change event
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ // TODO: Figure out what should be done here, if anything.
+ }
+
+ /**
+ * Receives notification if any of the viewport's bound properties changes.
+ *
+ * @param e the propery change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ // TODO: Figure out what should be done here, if anything.
+ }
+
+ /**
+ * Resets the viewPort field when the scrollpane's viewport
+ * changes. This method is called by
+ * {@link JScrollPane#setViewport(JViewport)} in order to update the
+ * viewPort field and set up the listeners on this viewport
+ * correctly.
+ */
+ public void resetViewPort()
+ {
+ viewPort.removeChangeListener(this);
+ viewPort.removePropertyChangeListener(this);
+ viewPort = getViewport();
+ viewPort.addChangeListener(this);
+ viewPort.addPropertyChangeListener(this);
+ }
+ }
+
+ private static final long serialVersionUID = 5203525440012340014L;
+
+ protected JViewport columnHeader;
+ protected JViewport rowHeader;
+
+ protected Component lowerLeft;
+ protected Component lowerRight;
+ protected Component upperLeft;
+ protected Component upperRight;
+
+ protected JScrollBar horizontalScrollBar;
+ protected int horizontalScrollBarPolicy;
+ protected JScrollBar verticalScrollBar;
+ protected int verticalScrollBarPolicy;
+
+ protected JViewport viewport;
+
+ private Border viewportBorder;
+
+ private boolean wheelScrollingEnabled;
+
+ public JViewport getColumnHeader()
+ {
+ return columnHeader;
+ }
+
+ public Component getCorner(String key)
+ {
+ if (getComponentOrientation()
+ == ComponentOrientation.LEFT_TO_RIGHT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ }
+ else if (getComponentOrientation()
+ == ComponentOrientation.RIGHT_TO_LEFT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ }
+
+ if (key == LOWER_RIGHT_CORNER)
+ return lowerRight;
+ else if (key == UPPER_RIGHT_CORNER)
+ return upperRight;
+ else if (key == LOWER_LEFT_CORNER)
+ return lowerLeft;
+ else if (key == UPPER_LEFT_CORNER)
+ return upperLeft;
+ return null;
+ }
+
+ public JScrollBar getHorizontalScrollBar()
+ {
+ return horizontalScrollBar;
+ }
+
+ public int getHorizontalScrollBarPolicy()
+ {
+ return horizontalScrollBarPolicy;
+ }
+
+ public JViewport getRowHeader()
+ {
+ return rowHeader;
+ }
+
+ public JScrollBar getVerticalScrollBar()
+ {
+ return verticalScrollBar;
+ }
+
+ public int getVerticalScrollBarPolicy()
+ {
+ return verticalScrollBarPolicy;
+ }
+
+ public JViewport getViewport()
+ {
+ return viewport;
+ }
+
+ public Border getViewportBorder()
+ {
+ return viewportBorder;
+ }
+
+ public Rectangle getViewportBorderBounds()
+ {
+ if (viewportBorder == null)
+ {
+ if (getViewport() == null)
+ return new Rectangle(0, 0, 0, 0);
+ else
+ return getViewport().getBounds();
+ }
+ else
+ {
+ Insets i = viewportBorder.getBorderInsets(getViewport());
+ if (getViewport() == null)
+ return new Rectangle(0, 0, i.left + i.right, i.top + i.bottom);
+ else
+ {
+ Rectangle b = getViewport().getBounds();
+ return new Rectangle(b.x - i.left,
+ b.y - i.top,
+ b.width + i.left + i.right,
+ b.height + i.top + i.bottom);
+ }
+ }
+ }
+
+ public boolean isWheelScrollingEnabled()
+ {
+ return wheelScrollingEnabled;
+ }
+
+
+
+ private void sync()
+ {
+ LayoutManager m = super.getLayout();
+ if (m != null && m instanceof ScrollPaneLayout)
+ {
+ ScrollPaneLayout sl = (ScrollPaneLayout) m;
+ sl.syncWithScrollPane(this);
+ }
+ }
+
+ private void removeNonNull(Component c)
+ {
+ if (c != null)
+ remove(c);
+ }
+
+ private void addNonNull(Component c, Object constraints)
+ {
+ if (c != null)
+ add(c, constraints);
+ }
+
+ public void setComponentOrientation(ComponentOrientation co)
+ {
+ ComponentOrientation old = super.getComponentOrientation();
+ super.setComponentOrientation(co);
+ firePropertyChange("componentOrientation", old, co);
+ sync();
+ }
+
+ public void setColumnHeader(JViewport h)
+ {
+ if (columnHeader == h)
+ return;
+
+ JViewport old = columnHeader;
+ removeNonNull(old);
+ columnHeader = h;
+ addNonNull(h, JScrollPane.COLUMN_HEADER);
+ firePropertyChange("columnHeader", old, h);
+ sync();
+ }
+
+ public void setColumnHeaderView(Component c)
+ {
+ if (columnHeader == null)
+ setColumnHeader(createViewport());
+ columnHeader.setView(c);
+ sync();
+ }
+
+ public void setCorner(String key, Component c)
+ {
+ if (getComponentOrientation()
+ == ComponentOrientation.LEFT_TO_RIGHT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ }
+ else if (getComponentOrientation()
+ == ComponentOrientation.RIGHT_TO_LEFT)
+ {
+ if (key == LOWER_LEADING_CORNER)
+ key = LOWER_RIGHT_CORNER;
+ else if (key == LOWER_TRAILING_CORNER)
+ key = LOWER_LEFT_CORNER;
+ else if (key == UPPER_LEADING_CORNER)
+ key = UPPER_RIGHT_CORNER;
+ else if (key == UPPER_TRAILING_CORNER)
+ key = UPPER_LEFT_CORNER;
+ }
+
+ if (key == LOWER_RIGHT_CORNER)
+ {
+ removeNonNull(lowerRight);
+ lowerRight = c;
+ addNonNull(c, JScrollPane.LOWER_RIGHT_CORNER);
+ }
+ else if (key == UPPER_RIGHT_CORNER)
+ {
+ removeNonNull(upperRight);
+ upperRight = c;
+ addNonNull(c, JScrollPane.UPPER_RIGHT_CORNER);
+ }
+ else if (key == LOWER_LEFT_CORNER)
+ {
+ removeNonNull(lowerLeft);
+ lowerLeft = c;
+ addNonNull(c, JScrollPane.LOWER_LEFT_CORNER);
+ }
+ else if (key == UPPER_LEFT_CORNER)
+ {
+ removeNonNull(upperLeft);
+ upperLeft = c;
+ addNonNull(c, JScrollPane.UPPER_LEFT_CORNER);
+ }
+ else
+ throw new IllegalArgumentException("unknown corner " + key);
+ sync();
+ }
+
+ public void setHorizontalScrollBar(JScrollBar h)
+ {
+ if (horizontalScrollBar == h)
+ return;
+
+ JScrollBar old = horizontalScrollBar;
+ removeNonNull(old);
+ horizontalScrollBar = h;
+ addNonNull(h, JScrollPane.HORIZONTAL_SCROLLBAR);
+ firePropertyChange("horizontalScrollBar", old, h);
+ sync();
+
+ }
+
+ public void setHorizontalScrollBarPolicy(int h)
+ {
+ if (horizontalScrollBarPolicy == h)
+ return;
+
+ if (h != HORIZONTAL_SCROLLBAR_AS_NEEDED
+ && h != HORIZONTAL_SCROLLBAR_NEVER
+ && h != HORIZONTAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("unknown horizontal scrollbar policy");
+
+ int old = horizontalScrollBarPolicy;
+ horizontalScrollBarPolicy = h;
+ firePropertyChange("horizontalScrollBarPolicy", old, h);
+ sync();
+ revalidate();
+ }
+
+ public void setLayout(LayoutManager l)
+ {
+ LayoutManager old = super.getLayout();
+ ScrollPaneLayout tmp = (ScrollPaneLayout) l;
+ super.setLayout(l);
+ tmp.syncWithScrollPane(this);
+ firePropertyChange("layout", old, l);
+ sync();
+ }
+
+ public void setRowHeader(JViewport v)
+ {
+ if (rowHeader == v)
+ return;
+
+ JViewport old = rowHeader;
+ removeNonNull(old);
+ rowHeader = v;
+ addNonNull(v, JScrollPane.ROW_HEADER);
+ firePropertyChange("rowHeader", old, v);
+ sync();
+ }
+
+ public void setRowHeaderView(Component c)
+ {
+ if (rowHeader == null)
+ setRowHeader(createViewport());
+ rowHeader.setView(c);
+ sync();
+ }
+
+ public void setVerticalScrollBar(JScrollBar v)
+ {
+ if (verticalScrollBar == v)
+ return;
+
+ JScrollBar old = verticalScrollBar;
+ removeNonNull(old);
+ verticalScrollBar = v;
+ addNonNull(v, JScrollPane.VERTICAL_SCROLLBAR);
+ firePropertyChange("verticalScrollBar", old, v);
+ sync();
+ }
+
+ public void setVerticalScrollBarPolicy(int v)
+ {
+ if (verticalScrollBarPolicy == v)
+ return;
+
+ if (v != VERTICAL_SCROLLBAR_AS_NEEDED
+ && v != VERTICAL_SCROLLBAR_NEVER
+ && v != VERTICAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("unknown vertical scrollbar policy");
+
+ int old = verticalScrollBarPolicy;
+ verticalScrollBarPolicy = v;
+ firePropertyChange("verticalScrollBarPolicy", old, v);
+ sync();
+ revalidate();
+ }
+
+ public void setWheelScrollingEnabled(boolean b)
+ {
+ if (wheelScrollingEnabled == b)
+ return;
+
+ boolean old = wheelScrollingEnabled;
+ wheelScrollingEnabled = b;
+ firePropertyChange("wheelScrollingEnabled", old, b);
+ sync();
+ }
+
+ public void setViewport(JViewport v)
+ {
+ if (viewport == v)
+ return;
+
+ JViewport old = viewport;
+ removeNonNull(old);
+ viewport = v;
+ addNonNull(v, JScrollPane.VIEWPORT);
+ revalidate();
+ repaint();
+ firePropertyChange("viewport", old, v);
+ sync();
+ if (accessibleContext != null)
+ {
+ AccessibleJScrollPane asp = (AccessibleJScrollPane) accessibleContext;
+ asp.resetViewPort();
+ }
+ }
+
+ public void setViewportBorder(Border b)
+ {
+ if (viewportBorder == b)
+ return;
+
+ Border old = viewportBorder;
+ viewportBorder = b;
+ firePropertyChange("viewportBorder", old, b);
+ sync();
+ }
+
+ public void setViewportView(Component view)
+ {
+ if (getViewport() == null)
+ {
+ setViewport(createViewport());
+ }
+
+ if (view != null)
+ {
+ getViewport().setView(view);
+ }
+ sync();
+ }
+
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * Creates a new JScrollPane without a view. The scrollbar
+ * policy is set to {@link #VERTICAL_SCROLLBAR_AS_NEEDED} and
+ * {@link #HORIZONTAL_SCROLLBAR_AS_NEEDED}.
+ */
+ public JScrollPane()
+ {
+ this(null);
+ }
+
+ /**
+ * Creates a new JScrollPane that embeds the specified
+ * view component, displaying vertical and horizontal scrollbars
+ * as needed.
+ *
+ * @param view the component that is embedded inside the JScrollPane
+ */
+ public JScrollPane(Component view)
+ {
+ this(view,
+ VERTICAL_SCROLLBAR_AS_NEEDED,
+ HORIZONTAL_SCROLLBAR_AS_NEEDED);
+ }
+
+ /**
+ * Creates a new JScrollPane without a view; The scrollbar
+ * policies are set to vsbPolicy and hsbPolicy.
+ *
+ * @param vsbPolicy the vertical scrollbar policy to set
+ * @param hsbPolicy the vertical scrollbar policy to set
+ *
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
+ */
+ public JScrollPane(int vsbPolicy, int hsbPolicy)
+ {
+ this(null, vsbPolicy, hsbPolicy);
+ }
+
+ /**
+ * Creates a new JScrollPane that embeds the specified
+ * view component; The scrollbar
+ * policies are set to vsbPolicy and hsbPolicy.
+ *
+ * @param vsbPolicy the vertical scrollbar policy to set
+ * @param hsbPolicy the vertical scrollbar policy to set
+ *
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
+ * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
+ */
+ public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
+ {
+ wheelScrollingEnabled = true;
+ setVerticalScrollBarPolicy(vsbPolicy);
+ setVerticalScrollBar(createVerticalScrollBar());
+ setHorizontalScrollBarPolicy(hsbPolicy);
+ setHorizontalScrollBar(createHorizontalScrollBar());
+ viewport = createViewport();
+ if (view != null)
+ getViewport().setView(view);
+ add(viewport,0);
+ setLayout(new ScrollPaneLayout());
+ setOpaque(false);
+ updateUI();
+ }
+
+
+ public JScrollBar createHorizontalScrollBar()
+ {
+ return new ScrollBar(SwingConstants.HORIZONTAL);
+ }
+
+ public JScrollBar createVerticalScrollBar()
+ {
+ return new ScrollBar(SwingConstants.VERTICAL);
+ }
+
+ protected JViewport createViewport()
+ {
+ return new JViewport();
+ }
+
+ public String getUIClassID()
+ {
+ return "ScrollPaneUI";
+ }
+
+ public void updateUI()
+ {
+ setUI((ScrollPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the scrollpane's UI delegate.
+ *
+ * @return The scrollpane's UI delegate.
+ */
+ public ScrollPaneUI getUI()
+ {
+ return (ScrollPaneUI) ui;
+ }
+
+ /**
+ * This method sets the scrollpane's UI delegate.
+ *
+ * @param ui The scrollpane's UI delegate.
+ */
+ public void setUI(ScrollPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ protected class ScrollBar
+ extends JScrollBar
+ implements UIResource
+ {
+ private static final long serialVersionUID = -42032395320987283L;
+
+ public ScrollBar(int orientation)
+ {
+ super(orientation);
+ }
+
+ public int getBlockIncrement(int direction)
+ {
+ Component view = JScrollPane.this.getViewport().getView();
+ if (view == null || (! (view instanceof Scrollable)))
+ return super.getBlockIncrement(direction);
+ else
+ {
+ Scrollable s = (Scrollable) view;
+ return s.getScrollableBlockIncrement(JScrollPane.this.getViewport().getViewRect(),
+ this.getOrientation(),
+ direction);
+ }
+ }
+
+ public int getUnitIncrement(int direction)
+ {
+ Component view = JScrollPane.this.getViewport().getView();
+ if (view == null || (! (view instanceof Scrollable)))
+ return super.getUnitIncrement(direction);
+ else
+ {
+ Scrollable s = (Scrollable) view;
+ return s.getScrollableUnitIncrement(JScrollPane.this.getViewport().getViewRect(),
+ this.getOrientation(),
+ direction);
+ }
+ }
+ }
+
+ /**
+ * Returns the accessible context associated with this
+ * JScrollPane.
+ *
+ * @return the accessible context associated with this
+ * JScrollPane
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJScrollPane();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSeparator.java b/libjava/classpath/javax/swing/JSeparator.java
new file mode 100644
index 000000000..15d15fd8b
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSeparator.java
@@ -0,0 +1,218 @@
+/* JSeparator.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.SeparatorUI;
+
+/**
+ * The JSeparator. It is mostly used to divide/space out
+ * components.
+ */
+public class JSeparator extends JComponent implements SwingConstants,
+ Accessible
+{
+ /**
+ * Provides the accessibility features for the JSeparator
+ * component.
+ */
+ protected class AccessibleJSeparator extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = 916332890553201095L;
+
+ /**
+ * Creates a new AccessibleJSeparator instance.
+ */
+ protected AccessibleJSeparator()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role for the JSeparator component.
+ *
+ * @return {@link AccessibleRole#SEPARATOR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SEPARATOR;
+ }
+ }
+
+ private static final long serialVersionUID = 125301223445282357L;
+
+ /** The orientation of the JSeparator. */
+ private transient int orientation = HORIZONTAL;
+
+ /**
+ * Creates a new horizontal JSeparator object.
+ */
+ public JSeparator()
+ {
+ this(HORIZONTAL);
+ }
+
+ /**
+ * Creates a new JSeparator object with the given orientation.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not
+ * one of the specified values.
+ */
+ public JSeparator(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a valid orientation.");
+ this.orientation = orientation;
+ updateUI();
+ }
+
+ /**
+ * Returns the UI delegate being used with the JSeparator.
+ *
+ * @return The JSeparator's UI delegate.
+ */
+ public SeparatorUI getUI()
+ {
+ return (SeparatorUI) ui;
+ }
+
+ /**
+ * Sets the separator's UI delegate.
+ *
+ * @param ui the UI delegate.
+ */
+ public void setUI(SeparatorUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this separator's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((SeparatorUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix ("SeparatorUI" in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a JSeparator.
+ *
+ * @return "SeparatorUI".
+ */
+ public String getUIClassID()
+ {
+ return "SeparatorUI";
+ }
+
+ /**
+ * Returns the orientation of the JSeparator.
+ *
+ * @return The orientation (one of {@link #HORIZONTAL} and {@link #VERTICAL}).
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for the JSeparator and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * orientation) to all registered listeners.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not
+ * one of the specified values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a valid orientation.");
+ int old = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", old, orientation);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JSeparator.
+ *
+ * @return A string describing the attributes of this JSeparator
+ * (never null).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ if (orientation == HORIZONTAL)
+ return superParamStr + ",orientation=HORIZONTAL";
+ else
+ return superParamStr + ",orientation=VERTICAL";
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JSeparator component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJSeparator}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSeparator();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSlider.java b/libjava/classpath/javax/swing/JSlider.java
new file mode 100644
index 000000000..a6f51138d
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSlider.java
@@ -0,0 +1,1144 @@
+/* JSlider.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.MenuContainer;
+import java.awt.image.ImageObserver;
+import java.beans.PropertyChangeEvent;
+import java.io.Serializable;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.SliderUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * A visual component that allows selection of a value within a
+ * range by adjusting a thumb in a track. The values for the minimum,
+ * maximum, extent and value are stored in a {@link
+ * DefaultBoundedRangeModel}.
+ *
+ * A JSlider component has the following properties:
+ *
+ *
+ *
+ *
Property
Stored in
Bound?
+ *
extent
model
no
+ *
inverted
slider
yes
+ *
labelTable
slider
yes
+ *
majorTickSpacing
slider
yes
+ *
maximum
model
yes
+ *
minimum
model
yes
+ *
minorTickSpacing
slider
yes
+ *
model
slider
yes
+ *
orientation
slider
yes
+ *
paintLabels
slider
yes
+ *
paintTicks
slider
yes
+ *
snapToTicks
slider
yes
+ *
value
model
no
+ *
valueIsAdjusting
model
no
+ *
+ *
+ *
+ * The various behavioural aspects of these properties follows:
+ *
+ *
+ *
+ *
+ * When a non-bound property stored in the slider changes, the slider fires
+ * a {@link ChangeEvent} to its change listeners.
+ *
+ *
+ * When a bound property stored in the slider changes, the slider fires a
+ * {@link PropertyChangeEvent} to its property change listeners.
+ *
+ *
+ * If any of the model's properties change, it fires a {@link ChangeEvent} to
+ * its listeners, which include the slider.
+ *
+ *
+ * If the slider receives a {@link ChangeEvent} from its model, it will
+ * propagate the event to its own change listeners, with the event's "source"
+ * property set to refer to the slider, rather than the model.
+ *
+ *
+ */
+public class JSlider extends JComponent implements SwingConstants, Accessible,
+ ImageObserver,
+ MenuContainer, Serializable
+{
+
+ /**
+ * A little testing shows that the reference implementation creates
+ * labels from a class named LabelUIResource.
+ */
+ private class LabelUIResource
+ extends JLabel
+ implements UIResource
+ {
+ LabelUIResource(String text, int align)
+ {
+ super(text, align);
+ setName("Slider.label");
+ }
+ }
+
+ private static final long serialVersionUID = -1441275936141218479L;
+
+ /**
+ * Provides the accessibility features for the JSlider
+ * component.
+ */
+ protected class AccessibleJSlider extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -6301740148041106789L;
+
+ /**
+ * Creates a new AccessibleJSlider instance.
+ */
+ protected AccessibleJSlider()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JSlider}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (orientation == JSlider.HORIZONTAL)
+ result.add(AccessibleState.HORIZONTAL);
+ else if (orientation == JSlider.VERTICAL)
+ result.add(AccessibleState.VERTICAL);
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the JSlider component.
+ *
+ * @return {@link AccessibleRole#SLIDER}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SLIDER;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JSlider}. Since this class implements
+ * {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The current value of the {@link JSlider} component.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getValue());
+ }
+
+ /**
+ * Sets the current value of the {@link JSlider} component and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is null, this method
+ * does nothing and returns false.
+ *
+ * @param value the new slider value (null permitted).
+ *
+ * @return true if the slider value is updated, and
+ * false otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setValue(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(getValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The minimum value of the {@link JSlider} component.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimum());
+ }
+
+ /**
+ * Returns the maximum value of the {@link JSlider} component, as an
+ * {@link Integer}.
+ *
+ * @return The maximum value of the {@link JSlider} component.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximum());
+ }
+ }
+
+ /** Whether or not this slider paints its ticks. */
+ private transient boolean paintTicks;
+
+ /** Whether or not this slider paints its track. */
+ private transient boolean paintTrack = true;
+
+ /** Whether or not this slider paints its labels. */
+ private transient boolean paintLabels;
+
+ /**
+ * A dictionary of (Integer, Component) pairs where each Component is a
+ * JLabel and the Integer determines where the label will be painted.
+ */
+ private transient Dictionary labelTable;
+
+ /** The model used to store the slider's range and current value. */
+ protected BoundedRangeModel sliderModel;
+
+ /** The space/distance between major ticks. */
+ protected int majorTickSpacing;
+
+ /** The space/distance between minor ticks. */
+ protected int minorTickSpacing;
+
+ /** Whether the slider snaps its values to ticks. */
+ protected boolean snapToTicks;
+
+ /** The orientation (horizontal or vertical) of the slider. */
+ protected int orientation = HORIZONTAL;
+
+ /** Whether the slider is inverted. */
+ private transient boolean isInverted;
+
+ /**
+ * The listener that monitors the slider's model and forwards events to the
+ * slider's listeners (see createChangeListener()).
+ */
+ protected ChangeListener changeListener;
+
+ /** The change event that is passed to all listeners of this slider. */
+ protected transient ChangeEvent changeEvent;
+
+ /**
+ * Creates a new horizontal JSlider instance with a minimum of
+ * 0, a maximum of 100, and a value of 50.
+ */
+ public JSlider()
+ {
+ this(HORIZONTAL, 0, 100, 50);
+ }
+
+ /**
+ * Creates a new JSlider instance with the given orientation
+ * and a minimum of 0, a maximum of 100, and a value of 50.
+ *
+ * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the specified values.
+ */
+ public JSlider(int orientation)
+ {
+ this(orientation, 0, 100, 50);
+ }
+
+ /**
+ * Creates a new horizontal JSlider instance with the given
+ * maximum and minimum and a value that is halfway between the minimum and the
+ * maximum.
+ *
+ * @param minimum The minimum value.
+ * @param maximum The maximum value.
+ *
+ * @throws IllegalArgumentException if minimum is greater than
+ * maximum.
+ */
+ public JSlider(int minimum, int maximum)
+ {
+ this(HORIZONTAL, minimum, maximum, (maximum + minimum) / 2);
+ }
+
+ /**
+ * Creates a new horizontal JSlider instance with the given
+ * minimum, maximum, and value.
+ *
+ * @param minimum The minimum value.
+ * @param maximum The maximum value.
+ * @param value The initial value.
+ *
+ * @throws IllegalArgumentException if value is not in the
+ * specified range.
+ * @throws IllegalArgumentException if minimum is greater than
+ * maximum.
+ */
+ public JSlider(int minimum, int maximum, int value)
+ {
+ this(HORIZONTAL, minimum, maximum, value);
+ }
+
+ /**
+ * Creates a new JSlider instance with the given orientation,
+ * minimum, maximum, and value.
+ *
+ * @param orientation The orientation of the slider ({@link #HORIZONTAL} or
+ * {@link #VERTICAL}).
+ * @param minimum The minimum value of the JSlider.
+ * @param maximum The maximum value of the JSlider.
+ * @param value The initial value of the JSlider.
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the specified values.
+ * @throws IllegalArgumentException if value is not in the
+ * specified range.
+ * @throws IllegalArgumentException if minimum is greater than
+ * maximum.
+ */
+ public JSlider(int orientation, int minimum, int maximum, int value)
+ {
+ sliderModel = new DefaultBoundedRangeModel(value, 0, minimum, maximum);
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ this.orientation = orientation;
+ changeListener = createChangeListener();
+ sliderModel.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Creates a new horizontal JSlider instance with the given
+ * model.
+ *
+ * @param model The model (null not permitted).
+ *
+ * @throws NullPointerException if model is null.
+ */
+ public JSlider(BoundedRangeModel model)
+ {
+ sliderModel = model;
+ changeListener = createChangeListener();
+ sliderModel.addChangeListener(changeListener);
+ updateUI();
+ }
+
+ /**
+ * Returns the slider's value (from the slider's model).
+ *
+ * @return The value of the slider.
+ *
+ * @see #setValue(int)
+ */
+ public int getValue()
+ {
+ return sliderModel.getValue();
+ }
+
+ /**
+ * Sets the slider's value and sends a {@link ChangeEvent} to all
+ * registered listeners. Note that the model will fire a change event to all
+ * of its registered listeners first (with the model as the event source) and
+ * then the slider will fire another change event to all of its registered
+ * listeners (this time with the slider as the event source).
+ *
+ * @param value the new value.
+ *
+ * @see #getValue()
+ */
+ public void setValue(int value)
+ {
+ sliderModel.setValue(value);
+ }
+
+ /**
+ * Returns the slider's UI delegate.
+ *
+ * @return The slider's UI delegate.
+ */
+ public SliderUI getUI()
+ {
+ return (SliderUI) ui;
+ }
+
+ /**
+ * Sets the slider's UI delegate.
+ *
+ * @param ui the UI delegate.
+ */
+ public void setUI(SliderUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Sets this slider's UI delegate to the default (obtained from the
+ * {@link UIManager}) for the current look and feel.
+ */
+ public void updateUI()
+ {
+ updateLabelUIs();
+ setUI((SliderUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns the suffix ("SliderUI" in this case) used to
+ * determine the class name for a UI delegate that can provide the look and
+ * feel for a JSlider.
+ *
+ * @return "SliderUI".
+ */
+ public String getUIClassID()
+ {
+ return "SliderUI";
+ }
+
+ /**
+ * Creates a {@link ChangeListener} that is added to the slider's model and
+ * forwards change events generated by the model to the listeners that are
+ * registered with the JSlider (by calling the
+ * {@link #fireStateChanged} method).
+ *
+ * @return A new listener.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ChangeListener()
+ {
+ public void stateChanged(ChangeEvent ce)
+ {
+ // No need to trigger a repaint since the UI listens to the model
+ // as well. All we need to do is pass on the stateChanged event
+ // to our listeners.
+ fireStateChanged();
+ }
+ };
+ }
+
+ /**
+ * Registers a listener with the slider so that it will receive
+ * {@link ChangeEvent} notifications. Note that change events generated
+ * by the slider's model will be forwarded automatically to the slider's
+ * listeners.
+ *
+ * @param listener the listener to register.
+ *
+ * @see #removeChangeListener(ChangeListener)
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes a listener from this slider so that it will no longer receive
+ * {@link ChangeEvent} notifications from the slider.
+ *
+ * @param listener The listener to remove.
+ *
+ * @see #addChangeListener(ChangeListener)
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Sends a {@link ChangeEvent} to all registered listeners, with this slider
+ * as the source.
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * Returns an array containing all the {@link ChangeListener} instances
+ * registered with this slider. If no listeners are registered, this method
+ * returns an empty array.
+ *
+ * @return An array array containing all the {@link ChangeListener} instances
+ * registered with this slider (possibly empty, but never
+ * null).
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Returns the slider's model, which stores the minimum, maximum and current
+ * values.
+ *
+ * @return The slider's model.
+ *
+ * @see #setModel(BoundedRangeModel)
+ */
+ public BoundedRangeModel getModel()
+ {
+ return sliderModel;
+ }
+
+ /**
+ * Sets the slider's model and sends a {@link PropertyChangeEvent} (with the
+ * property name "model") to all registered listeners. The change listener
+ * that the slider registered with the original model is removed and added
+ * to the new model (this ensures that {@link ChangeEvent} notifications
+ * generated by the model are automatically forwarded to listeners that are
+ * registered with the slider).
+ *
+ * @param model The model to use with the slider.
+ *
+ * @see #getModel()
+ */
+ public void setModel(BoundedRangeModel model)
+ {
+ // I didn't do the null pointer check on purpose.
+ // If you try it with Sun's, it'll go ahead and set it to null
+ // and bork the next time it tries to access the model.
+ if (model != sliderModel)
+ {
+ BoundedRangeModel oldModel = sliderModel;
+ sliderModel = model;
+ oldModel.removeChangeListener(changeListener);
+ sliderModel.addChangeListener(changeListener);
+ firePropertyChange("model", oldModel, sliderModel);
+ }
+ }
+
+ /**
+ * Returns the minimum value of the slider (from the slider's model).
+ *
+ * @return The minimum value of the slider.
+ *
+ * @see #setMinimum(int)
+ */
+ public int getMinimum()
+ {
+ return sliderModel.getMinimum();
+ }
+
+ /**
+ * Sets the minimum value of the slider and fires a
+ * {@link PropertyChangeEvent} (with the property name "minimum") to all
+ * registered listeners. Note that:
+ *
+ *
+ *
the minimum value is stored in the slider's model (see
+ * {@link #getModel()});
+ *
in addition to the property change event, the slider also fires a
+ * {@link ChangeEvent}.
+ *
+ *
+ * @param minimum The minimum value of the slider.
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(int minimum)
+ {
+ int old = sliderModel.getMinimum();
+ sliderModel.setMinimum(minimum);
+ if (minimum != old)
+ firePropertyChange("minimum", old, minimum);
+ }
+
+ /**
+ * Returns the slider's maximum value (obtained from the slider's model).
+ *
+ * @return The maximum value of the slider.
+ *
+ * @see #setMaximum(int)
+ */
+ public int getMaximum()
+ {
+ return sliderModel.getMaximum();
+ }
+
+ /**
+ * Sets the maximum value of the slider and fires a
+ * {@link PropertyChangeEvent} (with the property name "maximum") to all
+ * registered listeners. Note that:
+ *
+ *
+ *
the maximum value is stored in the slider's model (see
+ * {@link #getModel()});
+ *
in addition to the property change event, the slider also fires a
+ * {@link ChangeEvent}.
+ *
+ *
+ * @param maximum The maximum value of the slider.
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(int maximum)
+ {
+ int old = sliderModel.getMaximum();
+ sliderModel.setMaximum(maximum);
+ if (maximum != old)
+ firePropertyChange("maximum", old, maximum);
+ }
+
+ /**
+ * Returns the valueIsAdjusting flag from the slider's model.
+ *
+ * @return The valueIsAdjusting flag from the slider's model.
+ *
+ * @see #setValueIsAdjusting(boolean)
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return sliderModel.getValueIsAdjusting();
+ }
+
+ /**
+ * Sets the valueIsAdjusting flag in the slider's model, and
+ * sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param adjusting the new flag value.
+ *
+ * @see #getValueIsAdjusting()
+ */
+ public void setValueIsAdjusting(boolean adjusting)
+ {
+ sliderModel.setValueIsAdjusting(adjusting);
+ }
+
+ /**
+ * Returns the slider's extent value, obtained from the slider's model.
+ *
+ * @return The extent value.
+ *
+ * @see #setExtent(int)
+ */
+ public int getExtent()
+ {
+ return sliderModel.getExtent();
+ }
+
+ /**
+ * Sets the slider's extent value and sends a {@link ChangeEvent} to all
+ * registered listeners. Note that the model will fire a change event to all
+ * of its registered listeners first (with the model as the event source) and
+ * then the slider will fire another change event to all of its registered
+ * listeners (this time with the slider as the event source).
+ *
+ * @param extent The extent value for this slider.
+ *
+ * @see #getExtent()
+ */
+ public void setExtent(int extent)
+ {
+ sliderModel.setExtent(extent);
+ }
+
+ /**
+ * Returns the orientation of the slider, either {@link JSlider#HORIZONTAL}
+ * or {@link JSlider#VERTICAL}.
+ *
+ * @return The orientation of the slider.
+ *
+ * @see #setOrientation(int)
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * Sets the orientation for the slider and sends a
+ * {@link PropertyChangeEvent} (with the property name "orientation") to all
+ * registered listeners.
+ *
+ * @param orientation the orientation (one of {@link JSlider#HORIZONTAL} or
+ * {@link JSlider#VERTICAL}).
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the permitted values.
+ *
+ * @see #getOrientation()
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != VERTICAL && orientation != HORIZONTAL)
+ throw new IllegalArgumentException(
+ "orientation must be one of: VERTICAL, HORIZONTAL");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ revalidate();
+ }
+ }
+
+ /**
+ * Returns the label table for the slider.
+ *
+ * @return The label table for the slider (possibly null).
+ *
+ * @see #setLabelTable(Dictionary)
+ */
+ public Dictionary getLabelTable()
+ {
+ return labelTable;
+ }
+
+ /**
+ * Sets the table of labels for the slider and sends a
+ * {@link PropertyChangeEvent} (with the property name "labelTable") to all
+ * registered listeners.
+ *
+ * @param table the table of labels (null permitted).
+ *
+ * @see #getLabelTable()
+ */
+ public void setLabelTable(Dictionary table)
+ {
+ if (table != labelTable)
+ {
+ Dictionary oldTable = labelTable;
+ labelTable = table;
+ updateLabelUIs();
+ firePropertyChange("labelTable", oldTable, labelTable);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Resets the UI delegates for the labels in the labelTable to
+ * the default for the current look and feel.
+ */
+ protected void updateLabelUIs()
+ {
+ if (labelTable != null)
+ {
+ for (Enumeration list = labelTable.elements(); list.hasMoreElements();)
+ {
+ Object o = list.nextElement();
+ if (o instanceof JComponent)
+ {
+ JComponent jc = (JComponent) o;
+ jc.updateUI();
+ jc.setSize(jc.getPreferredSize());
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a hashtable of (Integer, JLabel) pairs that can be
+ * used as a label table for this slider. The labels will start from the
+ * slider's minimum and increase by the increment. Each label will have a text
+ * string indicating its integer value.
+ *
+ * @param increment The increment between labels (must be > 0).
+ *
+ * @return A hashtable containing the labels.
+ *
+ * @throws IllegalArgumentException if increment is not greater
+ * than zero.
+ */
+ public Hashtable createStandardLabels(int increment)
+ {
+ return createStandardLabels(increment, sliderModel.getMinimum());
+ }
+
+ /**
+ * Creates a hashtable of (Integer, JLabel) pairs that can be
+ * used as a label table for this slider. The labels will start from the
+ * given start value and increase by the increment. Each label will have a
+ * text string indicating its integer value.
+ *
+ * @param increment The increment between labels (must be > 0).
+ * @param start The value to start from.
+ *
+ * @return A hashtable with the labels and their keys.
+ *
+ * @throws IllegalArgumentException if increment is not greater
+ * than zero, or start is not within the range of the
+ * model.
+ */
+ public Hashtable createStandardLabels(int increment, int start)
+ {
+ if (increment <= 0)
+ throw new IllegalArgumentException("Requires 'increment' > 0.");
+ if (start < getMinimum() || start > getMaximum())
+ throw new IllegalArgumentException("The 'start' value is out of range.");
+ Hashtable table = new Hashtable();
+ int max = getMaximum();
+ for (int i = start; i <= max; i += increment)
+ {
+ LabelUIResource label = new LabelUIResource(String.valueOf(i),
+ JLabel.CENTER);
+ table.put(new Integer(i), label);
+ }
+ return table;
+ }
+
+ /**
+ * Returns the flag that controls whether or not the value scale for the
+ * slider is inverted (the default value is false).
+ *
+ * @return The flag that controls whether or not the value scale for the
+ * slider is inverted.
+ *
+ * @see #setInverted(boolean)
+ */
+ public boolean getInverted()
+ {
+ return isInverted;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the value scale for the
+ * slider is inverted and, if the new flag value is different to the old flag
+ * value, sends a {@link PropertyChangeEvent} to all registered listeners.
+ * Typically, a horizontal slider will display a scale that increases from
+ * left to right, but this is reversed if the 'inverted' flag is set to
+ * true. Similarly, a vertical slider will display a scale that
+ * increases from bottom to top, and this is reversed if the 'inverted' flag
+ * is set to true.
+ *
+ * @param inverted the new flag value.
+ *
+ * @see #getInverted()
+ */
+ public void setInverted(boolean inverted)
+ {
+ if (isInverted != inverted)
+ {
+ boolean oldInverted = isInverted;
+ isInverted = inverted;
+ firePropertyChange("inverted", oldInverted, isInverted);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the distance between major tick marks along the slider's value
+ * scale.
+ *
+ * @return The amount of units between each major tick mark.
+ *
+ * @see #setMajorTickSpacing(int)
+ */
+ public int getMajorTickSpacing()
+ {
+ return majorTickSpacing;
+ }
+
+ /**
+ * Sets the distance between major tick marks along the slider's value scale,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * "majorTickSpacing") to all registered listeners.
+ *
+ * @param spacing the distance between major tick marks.
+ *
+ * @see #getMajorTickSpacing()
+ */
+ public void setMajorTickSpacing(int spacing)
+ {
+ if (majorTickSpacing != spacing)
+ {
+ int oldSpacing = majorTickSpacing;
+ majorTickSpacing = spacing;
+ if (labelTable == null && majorTickSpacing > 0 && getPaintLabels())
+ setLabelTable(createStandardLabels(majorTickSpacing));
+ firePropertyChange("majorTickSpacing", oldSpacing, majorTickSpacing);
+ if (getPaintTicks())
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the distance between minor tick marks along the slider's value
+ * scale.
+ *
+ * @return The distance between minor tick marks along the slider's value
+ * scale.
+ *
+ * @see #setMinorTickSpacing(int)
+ */
+ public int getMinorTickSpacing()
+ {
+ return minorTickSpacing;
+ }
+
+ /**
+ * Sets the distance between minor tick marks along the slider's value scale,
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * "minorTickSpacing") to all registered listeners.
+ *
+ * @param spacing the distance between minor tick marks.
+ *
+ * @see #getMinorTickSpacing()
+ */
+ public void setMinorTickSpacing(int spacing)
+ {
+ if (minorTickSpacing != spacing)
+ {
+ int oldSpacing = minorTickSpacing;
+ minorTickSpacing = spacing;
+ firePropertyChange("minorTickSpacing", oldSpacing, minorTickSpacing);
+ if (getPaintTicks())
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether the slider thumb will snap to ticks.
+ * Sliders that snap to ticks will automatically move the thumb to the
+ * nearest tick mark.
+ *
+ * @return true if the slider thumb automatically.
+ *
+ * @see #setSnapToTicks(boolean)
+ */
+ public boolean getSnapToTicks()
+ {
+ return snapToTicks;
+ }
+
+ /**
+ * Sets the flag that controls whether the slider thumb will snap to ticks
+ * and sends a {@link PropertyChangeEvent} (with the property name
+ * 'snapToTicks') to all registered listeners. Sliders that snap to ticks
+ * will automatically move the thumb to the nearest tick mark.
+ *
+ * @param snap the new flag value.
+ *
+ * @see #getSnapToTicks()
+ */
+ public void setSnapToTicks(boolean snap)
+ {
+ if (snap != snapToTicks)
+ {
+ snapToTicks = snap;
+ firePropertyChange("snapToTicks", !snap, snap);
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not tick marks are painted along
+ * the slider's value scale.
+ *
+ * @return true if tick marks should be painted, and
+ * false if tick marks should not be painted.
+ *
+ * @see #setPaintTicks(boolean)
+ */
+ public boolean getPaintTicks()
+ {
+ return paintTicks;
+ }
+
+ /**
+ * Sets the flag that controls whether or not tick marks are painted along
+ * the slider's value scale, and sends a {@link PropertyChangeEvent} (with
+ * the property name "paintTicks") to all registered listeners. In
+ * addition to setting this property to true, one or both of the
+ * minor tick spacing and major tick spacing attributes must be set to a
+ * value greater than 0 in order for ticks to be painted.
+ *
+ * @param paint Whether ticks will be painted.
+ *
+ * @see #getPaintTicks()
+ */
+ public void setPaintTicks(boolean paint)
+ {
+ if (paint != paintTicks)
+ {
+ boolean oldPaintTicks = paintTicks;
+ paintTicks = paint;
+ firePropertyChange("paintTicks", oldPaintTicks, paintTicks);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not the track is painted.
+ *
+ * @return Whether the track will be painted.
+ *
+ * @see #setPaintTrack(boolean)
+ */
+ public boolean getPaintTrack()
+ {
+ return paintTrack;
+ }
+
+ /**
+ * Sets the flag that controls whether or not the track is painted, and
+ * sends a {@link PropertyChangeEvent} (for the "paintTrack" property) to all
+ * registered listeners.
+ *
+ * @param paint Whether the track will be painted.
+ *
+ * @see #getPaintTrack()
+ */
+ public void setPaintTrack(boolean paint)
+ {
+ if (paintTrack != paint)
+ {
+ paintTrack = paint;
+ firePropertyChange("paintTrack", !paint, paint);
+ repaint();
+ }
+ }
+
+ /**
+ * Returns the flag that controls whether or not labels are painted for the
+ * tick marks along the slider.
+ *
+ * @return Whether labels will be painted.
+ *
+ * @see #setPaintLabels(boolean)
+ */
+ public boolean getPaintLabels()
+ {
+ return paintLabels;
+ }
+
+ /**
+ * Sets the flag that controls whether or not labels are painted for the
+ * tick marks along the slider and sends a {@link PropertyChangeEvent} (with
+ * the property name "paintLabels") to all registered listeners.
+ *
+ * @param paint Whether labels will be painted.
+ *
+ * @see #getPaintLabels()
+ */
+ public void setPaintLabels(boolean paint)
+ {
+ if (paint != paintLabels)
+ {
+ paintLabels = paint;
+ if (paint && majorTickSpacing > 0 && labelTable == null)
+ setLabelTable(createStandardLabels(majorTickSpacing));
+ firePropertyChange("paintLabels", !paint, paint);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JSlider.
+ *
+ * @return A string describing the attributes of this JSlider
+ * (never null).
+ */
+ protected String paramString()
+ {
+ String superParamStr = super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",isInverted=").append(getInverted());
+ sb.append(",majorTickSpacing=").append(getMajorTickSpacing());
+ sb.append(",minorTickSpacing=").append(getMinorTickSpacing());
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append("VERTICAL");
+ sb.append(",paintLabels=").append(getPaintLabels());
+ sb.append(",paintTicks=").append(getPaintTicks());
+ sb.append(",paintTrack=").append(getPaintTrack());
+ sb.append(",snapToTicks=").append(getSnapToTicks());
+
+ // the following is output by the reference implementation. We don't
+ // strictly need to replicate this. Perhaps it has some meaning, but
+ // I couldn't determine it yet...
+ sb.append(",snapToValue=true");
+
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JSlider component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJSlider}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSlider();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSpinner.java b/libjava/classpath/javax/swing/JSpinner.java
new file mode 100644
index 000000000..e942caead
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSpinner.java
@@ -0,0 +1,754 @@
+/* JSpinner.java --
+ Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.SpinnerUI;
+import javax.swing.text.DateFormatter;
+import javax.swing.text.DefaultFormatterFactory;
+import javax.swing.text.NumberFormatter;
+
+/**
+ * A JSpinner is a component that displays a single value from
+ * a sequence of values, and provides a convenient means for selecting the
+ * previous and next values in the sequence. Typically the spinner displays
+ * a numeric value, but it is possible to display dates or arbitrary items
+ * from a list.
+ *
+ * @author Ka-Hing Cheung
+ *
+ * @since 1.4
+ */
+public class JSpinner extends JComponent
+{
+ /**
+ * The base class for the editor used by the {@link JSpinner} component.
+ * The editor is in fact a panel containing a {@link JFormattedTextField}
+ * component.
+ */
+ public static class DefaultEditor
+ extends JPanel
+ implements ChangeListener, PropertyChangeListener, LayoutManager
+ {
+ /** The spinner that the editor is allocated to. */
+ private JSpinner spinner;
+
+ /** The JFormattedTextField that backs the editor. */
+ JFormattedTextField ftf;
+
+ /**
+ * For compatability with Sun's JDK 1.4.2 rev. 5
+ */
+ private static final long serialVersionUID = -5317788736173368172L;
+
+ /**
+ * Creates a new DefaultEditor object. The editor is
+ * registered with the spinner as a {@link ChangeListener} here.
+ *
+ * @param spinner the JSpinner associated with this editor
+ */
+ public DefaultEditor(JSpinner spinner)
+ {
+ super();
+ setLayout(this);
+ this.spinner = spinner;
+ ftf = new JFormattedTextField();
+ add(ftf);
+ ftf.setValue(spinner.getValue());
+ ftf.addPropertyChangeListener(this);
+ if (getComponentOrientation().isLeftToRight())
+ ftf.setHorizontalAlignment(JTextField.RIGHT);
+ else
+ ftf.setHorizontalAlignment(JTextField.LEFT);
+ spinner.addChangeListener(this);
+ }
+
+ /**
+ * Returns the JSpinner component that the editor is assigned
+ * to.
+ *
+ * @return The spinner that the editor is assigned to.
+ */
+ public JSpinner getSpinner()
+ {
+ return spinner;
+ }
+
+ /**
+ * DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ // TODO: Implement this properly.
+ }
+
+ /**
+ * Removes the editor from the {@link ChangeListener} list maintained by
+ * the specified spinner.
+ *
+ * @param spinner the spinner (null not permitted).
+ */
+ public void dismiss(JSpinner spinner)
+ {
+ spinner.removeChangeListener(this);
+ }
+
+ /**
+ * Returns the text field used to display and edit the current value in
+ * the spinner.
+ *
+ * @return The text field.
+ */
+ public JFormattedTextField getTextField()
+ {
+ return ftf;
+ }
+
+ /**
+ * Sets the bounds for the child components in this container. In this
+ * case, the text field is the only component to be laid out.
+ *
+ * @param parent the parent container.
+ */
+ public void layoutContainer(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension size = getSize();
+ ftf.setBounds(insets.left, insets.top,
+ size.width - insets.left - insets.right,
+ size.height - insets.top - insets.bottom);
+ }
+
+ /**
+ * Calculates the minimum size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the minimum
+ * size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The minimum size.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension minSize = ftf.getMinimumSize();
+ return new Dimension(minSize.width + insets.left + insets.right,
+ minSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Calculates the preferred size for this component. In this case, the
+ * text field is the only subcomponent, so the return value is the
+ * preferred size of the text field plus the insets of this component.
+ *
+ * @param parent the parent container.
+ *
+ * @return The preferred size.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ Insets insets = getInsets();
+ Dimension prefSize = ftf.getPreferredSize();
+ return new Dimension(prefSize.width + insets.left + insets.right,
+ prefSize.height + insets.top + insets.bottom);
+ }
+
+ /**
+ * Receives notification of property changes. If the text field's 'value'
+ * property changes, the spinner's model is updated accordingly.
+ *
+ * @param event the event.
+ */
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ if (event.getSource() == ftf)
+ {
+ if (event.getPropertyName().equals("value"))
+ spinner.getModel().setValue(event.getNewValue());
+ }
+ }
+
+ /**
+ * Receives notification of changes in the state of the {@link JSpinner}
+ * that the editor belongs to - the content of the text field is updated
+ * accordingly.
+ *
+ * @param event the change event.
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ ftf.setValue(spinner.getValue());
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param child the child component to remove.
+ */
+ public void removeLayoutComponent(Component child)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method does nothing. It is required by the {@link LayoutManager}
+ * interface, but since this component has a single child, there is no
+ * need to use this method.
+ *
+ * @param name the name.
+ * @param child the child component to add.
+ */
+ public void addLayoutComponent(String name, Component child)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * A panel containing a {@link JFormattedTextField} that is configured for
+ * displaying and editing numbers. The panel is used as a subcomponent of
+ * a {@link JSpinner}.
+ *
+ * @see JSpinner#createEditor(SpinnerModel)
+ */
+ public static class NumberEditor extends DefaultEditor
+ {
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 3791956183098282942L;
+
+ /**
+ * Creates a new NumberEditor object for the specified
+ * spinner. The editor is registered with the spinner as a
+ * {@link ChangeListener}.
+ *
+ * @param spinner the component the editor will be used with.
+ */
+ public NumberEditor(JSpinner spinner)
+ {
+ super(spinner);
+ NumberEditorFormatter nef = new NumberEditorFormatter();
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new NumberEditor object.
+ *
+ * @param spinner the spinner.
+ * @param decimalFormatPattern the number format pattern.
+ */
+ public NumberEditor(JSpinner spinner, String decimalFormatPattern)
+ {
+ super(spinner);
+ NumberEditorFormatter nef
+ = new NumberEditorFormatter(decimalFormatPattern);
+ nef.setMinimum(getModel().getMinimum());
+ nef.setMaximum(getModel().getMaximum());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the format used by the text field.
+ *
+ * @return The format used by the text field.
+ */
+ public DecimalFormat getFormat()
+ {
+ NumberFormatter formatter = (NumberFormatter) ftf.getFormatter();
+ return (DecimalFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the model used by the editor's {@link JSpinner} component,
+ * cast to a {@link SpinnerNumberModel}.
+ *
+ * @return The model.
+ */
+ public SpinnerNumberModel getModel()
+ {
+ return (SpinnerNumberModel) getSpinner().getModel();
+ }
+ }
+
+ static class NumberEditorFormatter
+ extends NumberFormatter
+ {
+ public NumberEditorFormatter()
+ {
+ super(NumberFormat.getInstance());
+ }
+ public NumberEditorFormatter(String decimalFormatPattern)
+ {
+ super(new DecimalFormat(decimalFormatPattern));
+ }
+ }
+
+ /**
+ * A JSpinner editor used for the {@link SpinnerListModel}.
+ * This editor uses a JFormattedTextField to edit the values
+ * of the spinner.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public static class ListEditor extends DefaultEditor
+ {
+ /**
+ * Creates a new instance of ListEditor.
+ *
+ * @param spinner the spinner for which this editor is used
+ */
+ public ListEditor(JSpinner spinner)
+ {
+ super(spinner);
+ }
+
+ /**
+ * Returns the spinner's model cast as a {@link SpinnerListModel}.
+ *
+ * @return The spinner's model.
+ */
+ public SpinnerListModel getModel()
+ {
+ return (SpinnerListModel) getSpinner().getModel();
+ }
+ }
+
+ /**
+ * An editor class for a JSpinner that is used
+ * for displaying and editing dates (e.g. that uses
+ * SpinnerDateModel as model).
+ *
+ * The editor uses a {@link JTextField} with the value
+ * displayed by a {@link DateFormatter} instance.
+ */
+ public static class DateEditor extends DefaultEditor
+ {
+
+ /** The serialVersionUID. */
+ private static final long serialVersionUID = -4279356973770397815L;
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * JSpinner.
+ *
+ * @param spinner the JSpinner for which to
+ * create a DateEditor instance
+ */
+ public DateEditor(JSpinner spinner)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter();
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Creates a new instance of DateEditor for the specified
+ * JSpinner using the specified date format
+ * pattern.
+ *
+ * @param spinner the JSpinner for which to
+ * create a DateEditor instance
+ * @param dateFormatPattern the date format to use
+ *
+ * @see SimpleDateFormat#SimpleDateFormat(String)
+ */
+ public DateEditor(JSpinner spinner, String dateFormatPattern)
+ {
+ super(spinner);
+ DateEditorFormatter nef = new DateEditorFormatter(dateFormatPattern);
+ nef.setMinimum(getModel().getStart());
+ nef.setMaximum(getModel().getEnd());
+ ftf.setFormatterFactory(new DefaultFormatterFactory(nef));
+ }
+
+ /**
+ * Returns the SimpleDateFormat instance that is used to
+ * format the date value.
+ *
+ * @return the SimpleDateFormat instance that is used to
+ * format the date value
+ */
+ public SimpleDateFormat getFormat()
+ {
+ DateFormatter formatter = (DateFormatter) ftf.getFormatter();
+ return (SimpleDateFormat) formatter.getFormat();
+ }
+
+ /**
+ * Returns the {@link SpinnerDateModel} that is edited by this editor.
+ *
+ * @return the SpinnerDateModel that is edited by this editor
+ */
+ public SpinnerDateModel getModel()
+ {
+ return (SpinnerDateModel) getSpinner().getModel();
+ }
+ }
+
+ static class DateEditorFormatter
+ extends DateFormatter
+ {
+ public DateEditorFormatter()
+ {
+ super(DateFormat.getInstance());
+ }
+ public DateEditorFormatter(String dateFormatPattern)
+ {
+ super(new SimpleDateFormat(dateFormatPattern));
+ }
+ }
+
+ /**
+ * A listener that forwards {@link ChangeEvent} notifications from the model
+ * to the {@link JSpinner}'s listeners.
+ */
+ class ModelListener implements ChangeListener
+ {
+ /**
+ * Creates a new listener.
+ */
+ public ModelListener()
+ {
+ // nothing to do here
+ }
+
+ /**
+ * Receives notification from the model that its state has changed.
+ *
+ * @param event the event (ignored).
+ */
+ public void stateChanged(ChangeEvent event)
+ {
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * The model that defines the current value and permitted values for the
+ * spinner.
+ */
+ private SpinnerModel model;
+
+ /** The current editor. */
+ private JComponent editor;
+
+ private static final long serialVersionUID = 3412663575706551720L;
+
+ /**
+ * Creates a new JSpinner with default instance of
+ * {@link SpinnerNumberModel} (that is, a model with value 0, step size 1,
+ * and no upper or lower limit).
+ *
+ * @see javax.swing.SpinnerNumberModel
+ */
+ public JSpinner()
+ {
+ this(new SpinnerNumberModel());
+ }
+
+ /**
+ * Creates a new JSpinner with the specified model. The
+ * {@link #createEditor(SpinnerModel)} method is used to create an editor
+ * that is suitable for the model.
+ *
+ * @param model the model (null not permitted).
+ *
+ * @throws NullPointerException if model is null.
+ */
+ public JSpinner(SpinnerModel model)
+ {
+ this.model = model;
+ this.editor = createEditor(model);
+ model.addChangeListener(new ModelListener());
+ updateUI();
+ }
+
+ /**
+ * If the editor is JSpinner.DefaultEditor, then forwards the
+ * call to it, otherwise do nothing.
+ *
+ * @throws ParseException DOCUMENT ME!
+ */
+ public void commitEdit() throws ParseException
+ {
+ if (editor instanceof DefaultEditor)
+ ((DefaultEditor) editor).commitEdit();
+ }
+
+ /**
+ * Gets the current editor
+ *
+ * @return the current editor
+ *
+ * @see #setEditor
+ */
+ public JComponent getEditor()
+ {
+ return editor;
+ }
+
+ /**
+ * Changes the current editor to the new editor. The old editor is
+ * removed from the spinner's {@link ChangeEvent} list.
+ *
+ * @param editor the new editor (null not permitted.
+ *
+ * @throws IllegalArgumentException if editor is
+ * null.
+ *
+ * @see #getEditor
+ */
+ public void setEditor(JComponent editor)
+ {
+ if (editor == null)
+ throw new IllegalArgumentException("editor may not be null");
+
+ JComponent oldEditor = this.editor;
+ if (oldEditor instanceof DefaultEditor)
+ ((DefaultEditor) oldEditor).dismiss(this);
+ else if (oldEditor instanceof ChangeListener)
+ removeChangeListener((ChangeListener) oldEditor);
+
+ this.editor = editor;
+ firePropertyChange("editor", oldEditor, editor);
+ }
+
+ /**
+ * Returns the model used by the {@link JSpinner} component.
+ *
+ * @return The model.
+ *
+ * @see #setModel(SpinnerModel)
+ */
+ public SpinnerModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * Sets a new underlying model.
+ *
+ * @param newModel the new model to set
+ *
+ * @exception IllegalArgumentException if newModel is null
+ */
+ public void setModel(SpinnerModel newModel)
+ {
+ if (newModel == null)
+ throw new IllegalArgumentException();
+
+ if (model == newModel)
+ return;
+
+ SpinnerModel oldModel = model;
+ model = newModel;
+ firePropertyChange("model", oldModel, newModel);
+ setEditor(createEditor(model));
+ }
+
+ /**
+ * Gets the next value without changing the current value.
+ *
+ * @return the next value
+ *
+ * @see javax.swing.SpinnerModel#getNextValue
+ */
+ public Object getNextValue()
+ {
+ return model.getNextValue();
+ }
+
+ /**
+ * Gets the previous value without changing the current value.
+ *
+ * @return the previous value
+ *
+ * @see javax.swing.SpinnerModel#getPreviousValue
+ */
+ public Object getPreviousValue()
+ {
+ return model.getPreviousValue();
+ }
+
+ /**
+ * Gets the SpinnerUI that handles this spinner
+ *
+ * @return the SpinnerUI
+ */
+ public SpinnerUI getUI()
+ {
+ return (SpinnerUI) ui;
+ }
+
+ /**
+ * Gets the current value of the spinner, according to the underly model,
+ * not the UI.
+ *
+ * @return the current value
+ *
+ * @see javax.swing.SpinnerModel#getValue
+ */
+ public Object getValue()
+ {
+ return model.getValue();
+ }
+
+ /**
+ * Sets the value in the model.
+ *
+ * @param value the new value.
+ */
+ public void setValue(Object value)
+ {
+ model.setValue(value);
+ }
+
+ /**
+ * Returns the ID that identifies which look and feel class will be
+ * the UI delegate for this spinner.
+ *
+ * @return "SpinnerUI".
+ */
+ public String getUIClassID()
+ {
+ return "SpinnerUI";
+ }
+
+ /**
+ * This method resets the spinner's UI delegate to the default UI for the
+ * current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((SpinnerUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Sets the UI delegate for the component.
+ *
+ * @param ui The spinner's UI delegate.
+ */
+ public void setUI(SpinnerUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * Adds a ChangeListener
+ *
+ * @param listener the listener to add
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Remove a particular listener
+ *
+ * @param listener the listener to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Gets all the ChangeListeners
+ *
+ * @return all the ChangeListeners
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Fires a ChangeEvent to all the ChangeListeners
+ * added to this JSpinner
+ */
+ protected void fireStateChanged()
+ {
+ ChangeEvent evt = new ChangeEvent(this);
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(evt);
+ }
+
+ /**
+ * Creates an editor that is appropriate for the specified model.
+ *
+ * @param model the model.
+ *
+ * @return The editor.
+ */
+ protected JComponent createEditor(SpinnerModel model)
+ {
+ if (model instanceof SpinnerDateModel)
+ return new DateEditor(this);
+ else if (model instanceof SpinnerNumberModel)
+ return new NumberEditor(this);
+ else if (model instanceof SpinnerListModel)
+ return new ListEditor(this);
+ else
+ return new DefaultEditor(this);
+ }
+}
diff --git a/libjava/classpath/javax/swing/JSplitPane.java b/libjava/classpath/javax/swing/JSplitPane.java
new file mode 100644
index 000000000..856b2e5c5
--- /dev/null
+++ b/libjava/classpath/javax/swing/JSplitPane.java
@@ -0,0 +1,942 @@
+/* JSplitPane.java --
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Graphics;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleValue;
+import javax.swing.plaf.SplitPaneUI;
+
+/**
+ * This class implements JSplitPane. It is used to divide two components. By
+ * dragging the SplitPane's divider, the user can resize the two components.
+ * Note that the divider cannot resize a component to smaller than it's
+ * minimum size.
+ */
+public class JSplitPane extends JComponent implements Accessible
+{
+
+ /**
+ * Provides the accessibility features for the JSplitPane
+ * component.
+ */
+ protected class AccessibleJSplitPane extends JComponent.AccessibleJComponent
+ implements AccessibleValue
+ {
+ private static final long serialVersionUID = -1788116871416305366L;
+
+ /**
+ * Creates a new AccessibleJSplitPane instance.
+ */
+ protected AccessibleJSplitPane()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JSplitPane}
+ * component.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet result = super.getAccessibleStateSet();
+ if (getOrientation() == HORIZONTAL_SPLIT)
+ {
+ result.add(AccessibleState.HORIZONTAL);
+ }
+ else if (getOrientation() == VERTICAL_SPLIT)
+ {
+ result.add(AccessibleState.VERTICAL);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the JSplitPane component.
+ *
+ * @return {@link AccessibleRole#SPLIT_PANE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.SPLIT_PANE;
+ }
+
+ /**
+ * Returns an object that provides access to the current, minimum and
+ * maximum values for the {@link JSplitPane}. Since this class implements
+ * {@link AccessibleValue}, it returns itself.
+ *
+ * @return The accessible value.
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the current divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The current divider location.
+ */
+ public Number getCurrentAccessibleValue()
+ {
+ return new Integer(getDividerLocation());
+ }
+
+ /**
+ * Sets the divider location for the {@link JSplitPane} component and sends
+ * a {@link PropertyChangeEvent} (with the property name
+ * {@link AccessibleContext#ACCESSIBLE_VALUE_PROPERTY}) to all registered
+ * listeners. If the supplied value is null, this method
+ * does nothing and returns false.
+ *
+ * @param value the new divider location (null permitted).
+ *
+ * @return true if the divider location value is updated, and
+ * false otherwise.
+ */
+ public boolean setCurrentAccessibleValue(Number value)
+ {
+ if (value == null)
+ return false;
+ Number oldValue = getCurrentAccessibleValue();
+ setDividerLocation(value.intValue());
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VALUE_PROPERTY, oldValue,
+ new Integer(value.intValue()));
+ return true;
+ }
+
+ /**
+ * Returns the minimum divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The minimum divider location.
+ */
+ public Number getMinimumAccessibleValue()
+ {
+ return new Integer(getMinimumDividerLocation());
+ }
+
+ /**
+ * Returns the maximum divider location for the {@link JSplitPane}
+ * component, as an {@link Integer}.
+ *
+ * @return The maximum divider location.
+ */
+ public Number getMaximumAccessibleValue()
+ {
+ return new Integer(getMaximumDividerLocation());
+ }
+ }
+
+ private static final long serialVersionUID = -5634142046175988380L;
+
+ /** The constraints string used to add components to the bottom. */
+ public static final String BOTTOM = "bottom";
+
+ /** The property fired when the continuousLayout property changes. */
+ public static final String CONTINUOUS_LAYOUT_PROPERTY = "continuousLayout";
+
+ /** The property fired when the divider property changes. */
+ public static final String DIVIDER = "divider";
+
+ /** The property fired when the divider location property changes. */
+ public static final String DIVIDER_LOCATION_PROPERTY = "dividerLocation";
+
+ /** The property fired when the divider size property changes. */
+ public static final String DIVIDER_SIZE_PROPERTY = "dividerSize";
+
+ /**
+ * The value of the orientation when the components are split horizontally.
+ */
+ public static final int HORIZONTAL_SPLIT = 1;
+
+ /** The property fired when the last divider location property changes. */
+ public static final String LAST_DIVIDER_LOCATION_PROPERTY =
+ "lastDividerLocation";
+
+ /** The constraints string used to add components to the left. */
+ public static final String LEFT = "left";
+
+ /** The property fired when the one touch expandable property changes. */
+ public static final String ONE_TOUCH_EXPANDABLE_PROPERTY =
+ "oneTouchExpandable";
+
+ /** The property fired when the orientation property changes. */
+ public static final String ORIENTATION_PROPERTY = "orientation";
+
+ /** The property fired when the resize weight property changes. */
+ public static final String RESIZE_WEIGHT_PROPERTY = "resizeWeight";
+
+ /** The constraints string used to add components to the right. */
+ public static final String RIGHT = "right";
+
+ /** The constraints string used to add components to the top. */
+ public static final String TOP = "top";
+
+ /** The value of the orientation when the components are split vertically. */
+ public static final int VERTICAL_SPLIT = 0;
+
+ /** Whether the JSplitPane uses continuous layout. */
+ protected boolean continuousLayout;
+
+ /** Whether the JSplitPane uses one touch expandable buttons. */
+ protected boolean oneTouchExpandable = false;
+
+ // This is the master dividerSize variable and sets the
+ // BasicSplitPaneDivider one accordingly
+
+ /** The size of the divider. */
+ protected int dividerSize = 10;
+
+ /** The last location of the divider given by the UI. */
+ protected int lastDividerLocation;
+
+ /** The orientation of the JSplitPane. */
+ protected int orientation;
+
+ /** The component on the top or left. */
+ protected Component leftComponent;
+
+ /** The component on the right or bottom. */
+ protected Component rightComponent;
+
+ /**
+ * The divider location.
+ */
+ private int dividerLocation;
+
+ /** Determines how extra space should be allocated. */
+ private transient double resizeWeight;
+
+ /**
+ * Indicates if the dividerSize property has been set by a client program or
+ * by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientDividerSizeSet = false;
+
+ /**
+ * Indicates if the oneTouchExpandable property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientOneTouchExpandableSet = false;
+
+ /**
+ * Creates a new JSplitPane object with the given orientation, layout mode,
+ * and left and right components.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newContinuousLayout The layout mode to use.
+ * @param newLeftComponent The left component.
+ * @param newRightComponent The right component.
+ *
+ * @throws IllegalArgumentException DOCUMENT ME!
+ */
+ public JSplitPane(int newOrientation, boolean newContinuousLayout,
+ Component newLeftComponent, Component newRightComponent)
+ {
+ if (newOrientation != HORIZONTAL_SPLIT && newOrientation != VERTICAL_SPLIT)
+ throw new IllegalArgumentException("orientation is invalid.");
+ orientation = newOrientation;
+ continuousLayout = newContinuousLayout;
+ setLeftComponent(newLeftComponent);
+ setRightComponent(newRightComponent);
+ dividerLocation = -1;
+ updateUI();
+ }
+
+ /**
+ * Creates a new JSplitPane object using nonContinuousLayout mode, the given
+ * orientation and left and right components.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newLeftComponent The left component.
+ * @param newRightComponent The right component.
+ */
+ public JSplitPane(int newOrientation, Component newLeftComponent,
+ Component newRightComponent)
+ {
+ this(newOrientation, false, newLeftComponent, newRightComponent);
+ }
+
+ /**
+ * Creates a new JSplitPane object with the given layout mode and
+ * orientation.
+ *
+ * @param newOrientation The orientation to use.
+ * @param newContinuousLayout The layout mode to use.
+ */
+ public JSplitPane(int newOrientation, boolean newContinuousLayout)
+ {
+ this(newOrientation, newContinuousLayout, null, null);
+ }
+
+ /**
+ * Creates a new JSplitPane object using a nonContinuousLayout mode and the
+ * given orientation.
+ *
+ * @param newOrientation The orientation to use.
+ */
+ public JSplitPane(int newOrientation)
+ {
+ this(newOrientation, false, null, null);
+ }
+
+ /**
+ * Creates a new JSplitPane object using HORIZONTAL_SPLIT and a
+ * nonContinuousLayout mode.
+ */
+ public JSplitPane()
+ {
+ this(HORIZONTAL_SPLIT, false, new JButton("left button"),
+ new JButton("right button"));
+ }
+
+ /**
+ * This method adds a component to the JSplitPane. The constraints object is
+ * a string that identifies where this component should go. If the
+ * constraints is not a known one, it will throw an
+ * IllegalArgumentException. The valid constraints are LEFT, TOP, RIGHT,
+ * BOTTOM and DIVIDER.
+ *
+ * @param comp The component to add.
+ * @param constraints The constraints string to use.
+ * @param index Where to place to component in the list of components.
+ *
+ * @throws IllegalArgumentException When the constraints is not a known
+ * identifier.
+ */
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ if (constraints == null)
+ {
+ if (leftComponent == null)
+ constraints = LEFT;
+ else if (rightComponent == null)
+ constraints = RIGHT;
+ }
+
+ if (constraints instanceof String)
+ {
+ String placement = (String) constraints;
+
+ if (placement.equals(BOTTOM) || placement.equals(RIGHT))
+ {
+ if (rightComponent != null)
+ remove(rightComponent);
+ rightComponent = comp;
+ }
+ else if (placement.equals(LEFT) || placement.equals(TOP))
+ {
+ if (leftComponent != null)
+ remove(leftComponent);
+ leftComponent = comp;
+ }
+ else if (placement.equals(DIVIDER))
+ constraints = null;
+ else
+ throw new
+ IllegalArgumentException("Constraints is not a known identifier.");
+
+ // If no dividerLocation has been set, then we need to trigger an
+ // initial layout.
+ if (getDividerLocation() != -1)
+ resetToPreferredSizes();
+
+ super.addImpl(comp, constraints, index);
+ }
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JSplitPane component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJSplitPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJSplitPane();
+
+ return accessibleContext;
+ }
+
+ /**
+ * This method returns the bottom component.
+ *
+ * @return The bottom component.
+ */
+ public Component getBottomComponent()
+ {
+ return rightComponent;
+ }
+
+ /**
+ * This method returns the location of the divider. This method is passed to
+ * the UI.
+ *
+ * @return The location of the divider.
+ */
+ public int getDividerLocation()
+ {
+ return dividerLocation;
+ }
+
+ /**
+ * This method returns the size of the divider.
+ *
+ * @return The size of the divider.
+ */
+ public int getDividerSize()
+ {
+ return dividerSize;
+ }
+
+ /**
+ * This method returns the last divider location.
+ *
+ * @return The last divider location.
+ */
+ public int getLastDividerLocation()
+ {
+ return lastDividerLocation;
+ }
+
+ /**
+ * This method returns the left component.
+ *
+ * @return The left component.
+ */
+ public Component getLeftComponent()
+ {
+ return leftComponent;
+ }
+
+ /**
+ * This method returns the maximum divider location. This method is passed
+ * to the UI.
+ *
+ * @return DOCUMENT ME!
+ */
+ public int getMaximumDividerLocation()
+ {
+ if (ui != null)
+ return ((SplitPaneUI) ui).getMaximumDividerLocation(this);
+ else
+ return -1;
+ }
+
+ /**
+ * This method returns the minimum divider location. This method is passed
+ * to the UI.
+ *
+ * @return The minimum divider location.
+ */
+ public int getMinimumDividerLocation()
+ {
+ if (ui != null)
+ return ((SplitPaneUI) ui).getMinimumDividerLocation(this);
+ else
+ return -1;
+ }
+
+ /**
+ * This method returns the orientation that the JSplitPane is using.
+ *
+ * @return The current orientation.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ }
+
+ /**
+ * This method returns the current resize weight.
+ *
+ * @return The current resize weight.
+ */
+ public double getResizeWeight()
+ {
+ return resizeWeight;
+ }
+
+ /**
+ * This method returns the right component.
+ *
+ * @return The right component.
+ */
+ public Component getRightComponent()
+ {
+ return rightComponent;
+ }
+
+ /**
+ * This method returns the top component.
+ *
+ * @return The top component.
+ */
+ public Component getTopComponent()
+ {
+ return leftComponent;
+ }
+
+ /**
+ * This method returns the UI.
+ *
+ * @return The UI.
+ */
+ public SplitPaneUI getUI()
+ {
+ return (SplitPaneUI) ui;
+ }
+
+ /**
+ * This method returns true if the JSplitPane is using a continuousLayout.
+ *
+ * @return True if using a continuousLayout.
+ */
+ public boolean isContinuousLayout()
+ {
+ return continuousLayout;
+ }
+
+ /**
+ * This method returns true if the divider has one touch expandable buttons.
+ *
+ * @return True if one touch expandable is used.
+ */
+ public boolean isOneTouchExpandable()
+ {
+ return oneTouchExpandable;
+ }
+
+ /**
+ * This method returns true.
+ *
+ * @return true.
+ */
+ public boolean isValidateRoot()
+ {
+ return true;
+ }
+
+ /**
+ * This method overrides JComponent's paintChildren so the UI can be
+ * messaged when the children have finished painting.
+ *
+ * @param g The Graphics object to paint with.
+ */
+ protected void paintChildren(Graphics g)
+ {
+ super.paintChildren(g);
+ if (ui != null)
+ ((SplitPaneUI) ui).finishedPaintingChildren(this, g);
+ }
+
+ /**
+ * Returns an implementation-dependent string describing the attributes of
+ * this JSplitPane.
+ *
+ * @return A string describing the attributes of this JSplitPane
+ * (never null).
+ */
+ protected String paramString()
+ {
+ // FIXME: the next line can be restored once PR27208 is fixed
+ String superParamStr = ""; //super.paramString();
+ CPStringBuilder sb = new CPStringBuilder();
+ sb.append(",continuousLayout=").append(isContinuousLayout());
+ sb.append(",dividerSize=").append(getDividerSize());
+ sb.append(",lastDividerLocation=").append(getLastDividerLocation());
+ sb.append(",oneTouchExpandable=").append(isOneTouchExpandable());
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL_SPLIT)
+ sb.append("HORIZONTAL_SPLIT");
+ else
+ sb.append("VERTICAL_SPLIT");
+ return superParamStr + sb.toString();
+ }
+
+ /**
+ * This method removes the given component from the JSplitPane.
+ *
+ * @param component The Component to remove.
+ */
+ public void remove(Component component)
+ {
+ if (component == leftComponent)
+ leftComponent = null;
+ else if (component == rightComponent)
+ rightComponent = null;
+ super.remove(component);
+ }
+
+ /**
+ * This method removes the component at the given index.
+ *
+ * @param index The index of the component to remove.
+ */
+ public void remove(int index)
+ {
+ Component component = getComponent(index);
+ if (component == leftComponent)
+ leftComponent = null;
+ else if (component == rightComponent)
+ rightComponent = null;
+ super.remove(index);
+ }
+
+ /**
+ * This method removes all components from the JSplitPane.
+ */
+ public void removeAll()
+ {
+ leftComponent = null;
+ rightComponent = null;
+ super.removeAll();
+ }
+
+ /**
+ * This method resets all children of the JSplitPane to their preferred
+ * sizes.
+ */
+ public void resetToPreferredSizes()
+ {
+ if (ui != null)
+ ((SplitPaneUI) ui).resetToPreferredSizes(this);
+ }
+
+ /**
+ * This method sets the bottom component.
+ *
+ * @param comp The Component to be placed at the bottom.
+ */
+ public void setBottomComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, BOTTOM);
+ else
+ add(new JButton("right button"), BOTTOM);
+ }
+
+ /**
+ * This method sets the layout mode for the JSplitPane.
+ *
+ * @param newContinuousLayout Whether the JSplitPane is in continuousLayout
+ * mode.
+ */
+ public void setContinuousLayout(boolean newContinuousLayout)
+ {
+ if (newContinuousLayout != continuousLayout)
+ {
+ boolean oldValue = continuousLayout;
+ continuousLayout = newContinuousLayout;
+ firePropertyChange(CONTINUOUS_LAYOUT_PROPERTY, oldValue,
+ continuousLayout);
+ }
+ }
+
+ /**
+ * This method sets the location of the divider. A value of 0 sets the
+ * divider to the farthest left. A value of 1 sets the divider to the
+ * farthest right.
+ *
+ * @param proportionalLocation A double that describes the location of the
+ * divider.
+ *
+ * @throws IllegalArgumentException if proportionalLocation is
+ * not in the range from 0.0 to 1.0 inclusive.
+ */
+ public void setDividerLocation(double proportionalLocation)
+ {
+ if (proportionalLocation > 1 || proportionalLocation < 0)
+ throw new IllegalArgumentException
+ ("proportion has to be between 0 and 1.");
+
+ int max = ((orientation == HORIZONTAL_SPLIT) ? getWidth() : getHeight())
+ - getDividerSize();
+ setDividerLocation((int) (proportionalLocation * max));
+ }
+
+ /**
+ * This method sets the location of the divider.
+ *
+ * @param location The location of the divider. The negative value forces to
+ * compute the new location from the preferred sizes of the split
+ * pane components.
+ */
+ public void setDividerLocation(int location)
+ {
+ int oldLocation = dividerLocation;
+ dividerLocation = location;
+ SplitPaneUI ui = getUI();
+ if (ui != null)
+ ui.setDividerLocation(this, location);
+ firePropertyChange(DIVIDER_LOCATION_PROPERTY, oldLocation,
+ location);
+ }
+
+ /**
+ * This method sets the size of the divider.
+ *
+ * @param newSize The size of the divider.
+ */
+ public void setDividerSize(int newSize)
+ {
+ clientDividerSizeSet = true;
+ if (newSize != dividerSize)
+ {
+ int oldSize = dividerSize;
+ dividerSize = newSize;
+ firePropertyChange(DIVIDER_SIZE_PROPERTY, oldSize, dividerSize);
+ }
+ }
+
+ // This doesn't appear to do anything when set from user side.
+ // so it probably is only used from the UI side to change the
+ // lastDividerLocation var.
+
+ /**
+ * This method sets the last location of the divider.
+ *
+ * @param newLastLocation The last location of the divider.
+ */
+ public void setLastDividerLocation(int newLastLocation)
+ {
+ if (newLastLocation != lastDividerLocation)
+ {
+ int oldValue = lastDividerLocation;
+ lastDividerLocation = newLastLocation;
+ firePropertyChange(LAST_DIVIDER_LOCATION_PROPERTY, oldValue,
+ lastDividerLocation);
+ }
+ }
+
+ /**
+ * This method sets the left component.
+ *
+ * @param comp The left component.
+ */
+ public void setLeftComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, LEFT);
+ else
+ remove (leftComponent);
+ }
+
+ /**
+ * This method sets whether the divider has one touch expandable buttons.
+ * The one touch expandable buttons can expand the size of either component
+ * to the maximum allowed size.
+ *
+ * @param newValue Whether the divider will have one touch expandable
+ * buttons.
+ */
+ public void setOneTouchExpandable(boolean newValue)
+ {
+ clientOneTouchExpandableSet = true;
+ if (newValue != oneTouchExpandable)
+ {
+ boolean oldValue = oneTouchExpandable;
+ oneTouchExpandable = newValue;
+ firePropertyChange(ONE_TOUCH_EXPANDABLE_PROPERTY, oldValue,
+ oneTouchExpandable);
+ }
+ }
+
+ /**
+ * Sets the orientation for the JSplitPane and sends a
+ * {@link PropertyChangeEvent} (with the property name
+ * {@link #ORIENTATION_PROPERTY}) to all registered listeners.
+ *
+ * @param orientation the orientation (either {@link #HORIZONTAL_SPLIT}
+ * or {@link #VERTICAL_SPLIT}).
+ *
+ * @throws IllegalArgumentException if orientation is not one of
+ * the listed values.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL_SPLIT && orientation != VERTICAL_SPLIT)
+ throw new IllegalArgumentException
+ ("orientation must be one of VERTICAL_SPLIT, HORIZONTAL_SPLIT");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange(ORIENTATION_PROPERTY, oldOrientation,
+ this.orientation);
+ }
+ }
+
+ /**
+ * This method determines how extra space will be distributed among the left
+ * and right components. A value of 0 will allocate all extra space to the
+ * right component. A value of 1 indicates that all extra space will go to
+ * the left component. A value in between 1 and 0 will split the space
+ * accordingly.
+ *
+ * @param value The resize weight.
+ */
+ public void setResizeWeight(double value)
+ {
+ if (value < 0.0 || value > 1.0)
+ throw new IllegalArgumentException("Value outside permitted range.");
+ if (this.resizeWeight != value)
+ {
+ double old = resizeWeight;
+ resizeWeight = value;
+ firePropertyChange(RESIZE_WEIGHT_PROPERTY, old, value);
+ }
+ }
+
+ /**
+ * This method sets the right component.
+ *
+ * @param comp The right component.
+ */
+ public void setRightComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, RIGHT);
+ else
+ remove (rightComponent);
+ }
+
+ /**
+ * This method sets the top component.
+ *
+ * @param comp The top component.
+ */
+ public void setTopComponent(Component comp)
+ {
+ if (comp != null)
+ add(comp, TOP);
+ else
+ add(new JButton("left button"), TOP);
+ }
+
+ /**
+ * This method sets the UI used by the JSplitPane.
+ *
+ * @param ui The UI to use.
+ */
+ public void setUI(SplitPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method resets the UI to the one specified by the current Look and
+ * Feel.
+ */
+ public void updateUI()
+ {
+ setUI((SplitPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a string identifier to determine which UI class it
+ * needs.
+ *
+ * @return A string that identifies it's UI class.
+ */
+ public String getUIClassID()
+ {
+ return "SplitPaneUI";
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("dividerSize"))
+ {
+ if (! clientDividerSizeSet)
+ {
+ setDividerSize(((Integer) value).intValue());
+ clientDividerSizeSet = false;
+ }
+ }
+ else if (propertyName.equals("oneTouchExpandable"))
+ {
+ if (! clientOneTouchExpandableSet)
+ {
+ setOneTouchExpandable(((Boolean) value).booleanValue());
+ clientOneTouchExpandableSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTabbedPane.java b/libjava/classpath/javax/swing/JTabbedPane.java
new file mode 100644
index 000000000..18055e8a9
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTabbedPane.java
@@ -0,0 +1,1728 @@
+/* JTabbedPane.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.io.Serializable;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.TabbedPaneUI;
+import javax.swing.plaf.UIResource;
+
+/**
+ * This is a container for components where only one component is displayed at
+ * a given time and the displayed component can be switched by clicking on
+ * tabs.
+ *
+ *
+ * Tabs can be oriented in several ways. They can be above, below, left and
+ * right of the component. Tabs can either wrap around (by creating multiple
+ * rows of tabs) or they can be scrolled (where only a subset of the tabs
+ * can be seen at once). More tabs can be added by calling the
+ * add/addTab/insertTab methods.
+ *
+ */
+public class JTabbedPane extends JComponent implements Serializable,
+ Accessible,
+ SwingConstants
+{
+ /**
+ * Accessibility support for JTabbedPane.
+ */
+ protected class AccessibleJTabbedPane extends JComponent.AccessibleJComponent
+ implements AccessibleSelection, ChangeListener
+ {
+ /**
+ * The serialization UID.
+ */
+ private static final long serialVersionUID = 7610530885966830483L;
+
+ /**
+ * Creates a new AccessibleJTabbedPane object.
+ */
+ public AccessibleJTabbedPane()
+ {
+ super();
+ }
+
+ /**
+ * Receives notification when the selection state of the
+ * JTabbedPane changes and fires appropriate property change
+ * events to interested listeners.
+ *
+ * @param e the change event describing the change
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // I couldn't figure out what else should be done here.
+ Object source = e.getSource();
+ firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
+ null, source);
+ }
+
+ /**
+ * Returns the accessible role of the JTabbedPane, which is
+ * {@link AccessibleRole#PAGE_TAB_LIST}.
+ *
+ * @return the accessible role of the JTabbedPane
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PAGE_TAB_LIST;
+ }
+
+ /**
+ * Returns the number of accessible child components of the
+ * JTabbedPane.
+ *
+ * @return the number of accessible child components of the
+ * JTabbedPane
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return getTabCount();
+ }
+
+ /**
+ * Returns the accessible child component at the specified index.
+ *
+ * @param i the index of the child component to fetch
+ *
+ * @return the accessible child component at the specified index
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // Testing shows that the reference implementation returns instances
+ // of page here.
+ Accessible child = null;
+ if (i >= 0 && i < tabs.size())
+ child = (Page) tabs.get(i);
+ return child;
+ }
+
+ /**
+ * Returns the current selection state of the JTabbedPane
+ * as AccessibleSelection object.
+ *
+ * @return the current selection state of the JTabbedPane
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible child component at the specified coordinates.
+ * If there is no child component at this location, then return the
+ * currently selected tab.
+ *
+ * @param p the coordinates at which to look up the child component
+ *
+ * @return the accessible child component at the specified coordinates or
+ * the currently selected tab if there is no child component at
+ * this location
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ int tabIndex = indexAtLocation(p.x, p.y);
+ if (tabIndex >= 0)
+ return getAccessibleChild(tabIndex);
+ else
+ return getAccessibleSelection(0);
+ }
+
+ /**
+ * Returns the number of selected child components of the
+ * JTabbedPane. The reference implementation appears
+ * to return 1 always and we do the same.
+ *
+ * @return 1
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the selected tab, or null if there is no
+ * selection.
+ *
+ * @param i the selection index (ignored here).
+ *
+ * @return The selected tab, or null.
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ Accessible result = null;
+ int selected = getSelectedIndex();
+ if (selected >= 0)
+ result = (Page) tabs.get(selected);
+ return result;
+ }
+
+ /**
+ * Returns true if the specified child is selected,
+ * and false otherwise.
+ *
+ * @param i the child index.
+ *
+ * @return A boolean.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ return i == getSelectedIndex();
+ }
+
+ /**
+ * Selects the specified tab.
+ *
+ * @param i the index of the item to select.
+ */
+ public void addAccessibleSelection(int i)
+ {
+ setSelectedIndex(i);
+ }
+
+ /**
+ * Does nothing - it makes no sense to remove a selection for a
+ * tabbed pane, since one tab must always be selected.
+ *
+ * @param i the item index.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ // do nothing
+ }
+
+ /**
+ * Does nothing - it makes no sense to clear the selection for
+ * a tabbed pane, since one tab must always be selected.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void clearAccessibleSelection()
+ {
+ // do nothing
+ }
+
+ /**
+ * Does nothing - it makes no sense to select all for a tabbed
+ * pane, since only one tab can be selected at a time.
+ *
+ * @see #addAccessibleSelection(int)
+ */
+ public void selectAllAccessibleSelection()
+ {
+ // do nothing
+ }
+ }
+
+ /**
+ * A helper class that listens for changes to the model.
+ */
+ protected class ModelListener implements ChangeListener, Serializable
+ {
+ private static final long serialVersionUID = 497359819958114132L;
+
+ /**
+ * Creates a new ModelListener object.
+ */
+ protected ModelListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called whenever the model is changed.
+ *
+ * @param e The ChangeEvent that is passed from the model.
+ */
+ public void stateChanged(ChangeEvent e)
+ {
+ // Propagate to our listeners.
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * A private class that holds all the information for each tab.
+ */
+ private class Page
+ extends AccessibleContext
+ implements Accessible
+ {
+ /** The tooltip string. */
+ private String tip;
+
+ /** The component associated with the tab. */
+ private Component component;
+
+ /** The active icon associated with the tab. */
+ private transient Icon icon;
+
+ /** The disabled icon associated with the tab. */
+ private transient Icon disabledIcon;
+
+ /** The tab's enabled status. */
+ private transient boolean enabled = true;
+
+ /** The string painted on the tab. */
+ private transient String title;
+
+ /** The background color of the tab. */
+ private transient Color bg;
+
+ /** The foreground color of the tab. */
+ private transient Color fg;
+
+ /** The mnemonic associated with the tab. */
+ private transient int mnemonicKey;
+
+ /** The index of the underlined character in the string. */
+ private transient int underlinedChar = -1;
+
+ /**
+ * Creates a new data storage for the tab.
+ *
+ * @param title The string displayed on the tab.
+ * @param icon The active icon displayed on the tab.
+ * @param component The component associated with the tab.
+ * @param tip The tooltip associated with the tab.
+ */
+ protected Page(String title, Icon icon, Component component, String tip)
+ {
+ this.title = title;
+ this.icon = icon;
+ this.component = component;
+ this.tip = tip;
+ }
+
+ /**
+ * This method returns the component associated with the tab.
+ *
+ * @return The component associated with the tab.
+ */
+ public Component getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * This method sets the component associated with the tab.
+ *
+ * @param c The component associated with the tab.
+ */
+ public void setComponent(Component c)
+ {
+ int i = indexOfComponent(component);
+ insertTab(title, icon, c, tip, i);
+ component = c;
+ removeTabAt(i);
+ }
+
+ /**
+ * This method returns the tooltip string.
+ *
+ * @return The tooltip string.
+ */
+ public String getTip()
+ {
+ return tip;
+ }
+
+ /**
+ * This method sets the tooltip string.
+ *
+ * @param tip The tooltip string.
+ */
+ public void setTip(String tip)
+ {
+ this.tip = tip;
+ }
+
+ /**
+ * This method returns the background color.
+ *
+ * @return The background color.
+ */
+ public Color getBackground()
+ {
+ Color background;
+ if (bg == null)
+ background = JTabbedPane.this.getBackground();
+ else
+ background = bg;
+ return background;
+ }
+
+ /**
+ * This method sets the background color.
+ *
+ * @param background The background color.
+ */
+ public void setBackground(Color background)
+ {
+ bg = background;
+ }
+
+ /**
+ * This method returns the foreground color.
+ *
+ * @return The foreground color.
+ */
+ public Color getForeground()
+ {
+ Color foreground;
+ if (fg == null)
+ foreground = JTabbedPane.this.getForeground();
+ else
+ foreground = fg;
+ return foreground;
+ }
+
+ /**
+ * This method sets the foreground color.
+ *
+ * @param foreground The foreground color.
+ */
+ public void setForeground(Color foreground)
+ {
+ fg = foreground;
+ }
+
+ /**
+ * This method returns the title associated with the tab.
+ *
+ * @return The title of the tab.
+ */
+ public String getTitle()
+ {
+ return title;
+ }
+
+ private static final long serialVersionUID = 1614381073220130939L;
+
+ /**
+ * This method sets the title of the tab.
+ *
+ * @param text The title of the tab.
+ */
+ public void setTitle(String text)
+ {
+ title = text;
+ if (title != null && title.length() <= underlinedChar)
+ setDisplayedMnemonicIndex(title.length() - 1);
+ }
+
+ /**
+ * This method returns the active icon.
+ *
+ * @return The active icon.
+ */
+ public Icon getIcon()
+ {
+ return icon;
+ }
+
+ /**
+ * This method sets the active icon.
+ *
+ * @param icon The active icon.
+ */
+ public void setIcon(Icon icon)
+ {
+ this.icon = icon;
+ }
+
+ /**
+ * This method returns the disabled icon.
+ *
+ * @return The disabled icon.
+ */
+ public Icon getDisabledIcon()
+ {
+ if (disabledIcon == null && icon instanceof ImageIcon)
+ setDisabledIcon(icon);
+ return disabledIcon;
+ }
+
+ /**
+ * This method sets the disabled icon.
+ *
+ * @param disabledIcon The disabled icon.
+ */
+ public void setDisabledIcon(Icon disabledIcon)
+ {
+ this.disabledIcon = disabledIcon;
+ }
+
+ /**
+ * This method returns whether the tab is enabled.
+ *
+ * @return Whether the tab is enabled.
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * This method sets whether the tab is enabled.
+ *
+ * @param enabled Whether this tab is enabled.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ this.enabled = enabled;
+ }
+
+ /**
+ * This method returns the mnemonic.
+ *
+ * @return The mnemonic.
+ */
+ public int getMnemonic()
+ {
+ return mnemonicKey;
+ }
+
+ /**
+ * This method sets the mnemonic. If the title is set, it will update the
+ * mnemonicIndex.
+ *
+ * @param key The mnemonic.
+ */
+ public void setMnemonic(int key)
+ {
+ setMnemonic((char) key);
+ }
+
+ /**
+ * This method sets the mnemonic. If the title is set, it will update the
+ * mnemonicIndex.
+ *
+ * @param aChar The mnemonic.
+ */
+ public void setMnemonic(char aChar)
+ {
+ mnemonicKey = aChar;
+ if (title != null)
+ setDisplayedMnemonicIndex(title.indexOf(mnemonicKey));
+ }
+
+ /**
+ * This method returns the mnemonicIndex.
+ *
+ * @return The mnemonicIndex.
+ */
+ public int getDisplayedMnemonicIndex()
+ {
+ return underlinedChar;
+ }
+
+ /**
+ * This method sets the mnemonicIndex.
+ *
+ * @param index The mnemonicIndex.
+ *
+ * @throws IllegalArgumentException If index less than -1 || index greater
+ * or equal to title.length.
+ */
+ public void setDisplayedMnemonicIndex(int index)
+ throws IllegalArgumentException
+ {
+ if (index < -1 || title != null && index >= title.length())
+ throw new IllegalArgumentException();
+
+ if (title == null || mnemonicKey == 0 || (index > -1 && title.charAt(index) != mnemonicKey))
+ index = -1;
+
+ underlinedChar = index;
+ }
+
+ /**
+ * Returns the accessible context, which is this object itself.
+ *
+ * @return the accessible context, which is this object itself
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the accessible name for this tab.
+ *
+ * @return The accessible name.
+ */
+ public String getAccessibleName()
+ {
+ if (accessibleName != null)
+ return accessibleName;
+ else
+ return title;
+ }
+
+ /**
+ * Returns the accessible role of this tab, which is always
+ * {@link AccessibleRole#PAGE_TAB}.
+ *
+ * @return the accessible role of this tab
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.PAGE_TAB;
+ }
+
+ /**
+ * Returns the accessible state set of this object.
+ *
+ * @return the accessible state set of this object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleContext parentCtx = JTabbedPane.this.getAccessibleContext();
+ AccessibleStateSet state = parentCtx.getAccessibleStateSet();
+ state.add(AccessibleState.SELECTABLE);
+ if (component == getSelectedComponent())
+ state.add(AccessibleState.SELECTED);
+ return state;
+ }
+
+ /**
+ * Returns the index of this tab inside its parent.
+ *
+ * @return the index of this tab inside its parent
+ */
+ public int getAccessibleIndexInParent()
+ {
+ // TODO: Not sure if the title is unambiguous, but I can't figure
+ // another way of doing this.
+ return indexOfTab(title);
+ }
+
+ /**
+ * Returns the number of accessible children, which is always one (the
+ * component of this tab).
+ *
+ * @return the number of accessible children
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the accessible child of this tab, which is the component
+ * displayed by the tab.
+ *
+ * @return the accessible child of this tab
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ // A quick test shows that this method always returns the component
+ // displayed by the tab, regardless of the index.
+ return (Accessible) component;
+ }
+
+ /**
+ * Returns the locale of this accessible object.
+ *
+ * @return the locale of this accessible object
+ */
+ public Locale getLocale()
+ {
+ // TODO: Is this ok?
+ return Locale.getDefault();
+ }
+ }
+
+ private static final long serialVersionUID = 1614381073220130939L;
+
+ /** The changeEvent used to fire changes to listeners. */
+ protected ChangeEvent changeEvent;
+
+ /** The listener that listens to the model. */
+ protected ChangeListener changeListener;
+
+ /** The model that describes this JTabbedPane. */
+ protected SingleSelectionModel model;
+
+ /** Indicates that the TabbedPane is in scrolling mode. */
+ public static final int SCROLL_TAB_LAYOUT = 1;
+
+ /** Indicates that the TabbedPane is in wrap mode. */
+ public static final int WRAP_TAB_LAYOUT = 0;
+
+ /** The current tabPlacement of the TabbedPane. */
+ protected int tabPlacement = SwingConstants.TOP;
+
+ /** The current tabLayoutPolicy of the TabbedPane. */
+ private transient int layoutPolicy;
+
+ /** The list of tabs associated with the TabbedPane. */
+ transient Vector tabs = new Vector();
+
+ /**
+ * Creates a new JTabbedPane object with tabs on top and using wrap tab
+ * layout.
+ */
+ public JTabbedPane()
+ {
+ this(SwingConstants.TOP, WRAP_TAB_LAYOUT);
+ }
+
+ /**
+ * Creates a new JTabbedPane object using wrap tab layout and the given
+ * tabPlacement, where tabPlacement can be one
+ * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
+ * {@link #RIGHT}.
+ *
+ * @param tabPlacement where the tabs will be placed
+ */
+ public JTabbedPane(int tabPlacement)
+ {
+ this(tabPlacement, WRAP_TAB_LAYOUT);
+ }
+
+ /**
+ * Creates a new JTabbedPane object with the given tabPlacement
+ * and tabLayoutPolicy. The tabPlacement can be one
+ * of the following values: {@link #TOP}, {@link #BOTTOM}, {@link #LEFT} or
+ * {@link #RIGHT}. The tabLayoutPolicy can be either
+ * {@link #SCROLL_TAB_LAYOUT} or {@link #WRAP_TAB_LAYOUT}.
+ *
+ * @param tabPlacement where the tabs will be placed
+ * @param tabLayoutPolicy the way tabs will be placed
+ *
+ * @throws IllegalArgumentException If tabLayoutPolicy or tabPlacement are
+ * not valid.
+ */
+ public JTabbedPane(int tabPlacement, int tabLayoutPolicy)
+ {
+ if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
+ && tabPlacement != LEFT)
+ throw new IllegalArgumentException("tabPlacement is not valid.");
+ if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
+ && tabLayoutPolicy != WRAP_TAB_LAYOUT)
+ throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
+ this.tabPlacement = tabPlacement;
+ layoutPolicy = tabLayoutPolicy;
+
+ setModel(new DefaultSingleSelectionModel());
+
+ updateUI();
+ }
+
+ /**
+ * This method returns the UI used to display the JTabbedPane.
+ *
+ * @return The UI used to display the JTabbedPane.
+ */
+ public TabbedPaneUI getUI()
+ {
+ return (TabbedPaneUI) ui;
+ }
+
+ /**
+ * This method sets the UI used to display the JTabbedPane.
+ *
+ * @param ui The UI used to display the JTabbedPane.
+ */
+ public void setUI(TabbedPaneUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method restores the UI to the defaults given by the UIManager.
+ */
+ public void updateUI()
+ {
+ setUI((TabbedPaneUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns a string identifier that is used to determine which
+ * UI will be used with the JTabbedPane.
+ *
+ * @return A string identifier for the UI.
+ */
+ public String getUIClassID()
+ {
+ return "TabbedPaneUI";
+ }
+
+ /**
+ * This method creates a ChangeListener that is used to listen to the model
+ * for events.
+ *
+ * @return A ChangeListener to listen to the model.
+ */
+ protected ChangeListener createChangeListener()
+ {
+ return new ModelListener();
+ }
+
+ /**
+ * This method adds a ChangeListener to the JTabbedPane.
+ *
+ * @param l The ChangeListener to add.
+ */
+ public void addChangeListener(ChangeListener l)
+ {
+ listenerList.add(ChangeListener.class, l);
+ }
+
+ /**
+ * This method removes a ChangeListener to the JTabbedPane.
+ *
+ * @param l The ChangeListener to remove.
+ */
+ public void removeChangeListener(ChangeListener l)
+ {
+ listenerList.remove(ChangeListener.class, l);
+ }
+
+ /**
+ * This method fires a ChangeEvent to all the JTabbedPane's ChangeListeners.
+ */
+ protected void fireStateChanged()
+ {
+ Object[] changeListeners = listenerList.getListenerList();
+ if (changeEvent == null)
+ changeEvent = new ChangeEvent(this);
+ for (int i = changeListeners.length - 2; i >= 0; i -= 2)
+ {
+ if (changeListeners[i] == ChangeListener.class)
+ ((ChangeListener) changeListeners[i + 1]).stateChanged(changeEvent);
+ }
+ }
+
+ /**
+ * This method returns all ChangeListeners registered with the JTabbedPane.
+ *
+ * @return The ChangeListeners registered with the JTabbedPane.
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) super.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * This method returns the model used with the JTabbedPane.
+ *
+ * @return The JTabbedPane's model.
+ */
+ public SingleSelectionModel getModel()
+ {
+ return model;
+ }
+
+ /**
+ * This method changes the model property of the JTabbedPane.
+ *
+ * @param m The new model to use with the JTabbedPane.
+ */
+ public void setModel(SingleSelectionModel m)
+ {
+ if (m != model)
+ {
+ SingleSelectionModel oldModel = this.model;
+ if (oldModel != null && changeListener != null)
+ oldModel.removeChangeListener(changeListener);
+
+ model = m;
+
+ if (model != null)
+ {
+ if (changeListener == null)
+ changeListener = createChangeListener();
+ model.addChangeListener(changeListener);
+ }
+ firePropertyChange("model", oldModel, this.model);
+ }
+ }
+
+ /**
+ * This method returns the tabPlacement.
+ *
+ * @return The tabPlacement used with the JTabbedPane.
+ */
+ public int getTabPlacement()
+ {
+ return tabPlacement;
+ }
+
+ /**
+ * This method changes the tabPlacement property of the JTabbedPane.
+ *
+ * @param tabPlacement The tabPlacement to use.
+ *
+ * @throws IllegalArgumentException If tabPlacement is not one of TOP,
+ * BOTTOM, LEFT, or RIGHT.
+ */
+ public void setTabPlacement(int tabPlacement)
+ {
+ if (tabPlacement != TOP && tabPlacement != BOTTOM && tabPlacement != RIGHT
+ && tabPlacement != LEFT)
+ throw new IllegalArgumentException("tabPlacement is not valid.");
+ if (tabPlacement != this.tabPlacement)
+ {
+ int oldPlacement = this.tabPlacement;
+ this.tabPlacement = tabPlacement;
+ firePropertyChange("tabPlacement", oldPlacement, this.tabPlacement);
+ }
+ }
+
+ /**
+ * This method returns the tabLayoutPolicy.
+ *
+ * @return The tabLayoutPolicy.
+ */
+ public int getTabLayoutPolicy()
+ {
+ return layoutPolicy;
+ }
+
+ /**
+ * This method changes the tabLayoutPolicy property of the JTabbedPane.
+ *
+ * @param tabLayoutPolicy The tabLayoutPolicy to use.
+ *
+ * @throws IllegalArgumentException If tabLayoutPolicy is not one of
+ * SCROLL_TAB_LAYOUT or WRAP_TAB_LAYOUT.
+ */
+ public void setTabLayoutPolicy(int tabLayoutPolicy)
+ {
+ if (tabLayoutPolicy != SCROLL_TAB_LAYOUT
+ && tabLayoutPolicy != WRAP_TAB_LAYOUT)
+ throw new IllegalArgumentException("tabLayoutPolicy is not valid.");
+ if (tabLayoutPolicy != layoutPolicy)
+ {
+ int oldPolicy = layoutPolicy;
+ layoutPolicy = tabLayoutPolicy;
+ firePropertyChange("tabLayoutPolicy", oldPolicy, layoutPolicy);
+ }
+ }
+
+ /**
+ * This method returns the index of the tab that is currently selected.
+ *
+ * @return The index of the selected tab.
+ */
+ public int getSelectedIndex()
+ {
+ return model.getSelectedIndex();
+ }
+
+ /**
+ * This method checks the index.
+ *
+ * @param index The index to check.
+ * @param start DOCUMENT ME!
+ * @param end DOCUMENT ME!
+ *
+ * @throws IndexOutOfBoundsException DOCUMENT ME!
+ */
+ private void checkIndex(int index, int start, int end)
+ {
+ if (index < start || index >= end)
+ throw new IndexOutOfBoundsException("Index < " + start + " || Index >= "
+ + end);
+ }
+
+ /**
+ * This method sets the selected index. This method will hide the old
+ * component and show the new component.
+ *
+ * @param index The index to set it at.
+ */
+ public void setSelectedIndex(int index)
+ {
+ checkIndex(index, -1, tabs.size());
+ if (index != getSelectedIndex())
+ {
+ // Hiding and showing the involved components
+ // is done by the JTabbedPane's UI.
+ model.setSelectedIndex(index);
+ }
+ }
+
+ /**
+ * This method returns the component at the selected index.
+ *
+ * @return The component at the selected index.
+ */
+ public Component getSelectedComponent()
+ {
+ int selectedIndex = getSelectedIndex();
+ Component selected = null;
+ if (selectedIndex >= 0)
+ selected = getComponentAt(selectedIndex);
+ return selected;
+ }
+
+ /**
+ * This method sets the component at the selected index.
+ *
+ * @param c The component associated with the selected index.
+ */
+ public void setSelectedComponent(Component c)
+ {
+ if (c.getParent() == this)
+ setSelectedIndex(indexOfComponent(c));
+ else
+ setComponentAt(getSelectedIndex(), c);
+ }
+
+ /**
+ * This method inserts tabs into JTabbedPane. This includes adding the
+ * component to the JTabbedPane and hiding it.
+ *
+ * @param title the title of the tab; may be null
+ * @param icon the tab's icon; may be null
+ * @param component the component associated with the tab
+ * @param tip the tooltip for the tab
+ * @param index the index to insert the tab at
+ */
+ public void insertTab(String title, Icon icon, Component component,
+ String tip, int index)
+ {
+ if (title == null)
+ title = "";
+ Page p = new Page(title, icon, component, tip);
+ tabs.insertElementAt(p, index);
+
+ // Hide the component so we don't see it. Do it before we parent it
+ // so we don't trigger a repaint.
+ if (component != null)
+ {
+ component.hide();
+ super.add(component);
+ }
+
+ if (getSelectedIndex() == -1)
+ {
+ setSelectedIndex(0);
+ fireStateChanged();
+ }
+
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be null
+ * @param icon the icon for the tab; may be null
+ * @param component the associated component
+ * @param tip the associated tooltip
+ */
+ public void addTab(String title, Icon icon, Component component, String tip)
+ {
+ insertTab(title, icon, component, tip, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be null
+ * @param icon the icon for the tab; may be null
+ * @param component the associated component
+ */
+ public void addTab(String title, Icon icon, Component component)
+ {
+ insertTab(title, icon, component, null, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be null
+ * @param component the associated component
+ */
+ public void addTab(String title, Component component)
+ {
+ insertTab(title, null, component, null, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. The title of the tab is the
+ * Component's name. If the Component is an instance of UIResource, it
+ * doesn't add the tab and instead add the component directly to the
+ * JTabbedPane.
+ *
+ * @param component The associated component.
+ *
+ * @return The Component that was added.
+ */
+ public Component add(Component component)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(component.getName(), null, component, null, tabs.size());
+
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane.
+ *
+ * @param title the title of the tab; may be null
+ * @param component the associated component
+ *
+ * @return The Component that was added.
+ */
+ public Component add(String title, Component component)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(title, null, component, null, tabs.size());
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane.
+ *
+ * @param component The associated component.
+ * @param index The index to insert the tab at.
+ *
+ * @return The Component that was added.
+ */
+ public Component add(Component component, int index)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ insertTab(component.getName(), null, component, null, index);
+ return component;
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane. If the constraints object is an
+ * icon, it will be used as the tab's icon. If the constraints object is a
+ * string, we will use it as the title.
+ *
+ * @param component The associated component.
+ * @param constraints The constraints object.
+ */
+ public void add(Component component, Object constraints)
+ {
+ add(component, constraints, tabs.size());
+ }
+
+ /**
+ * This method adds a tab to the JTabbedPane. If the Component is an
+ * instance of UIResource, it doesn't add the tab and instead add the
+ * component directly to the JTabbedPane. If the constraints object is an
+ * icon, it will be used as the tab's icon. If the constraints object is a
+ * string, we will use it as the title.
+ *
+ * @param component The associated component.
+ * @param constraints The constraints object.
+ * @param index The index to insert the tab at.
+ */
+ public void add(Component component, Object constraints, int index)
+ {
+ if (component instanceof UIResource)
+ super.add(component);
+ else
+ {
+ if (constraints instanceof String)
+ insertTab((String) constraints, null, component, null, index);
+ else
+ insertTab(component.getName(),
+ (constraints instanceof Icon) ? (Icon) constraints : null,
+ component, null, index);
+ }
+ }
+
+ /**
+ * Removes the tab at index. After the component associated with
+ * index is removed, its visibility is reset to true to ensure it
+ * will be visible if added to other containers.
+ *
+ * @param index The index of the tab to remove.
+ */
+ public void removeTabAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+
+ // We need to adjust the selection if we remove a tab that comes
+ // before the selected tab or if the selected tab is removed.
+ // This decrements the selected index by 1 if any of this is the case.
+ // Note that this covers all cases:
+ // - When the selected tab comes after the removed tab, this simply
+ // adjusts the selection so that after the removal the selected tab
+ // is still the same.
+ // - When we remove the currently selected tab, then the tab before the
+ // selected tab gets selected.
+ // - When the last tab is removed, then we have an index==0, which gets
+ // decremented to -1, which means no selection, which is 100% perfect.
+ int selectedIndex = getSelectedIndex();
+ if (selectedIndex >= index)
+ setSelectedIndex(selectedIndex - 1);
+
+ Component comp = getComponentAt(index);
+
+ // Remove the tab object.
+ tabs.remove(index);
+
+ // Remove the component. I think we cannot assume that the tab order
+ // is equal to the component order, so we iterate over the children
+ // here to find the and remove the correct component.
+ if (comp != null)
+ {
+ Component[] children = getComponents();
+ for (int i = children.length - 1; i >= 0; --i)
+ {
+ if (children[i] == comp)
+ {
+ super.remove(i);
+ comp.setVisible(true);
+ break;
+ }
+ }
+ }
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Removes the specified Component from the JTabbedPane.
+ *
+ * @param component The Component to remove.
+ */
+ public void remove(Component component)
+ {
+ // Since components implementing UIResource
+ // are not added as regular tabs by the add()
+ // methods we have to take special care when
+ // removing these object. Especially
+ // Container.remove(Component) cannot be used
+ // because it will call JTabbedPane.remove(int)
+ // later which is overridden and can only
+ // handle tab components.
+ // This implementation can even cope with a
+ // situation that someone called insertTab()
+ // with a component that implements UIResource.
+ int index = indexOfComponent(component);
+
+ // If the component is not a tab component
+ // find out its Container-given index
+ // and call that class' implementation
+ // directly.
+ if (index == -1)
+ {
+ Component[] cs = getComponents();
+ for (int i = 0; i< cs.length; i++)
+ if (cs[i] == component)
+ super.remove(i);
+ }
+ else
+ removeTabAt(index);
+ }
+
+ /**
+ * Removes the tab and component which corresponds to the specified index.
+ *
+ * @param index The index of the tab to remove.
+ */
+ public void remove(int index)
+ {
+ removeTabAt(index);
+ }
+
+ /**
+ * This method removes all tabs and associated components from the
+ * JTabbedPane.
+ */
+ public void removeAll()
+ {
+ setSelectedIndex(-1);
+ for (int i = getTabCount() - 1; i >= 0; i--)
+ removeTabAt(i);
+ }
+
+ /**
+ * This method returns how many tabs are in the JTabbedPane.
+ *
+ * @return The number of tabs in the JTabbedPane.
+ */
+ public int getTabCount()
+ {
+ return tabs.size();
+ }
+
+ /**
+ * This method returns the number of runs used to paint the JTabbedPane.
+ *
+ * @return The number of runs.
+ */
+ public int getTabRunCount()
+ {
+ return ((TabbedPaneUI) ui).getTabRunCount(this);
+ }
+
+ /**
+ * This method returns the tab title given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The title for the tab.
+ */
+ public String getTitleAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getTitle();
+ }
+
+ /**
+ * This method returns the active icon given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The active icon for the tab.
+ */
+ public Icon getIconAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getIcon();
+ }
+
+ /**
+ * This method returns the disabled icon given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The disabled icon for the tab.
+ */
+ public Icon getDisabledIconAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getDisabledIcon();
+ }
+
+ /**
+ * This method returns the tooltip string for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The tooltip string for the tab.
+ */
+ public String getToolTipTextAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getTip();
+ }
+
+ /**
+ * This method returns the foreground color for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The foreground color for the tab.
+ */
+ public Color getForegroundAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getForeground();
+ }
+
+ /**
+ * This method returns the background color for the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The background color for the tab.
+ */
+ public Color getBackgroundAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getBackground();
+ }
+
+ /**
+ * This method returns the component associated with the tab.
+ *
+ * @param index The index of the tab.
+ *
+ * @return The component associated with the tab.
+ */
+ public Component getComponentAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).getComponent();
+ }
+
+ /**
+ * This method returns whether this tab is enabled. Disabled tabs cannot be
+ * selected.
+ *
+ * @param index The index of the tab.
+ *
+ * @return Whether the tab is enabled.
+ */
+ public boolean isEnabledAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((Page) tabs.elementAt(index)).isEnabled();
+ }
+
+ /**
+ * This method returns the mnemonic for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ *
+ * @return The mnemonic for the tab.
+ */
+ public int getMnemonicAt(int tabIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ return ((Page) tabs.elementAt(tabIndex)).getMnemonic();
+ }
+
+ /**
+ * This method returns the mnemonic index for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ *
+ * @return The mnemonic index for the tab.
+ */
+ public int getDisplayedMnemonicIndexAt(int tabIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ return ((Page) tabs.elementAt(tabIndex)).getDisplayedMnemonicIndex();
+ }
+
+ /**
+ * This method returns the bounds of the tab given the index.
+ *
+ * @param index The index of the tab.
+ *
+ * @return A rectangle describing the bounds of the tab.
+ */
+ public Rectangle getBoundsAt(int index)
+ {
+ checkIndex(index, 0, tabs.size());
+ return ((TabbedPaneUI) ui).getTabBounds(this, index);
+ }
+
+ /**
+ * This method sets the title of the tab.
+ *
+ * @param index The index of the tab.
+ * @param title The new title.
+ */
+ public void setTitleAt(int index, String title)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setTitle(title);
+ }
+
+ /**
+ * This method sets the icon of the tab.
+ *
+ * @param index The index of the tab.
+ * @param icon The new icon.
+ */
+ public void setIconAt(int index, Icon icon)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setIcon(icon);
+ }
+
+ /**
+ * This method sets the disabled icon of the tab.
+ *
+ * @param index The index of the tab.
+ * @param disabledIcon The new disabled icon.
+ */
+ public void setDisabledIconAt(int index, Icon disabledIcon)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setDisabledIcon(disabledIcon);
+ }
+
+ /**
+ * This method sets the tooltip text of the tab.
+ *
+ * @param index The index of the tab.
+ * @param toolTipText The tooltip text.
+ */
+ public void setToolTipTextAt(int index, String toolTipText)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setTip(toolTipText);
+ }
+
+ /**
+ * This method sets the background color of the tab.
+ *
+ * @param index The index of the tab.
+ * @param background The background color of the tab.
+ */
+ public void setBackgroundAt(int index, Color background)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setBackground(background);
+ }
+
+ /**
+ * This method sets the foreground color of the tab.
+ *
+ * @param index The index of the tab.
+ * @param foreground The foreground color of the tab.
+ */
+ public void setForegroundAt(int index, Color foreground)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setForeground(foreground);
+ }
+
+ /**
+ * This method sets whether the tab is enabled.
+ *
+ * @param index The index of the tab.
+ * @param enabled Whether the tab is enabled.
+ */
+ public void setEnabledAt(int index, boolean enabled)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setEnabled(enabled);
+ }
+
+ /**
+ * This method sets the component associated with the tab.
+ *
+ * @param index The index of the tab.
+ * @param component The component associated with the tab.
+ */
+ public void setComponentAt(int index, Component component)
+ {
+ checkIndex(index, 0, tabs.size());
+ ((Page) tabs.elementAt(index)).setComponent(component);
+ }
+
+ /**
+ * This method sets the displayed mnemonic index of the tab.
+ *
+ * @param tabIndex The index of the tab.
+ * @param mnemonicIndex The mnemonic index.
+ */
+ public void setDisplayedMnemonicIndexAt(int tabIndex, int mnemonicIndex)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ ((Page) tabs.elementAt(tabIndex)).setDisplayedMnemonicIndex(mnemonicIndex);
+ }
+
+ /**
+ * This method sets the mnemonic for the tab.
+ *
+ * @param tabIndex The index of the tab.
+ * @param mnemonic The mnemonic.
+ */
+ public void setMnemonicAt(int tabIndex, int mnemonic)
+ {
+ checkIndex(tabIndex, 0, tabs.size());
+ ((Page) tabs.elementAt(tabIndex)).setMnemonic(mnemonic);
+ }
+
+ /**
+ * This method finds the index of a tab given the title.
+ *
+ * @param title The title that belongs to a tab.
+ *
+ * @return The index of the tab that has the title or -1 if not found.
+ */
+ public int indexOfTab(String title)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getTitle().equals(title))
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method finds the index of a tab given the icon.
+ *
+ * @param icon The icon that belongs to a tab.
+ *
+ * @return The index of the tab that has the icon or -1 if not found.
+ */
+ public int indexOfTab(Icon icon)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getIcon() == icon)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method finds the index of a tab given the component.
+ *
+ * @param component A component associated with a tab.
+ *
+ * @return The index of the tab that has this component or -1 if not found.
+ */
+ public int indexOfComponent(Component component)
+ {
+ int index = -1;
+ for (int i = 0; i < tabs.size(); i++)
+ {
+ if (((Page) tabs.elementAt(i)).getComponent() == component)
+ {
+ index = i;
+ break;
+ }
+ }
+ return index;
+ }
+
+ /**
+ * This method returns a tab index given an (x,y) location. The origin of
+ * the (x,y) pair will be the JTabbedPane's top left position. The tab
+ * returned will be the one that contains the point. This method is
+ * delegated to the UI.
+ *
+ * @param x The x coordinate of the point.
+ * @param y The y coordinate of the point.
+ *
+ * @return The index of the tab that contains the point.
+ */
+ public int indexAtLocation(int x, int y)
+ {
+ return ((TabbedPaneUI) ui).tabForCoordinate(this, x, y);
+ }
+
+ /**
+ * This method returns the tooltip text given a mouse event.
+ *
+ * @param event The mouse event.
+ *
+ * @return The tool tip text that is associated with this mouse event.
+ */
+ public String getToolTipText(MouseEvent event)
+ {
+ int index = indexAtLocation(event.getX(), event.getY());
+ return ((Page) tabs.elementAt(index)).getTip();
+ }
+
+ /**
+ * Returns a string describing the attributes for the
+ * JTabbedPane component, for use in debugging. The return
+ * value is guaranteed to be non-null, but the format of the
+ * string may vary between implementations.
+ *
+ * @return A string describing the attributes of the
+ * JTabbedPane.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",tabPlacement=");
+ if (tabPlacement == TOP)
+ sb.append("TOP");
+ if (tabPlacement == BOTTOM)
+ sb.append("BOTTOM");
+ if (tabPlacement == LEFT)
+ sb.append("LEFT");
+ if (tabPlacement == RIGHT)
+ sb.append("RIGHT");
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JTabbedPane component.
+ *
+ * @return The accessible context (an instance of
+ * {@link AccessibleJTabbedPane}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJTabbedPane ctx = new AccessibleJTabbedPane();
+ addChangeListener(ctx);
+ accessibleContext = ctx;
+ }
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java
new file mode 100644
index 000000000..b60c67aa3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTable.java
@@ -0,0 +1,5157 @@
+/* JTable.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.util.EventObject;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleExtendedTable;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleTable;
+import javax.accessibility.AccessibleTableModelChange;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.event.TableColumnModelEvent;
+import javax.swing.event.TableColumnModelListener;
+import javax.swing.event.TableModelEvent;
+import javax.swing.event.TableModelListener;
+import javax.swing.plaf.TableUI;
+import javax.swing.table.DefaultTableCellRenderer;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.DefaultTableModel;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableCellEditor;
+import javax.swing.table.TableCellRenderer;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+
+/**
+ * The table component, displaying information, organized in rows and columns.
+ * The table can be placed in the scroll bar and have the optional header
+ * that is always visible. Cell values may be editable after double clicking
+ * on the cell. Cell columns may have various data types, that are
+ * displayed and edited by the different renderers and editors. It is possible
+ * to set different column width. The columns are also resizeable by
+ * dragging the column boundary in the header.
+ */
+public class JTable
+ extends JComponent
+ implements TableModelListener, Scrollable, TableColumnModelListener,
+ ListSelectionListener, CellEditorListener, Accessible
+{
+ /**
+ * Provides accessibility support for JTable.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTable
+ extends AccessibleJComponent
+ implements AccessibleSelection, ListSelectionListener, TableModelListener,
+ TableColumnModelListener, CellEditorListener, PropertyChangeListener,
+ AccessibleExtendedTable
+ {
+
+ /**
+ * Provides accessibility support for table cells.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTableCell
+ extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ /**
+ * The table of this cell.
+ */
+ private JTable table;
+
+ /**
+ * The row index of this cell.
+ */
+ private int row;
+
+ /**
+ * The column index of this cell.
+ */
+ private int column;
+
+ /**
+ * The index of this cell inside the AccessibleJTable parent.
+ */
+ private int index;
+
+ /**
+ * Creates a new AccessibleJTableCell.
+ *
+ * @param t the table
+ * @param r the row
+ * @param c the column
+ * @param i the index of this cell inside the accessible table parent
+ */
+ public AccessibleJTableCell(JTable t, int r, int c, int i)
+ {
+ table = t;
+ row = r;
+ column = c;
+ index = i;
+ }
+
+ /**
+ * Returns the accessible row for the table cell.
+ *
+ * @return the accessible row for the table cell
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ // TODO: What is the role of the table cell?
+ // Seems like the RI returns UNKNOWN here for 'normal' cells, might
+ // be different for special renderers though (not tested yet).
+ return AccessibleRole.UNKNOWN;
+ }
+
+ /**
+ * Returns the accessible state set of this accessible table cell.
+ *
+ * @return the accessible state set of this accessible table cell
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = new AccessibleStateSet();
+
+ // Figure out the SHOWING state.
+ Rectangle visibleRect = getVisibleRect();
+ Rectangle cellRect = getCellRect(row, column, false);
+ if (visibleRect.intersects(cellRect))
+ state.add(AccessibleState.SHOWING);
+
+ // Figure out SELECTED state.
+ if (isCellSelected(row, column))
+ state.add(AccessibleState.SELECTED);
+
+ // Figure out ACTIVE state.
+ if (row == getSelectedRow() && column == getSelectedColumn())
+ state.add(AccessibleState.ACTIVE);
+
+ // TRANSIENT seems to be always set in the RI.
+ state.add(AccessibleState.TRANSIENT);
+
+ // TODO: Any other state to handle here?
+ return state;
+ }
+
+ /**
+ * Returns the index of this cell in the parent object.
+ *
+ * @return the index of this cell in the parent object
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return index;
+ }
+
+ /**
+ * Returns the number of children of this object. Table cells cannot have
+ * children, so we return 0 here.
+ *
+ * @return 0
+ */
+ public int getAccessibleChildrenCount()
+ {
+ return 0;
+ }
+
+ /**
+ * Returns the accessible child at index i. Table cells
+ * don't have children, so we return null here.
+ *
+ * @return null
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the locale setting for this accessible table cell.
+ *
+ * @return the locale setting for this accessible table cell
+ */
+ public Locale getLocale()
+ {
+ // TODO: For now, we return english here. This must be fixed as soon
+ // as we have a localized Swing.
+ return Locale.ENGLISH;
+ }
+
+ /**
+ * Returns the accessible context of this table cell. Since accessible
+ * table cells are their own accessible context, we return
+ * this.
+ *
+ * @return the accessible context of this table cell
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the background color of this cell.
+ *
+ * @return the background color of this cell
+ */
+ public Color getBackground()
+ {
+ return table.getBackground();
+ }
+
+ /**
+ * Sets the background of the cell. Since table cells cannot have
+ * individual background colors, this method does nothing. Set the
+ * background directly on the table instead.
+ *
+ * @param color not used
+ */
+ public void setBackground(Color color)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the foreground color of the table cell.
+ *
+ * @return the foreground color of the table cell
+ */
+ public Color getForeground()
+ {
+ return table.getForeground();
+ }
+
+ /**
+ * Sets the foreground of the cell. Since table cells cannot have
+ * individual foreground colors, this method does nothing. Set the
+ * foreground directly on the table instead.
+ *
+ * @param color not used
+ */
+ public void setForeground(Color color)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the cursor for this table cell.
+ *
+ * @return the cursor for this table cell
+ */
+ public Cursor getCursor()
+ {
+ return table.getCursor();
+ }
+
+ /**
+ * Sets the cursor of the cell. Since table cells cannot have
+ * individual cursors, this method does nothing. Set the
+ * cursor directly on the table instead.
+ *
+ * @param cursor not used
+ */
+ public void setCursor(Cursor cursor)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the font of the table cell.
+ *
+ * @return the font of the table cell
+ */
+ public Font getFont()
+ {
+ return table.getFont();
+ }
+
+ /**
+ * Sets the font of the cell. Since table cells cannot have
+ * individual fonts, this method does nothing. Set the
+ * font directly on the table instead.
+ *
+ * @param font not used
+ */
+ public void setFont(Font font)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the font metrics for a specified font.
+ *
+ * @param font the font for which we return the metrics
+ *
+ * @return the font metrics for a specified font
+ */
+ public FontMetrics getFontMetrics(Font font)
+ {
+ return table.getFontMetrics(font);
+ }
+
+ /**
+ * Returns true if this table cell is enabled,
+ * false otherwise.
+ *
+ * @return true if this table cell is enabled,
+ * false otherwise
+ */
+ public boolean isEnabled()
+ {
+ return table.isEnabled();
+ }
+
+ /**
+ * Table cells cannot be disabled or enabled individually, so this method
+ * does nothing. Set the enabled flag on the table itself.
+ *
+ * @param b not used here
+ */
+ public void setEnabled(boolean b)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns true if this cell is visible, false
+ * otherwise.
+ *
+ * @return true if this cell is visible, false
+ * otherwise
+ */
+ public boolean isVisible()
+ {
+ return table.isVisible();
+ }
+
+ /**
+ * The visibility cannot be set on individual table cells, so this method
+ * does nothing. Set the visibility on the table itself.
+ *
+ * @param b not used
+ */
+ public void setVisible(boolean b)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns true if this table cell is currently showing on
+ * screen.
+ *
+ * @return true if this table cell is currently showing on
+ * screen
+ */
+ public boolean isShowing()
+ {
+ return table.isShowing();
+ }
+
+ /**
+ * Returns true if this table cell contains the location
+ * at point, false otherwise.
+ * point is interpreted as relative to the coordinate system
+ * of the table cell.
+ *
+ * @return true if this table cell contains the location
+ * at point, false otherwise
+ */
+ public boolean contains(Point point)
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ cellRect.x = 0;
+ cellRect.y = 0;
+ return cellRect.contains(point);
+ }
+
+ /**
+ * Returns the screen location of the table cell.
+ *
+ * @return the screen location of the table cell
+ */
+ public Point getLocationOnScreen()
+ {
+ Point tableLoc = table.getLocationOnScreen();
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ tableLoc.x += cellRect.x;
+ tableLoc.y += cellRect.y;
+ return tableLoc;
+ }
+
+ /**
+ * Returns the location of this cell relative to the table's bounds.
+ *
+ * @return the location of this cell relative to the table's bounds
+ */
+ public Point getLocation()
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ return new Point(cellRect.x, cellRect.y);
+ }
+
+ /**
+ * The location of the table cells cannot be manipulated directly, so
+ * this method does nothing.
+ *
+ * @param point not used
+ */
+ public void setLocation(Point point)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the bounds of the cell relative to its table.
+ *
+ * @return the bounds of the cell relative to its table
+ */
+ public Rectangle getBounds()
+ {
+ return table.getCellRect(row, column, true);
+ }
+
+ /**
+ * The bounds of the table cells cannot be manipulated directly, so
+ * this method does nothing.
+ *
+ * @param rectangle not used
+ */
+ public void setBounds(Rectangle rectangle)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Returns the size of the table cell.
+ *
+ * @return the size of the table cell
+ */
+ public Dimension getSize()
+ {
+ Rectangle cellRect = table.getCellRect(row, column, true);
+ return new Dimension(cellRect.width, cellRect.height);
+ }
+
+ /**
+ * The size cannot be set on table cells directly, so this method does
+ * nothing.
+ *
+ * @param dimension not used
+ */
+ public void setSize(Dimension dimension)
+ {
+ // This method does nothing. See API comments.
+ }
+
+ /**
+ * Table cells have no children, so we return null here.
+ *
+ * @return null
+ */
+ public Accessible getAccessibleAt(Point point)
+ {
+ return null;
+ }
+
+ /**
+ * Returns true if this table cell is focus traversable,
+ * false otherwise.
+ *
+ * @return true if this table cell is focus traversable,
+ * false otherwise
+ */
+ public boolean isFocusTraversable()
+ {
+ return table.isFocusable();
+ }
+
+ /**
+ * Requests that this table cell gets the keyboard focus.
+ */
+ public void requestFocus()
+ {
+ // We first set the selection models' lead selection to this cell.
+ table.getColumnModel().getSelectionModel()
+ .setLeadSelectionIndex(column);
+ table.getSelectionModel().setLeadSelectionIndex(row);
+ // Now we request that the table receives focus.
+ table.requestFocus();
+ }
+
+ /**
+ * Adds a focus listener to this cell. The focus listener is really
+ * added to the table, so there is no way to find out when an individual
+ * cell changes the focus.
+ *
+ * @param listener the focus listener to add
+ */
+ public void addFocusListener(FocusListener listener)
+ {
+ table.addFocusListener(listener);
+ }
+
+ /**
+ * Removes a focus listener from the cell. The focus listener is really
+ * removed from the table.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeFocusListener(FocusListener listener)
+ {
+ table.removeFocusListener(listener);
+ }
+
+ }
+
+ protected class AccessibleJTableModelChange
+ implements AccessibleTableModelChange
+ {
+ protected int type;
+ protected int firstRow;
+ protected int lastRow;
+ protected int firstColumn;
+ protected int lastColumn;
+
+ protected AccessibleJTableModelChange(int type, int firstRow,
+ int lastRow, int firstColumn,
+ int lastColumn)
+ {
+ this.type = type;
+ this.firstRow = firstRow;
+ this.lastRow = lastRow;
+ this.firstColumn = firstColumn;
+ this.lastColumn = lastColumn;
+ }
+
+ public int getType()
+ {
+ return type;
+ }
+
+ public int getFirstRow()
+ {
+ return firstRow;
+ }
+
+ public int getLastRow()
+ {
+ return lastRow;
+ }
+
+ public int getFirstColumn()
+ {
+ return firstColumn;
+ }
+
+ public int getLastColumn()
+ {
+ return lastColumn;
+ }
+ }
+
+ /**
+ * The RI returns an instance with this name in
+ * {@link #getAccessibleColumnHeader()}, this makes sense, so we do the
+ * same.
+ */
+ private class AccessibleTableHeader
+ implements AccessibleTable
+ {
+
+ /**
+ * The JTableHeader wrapped by this class.
+ */
+ private JTableHeader header;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param h the JTableHeader to wrap
+ */
+ private AccessibleTableHeader(JTableHeader h)
+ {
+ header = h;
+ }
+
+ /**
+ * Returns the caption for the table header.
+ *
+ * @return the caption for the table header
+ */
+ public Accessible getAccessibleCaption()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the caption for the table header.
+ *
+ * @param caption the caption to set
+ */
+ public void setAccessibleCaption(Accessible caption)
+ {
+ // This seems to be a no-op in the RI, so we do the same.
+ }
+
+ /**
+ * Returns the caption for the table header.
+ *
+ * @return the caption for the table header
+ */
+ public Accessible getAccessibleSummary()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the summary for the table header.
+ *
+ * @param summary the caption to set
+ */
+ public void setAccessibleSummary(Accessible summary)
+ {
+ // This seems to be a no-op in the RI, so we do the same.
+ }
+
+ /**
+ * Returns the number of rows, which is always 1 for the table header.
+ *
+ * @return the number of rows
+ */
+ public int getAccessibleRowCount()
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the number of columns in the table header.
+ *
+ * @return the number of columns in the table header
+ */
+ public int getAccessibleColumnCount()
+ {
+ return header.getColumnModel().getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child at the specified row and column.
+ * The row number is ignored here, and we return an
+ * AccessibleJTableHeaderCell here with the renderer component as
+ * component.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the accessible child at the specified row and column
+ */
+ public Accessible getAccessibleAt(int r, int c)
+ {
+ TableColumn column = header.getColumnModel().getColumn(c);
+ TableCellRenderer rend = column.getHeaderRenderer();
+ if (rend == null)
+ rend = header.getDefaultRenderer();
+ Component comp =
+ rend.getTableCellRendererComponent(header.getTable(),
+ column.getHeaderValue(), false,
+ false, -1, c);
+ return new AccessibleJTableHeaderCell(header, comp, r, c);
+ }
+
+ public int getAccessibleRowExtentAt(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getAccessibleColumnExtentAt(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public AccessibleTable getAccessibleRowHeader()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleRowHeader(AccessibleTable header)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public AccessibleTable getAccessibleColumnHeader()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleColumnHeader(AccessibleTable header)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleRowDescription(int r)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleRowDescription(int r, Accessible description)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleColumnDescription(int c)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setAccessibleColumnDescription(int c, Accessible description)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isAccessibleSelected(int r, int c)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isAccessibleRowSelected(int r)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean isAccessibleColumnSelected(int c)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public int[] getSelectedAccessibleRows()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int[] getSelectedAccessibleColumns()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ }
+
+ /**
+ * The RI returns an instance of such class for table header cells. This
+ * makes sense so I added this class. This still needs to be fully
+ * implemented, I just don't feel motivated enough to do so just now.
+ */
+ private class AccessibleJTableHeaderCell
+ extends AccessibleContext
+ implements Accessible, AccessibleComponent
+ {
+
+ JTableHeader header;
+
+ int columnIndex;
+
+ /**
+ *
+ * @param h the table header.
+ * @param comp
+ * @param r
+ * @param c the column index.
+ */
+ private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r,
+ int c)
+ {
+ header = h;
+ columnIndex = c;
+ }
+
+ /**
+ * Returns the header renderer.
+ *
+ * @return The header renderer.
+ */
+ Component getColumnHeaderRenderer()
+ {
+ TableColumn tc = header.getColumnModel().getColumn(columnIndex);
+ TableCellRenderer r = tc.getHeaderRenderer();
+ if (r == null)
+ r = header.getDefaultRenderer();
+ return r.getTableCellRendererComponent(header.getTable(),
+ tc.getHeaderValue(), false, false, -1, columnIndex);
+ }
+
+ /**
+ * Returns the accessible role for the table header cell.
+ *
+ * @return The accessible role.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ Component renderer = getColumnHeaderRenderer();
+ if (renderer instanceof Accessible)
+ {
+ Accessible ac = (Accessible) renderer;
+ return ac.getAccessibleContext().getAccessibleRole();
+ }
+ return null;
+ }
+
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public int getAccessibleIndexInParent()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public int getAccessibleChildrenCount()
+ {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ public Accessible getAccessibleChild(int i)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Locale getLocale()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ /**
+ * Returns the accessible context.
+ *
+ * @return this.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ public Color getBackground()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setBackground(Color color)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Color getForeground()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setForeground(Color color)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Cursor getCursor()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setCursor(Cursor cursor)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Font getFont()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setFont(Font font)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public FontMetrics getFontMetrics(Font font)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isEnabled()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setEnabled(boolean b)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isVisible()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void setVisible(boolean b)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public boolean isShowing()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public boolean contains(Point point)
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public Point getLocationOnScreen()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public Point getLocation()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setLocation(Point point)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Rectangle getBounds()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setBounds(Rectangle rectangle)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Dimension getSize()
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public void setSize(Dimension dimension)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public Accessible getAccessibleAt(Point point)
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public boolean isFocusTraversable()
+ {
+ // TODO Auto-generated method stub
+ return false;
+ }
+
+ public void requestFocus()
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void addFocusListener(FocusListener listener)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removeFocusListener(FocusListener listener)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ /**
+ * The last selected row. This is needed to track the selection in
+ * {@link #valueChanged(ListSelectionEvent)}.
+ */
+ private int lastSelectedRow;
+
+ /**
+ * The last selected column. This is needed to track the selection in
+ * {@link #valueChanged(ListSelectionEvent)}.
+ */
+ private int lastSelectedColumn;
+
+ /**
+ * The caption of the table.
+ */
+ private Accessible caption;
+
+ /**
+ * The summary of the table.
+ */
+ private Accessible summary;
+
+ /**
+ * Accessible descriptions for rows.
+ */
+ private Accessible[] rowDescriptions;
+
+ /**
+ * Accessible descriptions for columns.
+ */
+ private Accessible[] columnDescriptions;
+
+ /**
+ * Creates a new AccessibleJTable.
+ *
+ * @since JDK1.5
+ */
+ protected AccessibleJTable()
+ {
+ getModel().addTableModelListener(this);
+ getSelectionModel().addListSelectionListener(this);
+ getColumnModel().addColumnModelListener(this);
+ lastSelectedRow = getSelectedRow();
+ lastSelectedColumn = getSelectedColumn();
+ TableCellEditor editor = getCellEditor();
+ if (editor != null)
+ editor.addCellEditorListener(this);
+ }
+
+ /**
+ * Returns the accessible role for the JTable component.
+ *
+ * @return {@link AccessibleRole#TABLE}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TABLE;
+ }
+
+ /**
+ * Returns the accessible table.
+ *
+ * @return this.
+ */
+ public AccessibleTable getAccessibleTable()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of selected items in this table.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectedColumnCount();
+ }
+
+ /**
+ * Returns the selected accessible object with the specified index
+ * i. This basically returns the i-th selected cell in the
+ * table when going though it row-wise, and inside the rows, column-wise.
+ *
+ * @param i the index of the selected object to find
+ *
+ * @return the selected accessible object with the specified index
+ * i
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ Accessible found = null;
+
+ int[] selectedRows = getSelectedRows();
+ int[] selectedColumns = getSelectedColumns();
+ int numCols = getColumnCount();
+ int numRows = getRowCount();
+
+ // We have to go through every selected row and column and count until we
+ // find the specified index. This is potentially inefficient, but I can't
+ // think of anything better atm.
+ if (getRowSelectionAllowed() && getColumnSelectionAllowed())
+ {
+ int current = -1;
+ int newIndex = current;
+ int lastSelectedRow = -1;
+ // Go through the selected rows array, don't forget the selected
+ // cells inside the not-selected rows' columns.
+ for (int j = 0; i < selectedRows.length; i++)
+ {
+ // Handle unselected rows between this selected and the last
+ // selected row, if any.
+ int selectedRow = selectedRows[j];
+ int r = -1;
+ int ci = -1;
+ for (r = lastSelectedRow + 1;
+ r < selectedRow && current < i; r++)
+ {
+ for (ci = 0; ci < selectedColumns.length && current < i;
+ ci++)
+ {
+ current++;
+ }
+ }
+ if (current == i)
+ {
+ // We found the cell in the above loops, now get out of here.
+ found = getAccessibleChild(r * numCols
+ + selectedColumns[ci]);
+ break;
+ }
+
+ // If we're still here, handle the current selected row.
+ if (current < i && current + numCols >= i)
+ {
+ // The cell must be in that row, which one is it?
+ found = getAccessibleChild(r * numCols + (i - current));
+ break;
+ }
+ current += numCols;
+ }
+ if (found == null)
+ {
+ // The cell can still be in the last couple of unselected rows.
+ int r = 0;
+ int ci = 0;
+ for (r = lastSelectedRow + 1;
+ r < numRows && current < i; r++)
+ {
+ for (ci = 0; ci < selectedColumns.length && current < i;
+ ci++)
+ {
+ current++;
+ }
+ }
+ if (current == i)
+ {
+ // We found the cell in the above loops, now get out of here.
+ found = getAccessibleChild(r * numCols
+ + selectedColumns[ci]);
+ }
+ }
+ }
+ // One or more rows can be completely selected.
+ else if (getRowSelectionAllowed())
+ {
+ int c = i % numCols;
+ int r = selectedRows[i / numCols];
+ found = getAccessibleChild(r * numCols + c);
+ }
+ // One or more columns can be completely selected.
+ else if (getRowSelectionAllowed())
+ {
+ int numSelectedColumns = selectedColumns.length;
+ int c = selectedColumns[i % numSelectedColumns];
+ int r = i / numSelectedColumns;
+ found = getAccessibleChild(r * numCols + c);
+ }
+
+ return found;
+ }
+
+ /**
+ * Returns true if the accessible child with the index
+ * i is selected, false otherwise.
+ *
+ * @param i the index of the accessible to check
+ *
+ * @return true if the accessible child with the index
+ * i is selected, false otherwise
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ return isCellSelected(r, c);
+ }
+
+ /**
+ * Adds the accessible child with the specified index i to the
+ * selection.
+ *
+ * @param i the index of the accessible child to add to the selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ changeSelection(r, c, true, false);
+ }
+
+ /**
+ * Removes the accessible child with the specified index i
+ * from the current selection. This will only work on tables that have
+ * cell selection enabled (rowSelectionAllowed == false &&
+ * columnSelectionAllowed == false).
+ *
+ * @param i the index of the accessible to be removed from the selection
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (! getRowSelectionAllowed() && ! getColumnSelectionAllowed())
+ {
+ int r = getAccessibleRowAtIndex(i);
+ int c = getAccessibleColumnAtIndex(i);
+ removeRowSelectionInterval(r, r);
+ removeColumnSelectionInterval(c, c);
+ }
+ }
+
+ /**
+ * Deselects all selected accessible children.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Selects all accessible children that can be selected. This will only
+ * work on tables that support multiple selections and that have individual
+ * cell selection enabled.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ selectAll();
+ }
+
+ /**
+ * Receives notification when the row selection changes and fires
+ * appropriate property change events.
+ *
+ * @param event the list selection event
+ */
+ public void valueChanged(ListSelectionEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
+ Boolean.FALSE, Boolean.TRUE);
+ int r = getSelectedRow();
+ int c = getSelectedColumn();
+ if (r != lastSelectedRow || c != lastSelectedColumn)
+ {
+ Accessible o = getAccessibleAt(lastSelectedRow,
+ lastSelectedColumn);
+ Accessible n = getAccessibleAt(r, c);
+ firePropertyChange(AccessibleContext
+ .ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n);
+ lastSelectedRow = r;
+ lastSelectedColumn = c;
+ }
+ }
+
+ /**
+ * Receives notification when the table model changes. Depending on the
+ * type of change, this method calls {@link #tableRowsInserted} or
+ * {@link #tableRowsDeleted}.
+ *
+ * @param event the table model event
+ */
+ public void tableChanged(TableModelEvent event)
+ {
+ switch (event.getType())
+ {
+ case TableModelEvent.INSERT:
+ tableRowsInserted(event);
+ break;
+ case TableModelEvent.DELETE:
+ tableRowsDeleted(event);
+ break;
+ }
+ }
+
+ /**
+ * Receives notification when one or more rows have been inserted into the
+ * table and fires appropriate property change events.
+ *
+ * @param event the table model event
+ */
+ public void tableRowsInserted(TableModelEvent event)
+ {
+ handleRowChange(event);
+ }
+
+ /**
+ * Receives notification when one or more rows have been deleted from the
+ * table.
+ *
+ * @param event the table model event
+ */
+ public void tableRowsDeleted(TableModelEvent event)
+ {
+ handleRowChange(event);
+ }
+
+ /**
+ * Fires a PropertyChangeEvent for inserted or deleted rows.
+ *
+ * @param event the table model event
+ */
+ private void handleRowChange(TableModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ int firstColumn = event.getColumn();
+ int lastColumn = event.getColumn();
+ if (firstColumn == TableModelEvent.ALL_COLUMNS)
+ {
+ firstColumn = 0;
+ lastColumn = getColumnCount() - 1;
+ }
+ AccessibleJTableModelChange change = new AccessibleJTableModelChange
+ (event.getType(), event.getFirstRow(), event.getLastRow(),
+ firstColumn, lastColumn);
+ firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
+ null, change);
+ }
+
+ public void columnAdded(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.INSERT,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ public void columnRemoved(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.DELETE,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ public void columnMoved(TableColumnModelEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ handleColumnChange(AccessibleTableModelChange.DELETE,
+ event.getFromIndex(), event.getFromIndex());
+ handleColumnChange(AccessibleTableModelChange.INSERT,
+ event.getFromIndex(), event.getToIndex());
+ }
+
+ /**
+ * Fires a PropertyChangeEvent for inserted or deleted columns.
+ *
+ * @param type the type of change
+ * @param from the start of the change
+ * @param to the target of the change
+ */
+ private void handleColumnChange(int type, int from, int to)
+ {
+ AccessibleJTableModelChange change =
+ new AccessibleJTableModelChange(type, 0, 0, from, to);
+ firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
+ null, change);
+ }
+
+ public void columnMarginChanged(ChangeEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ }
+
+ public void columnSelectionChanged(ListSelectionEvent event)
+ {
+ // AFAICS, nothing is done here.
+ }
+
+ public void editingCanceled(ChangeEvent event)
+ {
+ // AFAICS, nothing is done here.
+ }
+
+ public void editingStopped(ChangeEvent event)
+ {
+ firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
+ null, null);
+ }
+
+ /**
+ * Receives notification when any of the JTable's properties changes. This
+ * is used to replace the listeners on the table's model, selection model,
+ * column model and cell editor.
+ *
+ * @param e the property change event
+ */
+ public void propertyChange(PropertyChangeEvent e)
+ {
+ String propName = e.getPropertyName();
+ if (propName.equals("tableModel"))
+ {
+ TableModel oldModel = (TableModel) e.getOldValue();
+ oldModel.removeTableModelListener(this);
+ TableModel newModel = (TableModel) e.getNewValue();
+ newModel.addTableModelListener(this);
+ }
+ else if (propName.equals("columnModel"))
+ {
+ TableColumnModel oldModel = (TableColumnModel) e.getOldValue();
+ oldModel.removeColumnModelListener(this);
+ TableColumnModel newModel = (TableColumnModel) e.getNewValue();
+ newModel.addColumnModelListener(this);
+ }
+ else if (propName.equals("selectionModel"))
+ {
+ ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
+ oldModel.removeListSelectionListener(this);
+ ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
+ newModel.addListSelectionListener(this);
+ }
+ else if (propName.equals("cellEditor"))
+ {
+ CellEditor oldEd = (CellEditor) e.getOldValue();
+ oldEd.removeCellEditorListener(this);
+ CellEditor newEd = (CellEditor) e.getNewValue();
+ newEd.addCellEditorListener(this);
+ }
+ }
+
+ /**
+ * Returns the row number of an accessible child (cell) with the specified
+ * index.
+ *
+ * @param index the index of the cell of which the row number is queried
+ *
+ * @return the row number of an accessible child (cell) with the specified
+ * index
+ */
+ public int getAccessibleRow(int index)
+ {
+ return getAccessibleRowAtIndex(index);
+ }
+
+ /**
+ * Returns the column number of an accessible child (cell) with the
+ * specified index.
+ *
+ * @param index the index of the cell of which the column number is queried
+ *
+ * @return the column number of an accessible child (cell) with the
+ * specified index
+ */
+ public int getAccessibleColumn(int index)
+ {
+ return getAccessibleColumnAtIndex(index);
+ }
+
+ /**
+ * Returns the index of the accessible child at the specified row and
+ * column.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the index of the accessible child at the specified row and
+ * column
+ */
+ public int getAccessibleIndex(int r, int c)
+ {
+ return getAccessibleIndexAt(r, c);
+ }
+
+ /**
+ * Returns the caption of the table.
+ *
+ * @return the caption of the table
+ *
+ * @see #setAccessibleCaption(Accessible)
+ */
+ public Accessible getAccessibleCaption()
+ {
+ return caption;
+ }
+
+ /**
+ * Sets the caption for the table.
+ *
+ * @param c the caption to set
+ */
+ public void setAccessibleCaption(Accessible c)
+ {
+ caption = c;
+ }
+
+ /**
+ * Returns the summary for the table.
+ *
+ * @return the summary for the table
+ */
+ public Accessible getAccessibleSummary()
+ {
+ return summary;
+ }
+
+ /**
+ * Sets the summary for the table.
+ *
+ * @param s the summary to set
+ */
+ public void setAccessibleSummary(Accessible s)
+ {
+ summary = s;
+ }
+
+ /**
+ * Returns the number of rows in the table.
+ *
+ * @return the number of rows in the table
+ */
+ public int getAccessibleRowCount()
+ {
+ return getRowCount();
+ }
+
+ /**
+ * Returns the number of columns in the table.
+ *
+ * @return the number of columns in the table
+ */
+ public int getAccessibleColumnCount()
+ {
+ return getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child at the given index.
+ *
+ * @param index the child index.
+ *
+ * @return The accessible child.
+ */
+ public Accessible getAccessibleChild(int index)
+ {
+ int r = getAccessibleRow(index);
+ int c = getAccessibleColumn(index);
+ return getAccessibleAt(r, c);
+ }
+
+ /**
+ * Returns the accessible child (table cell) at the specified row and
+ * column.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the accessible child (table cell) at the specified row and
+ * column
+ */
+ public Accessible getAccessibleAt(int r, int c)
+ {
+ TableCellRenderer cellRenderer = getCellRenderer(r, c);
+ Component renderer = cellRenderer.getTableCellRendererComponent(
+ JTable.this, getValueAt(r, c), isCellSelected(r, c), false, r, c);
+ if (renderer instanceof Accessible)
+ return (Accessible) renderer;
+ return null;
+ }
+
+ /**
+ * Returns the number of rows that the specified cell occupies. The
+ * standard table cells only occupy one row, so we return 1
+ * here.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the number of rows that the specified cell occupies
+ */
+ public int getAccessibleRowExtentAt(int r, int c)
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the number of columns that the specified cell occupies. The
+ * standard table cells only occupy one column, so we return 1
+ * here.
+ *
+ * @param r the row number
+ * @param c the column number
+ *
+ * @return the number of rows that the specified cell occupies
+ */
+ public int getAccessibleColumnExtentAt(int r, int c)
+ {
+ return 1;
+ }
+
+ /**
+ * Returns the accessible row header.
+ *
+ * @return the accessible row header
+ */
+ public AccessibleTable getAccessibleRowHeader()
+ {
+ // The RI seems to always return null here, so do we.
+ return null;
+ }
+
+ /**
+ * Sets the accessible row header.
+ *
+ * @param header the header to set
+ */
+ public void setAccessibleRowHeader(AccessibleTable header)
+ {
+ // In the RI this seems to be a no-op.
+ }
+
+ /**
+ * Returns the column header.
+ *
+ * @return the column header, or null if there is no column
+ * header
+ */
+ public AccessibleTable getAccessibleColumnHeader()
+ {
+ JTableHeader h = getTableHeader();
+ AccessibleTable header = null;
+ if (h != null)
+ header = new AccessibleTableHeader(h);
+ return header;
+ }
+
+ /**
+ * Sets the accessible column header. The default implementation doesn't
+ * allow changing the header this way, so this is a no-op.
+ *
+ * @param header the accessible column header to set
+ */
+ public void setAccessibleColumnHeader(AccessibleTable header)
+ {
+ // The RI doesn't seem to do anything, so we also do nothing.
+ }
+
+ /**
+ * Returns the accessible description for the row with the specified index,
+ * or null if no description has been set.
+ *
+ * @param r the row for which the description is queried
+ *
+ * @return the accessible description for the row with the specified index,
+ * or null if no description has been set
+ */
+ public Accessible getAccessibleRowDescription(int r)
+ {
+ Accessible descr = null;
+ if (rowDescriptions != null)
+ descr = rowDescriptions[r];
+ return descr;
+ }
+
+ /**
+ * Sets the accessible description for the row with the specified index.
+ *
+ * @param r the row number for which to set the description
+ * @param description the description to set
+ */
+ public void setAccessibleRowDescription(int r, Accessible description)
+ {
+ if (rowDescriptions == null)
+ rowDescriptions = new Accessible[getAccessibleRowCount()];
+ rowDescriptions[r] = description;
+ }
+
+ /**
+ * Returns the accessible description for the column with the specified
+ * index, or null if no description has been set.
+ *
+ * @param c the column for which the description is queried
+ *
+ * @return the accessible description for the column with the specified
+ * index, or null if no description has been set
+ */
+ public Accessible getAccessibleColumnDescription(int c)
+ {
+ Accessible descr = null;
+ if (columnDescriptions != null)
+ descr = columnDescriptions[c];
+ return descr;
+ }
+
+ /**
+ * Sets the accessible description for the column with the specified index.
+ *
+ * @param c the column number for which to set the description
+ * @param description the description to set
+ */
+ public void setAccessibleColumnDescription(int c, Accessible description)
+ {
+ if (columnDescriptions == null)
+ columnDescriptions = new Accessible[getAccessibleRowCount()];
+ columnDescriptions[c] = description;
+ }
+
+ /**
+ * Returns true if the accessible child at the specified
+ * row and column is selected, false otherwise.
+ *
+ * @param r the row number of the child
+ * @param c the column number of the child
+ *
+ * @return true if the accessible child at the specified
+ * row and column is selected, false otherwise
+ */
+ public boolean isAccessibleSelected(int r, int c)
+ {
+ return isCellSelected(r, c);
+ }
+
+ /**
+ * Returns true if the row with the specified index is
+ * selected, false otherwise.
+ *
+ * @param r the row number
+ *
+ * @return true if the row with the specified index is
+ * selected, false otherwise
+ */
+ public boolean isAccessibleRowSelected(int r)
+ {
+ return isRowSelected(r);
+ }
+
+ /**
+ * Returns true if the column with the specified index is
+ * selected, false otherwise.
+ *
+ * @param c the column number
+ *
+ * @return true if the column with the specified index is
+ * selected, false otherwise
+ */
+ public boolean isAccessibleColumnSelected(int c)
+ {
+ return isColumnSelected(c);
+ }
+
+ /**
+ * Returns the indices of all selected rows.
+ *
+ * @return the indices of all selected rows
+ */
+ public int[] getSelectedAccessibleRows()
+ {
+ return getSelectedRows();
+ }
+
+ /**
+ * Returns the indices of all selected columns.
+ *
+ * @return the indices of all selected columns
+ */
+ public int[] getSelectedAccessibleColumns()
+ {
+ return getSelectedColumns();
+ }
+
+ /**
+ * Returns the accessible row at the specified index.
+ *
+ * @param index the index for which to query the row
+ *
+ * @return the row number at the specified table index
+ */
+ public int getAccessibleRowAtIndex(int index)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return index / getColumnCount();
+ }
+
+ /**
+ * Returns the accessible column at the specified index.
+ *
+ * @param index the index for which to query the column
+ *
+ * @return the column number at the specified table index
+ */
+ public int getAccessibleColumnAtIndex(int index)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return index % getColumnCount();
+ }
+
+ /**
+ * Returns the accessible child index at the specified column and row.
+ *
+ * @param row the row
+ * @param column the column
+ *
+ * @return the index of the accessible child at the specified row and
+ * column
+ */
+ public int getAccessibleIndexAt(int row, int column)
+ {
+ // TODO: Back this up by a Mauve test and update API docs accordingly.
+ return row * getColumnCount() + column;
+ }
+ }
+ /**
+ * Handles property changes from the TableColumns of this
+ * JTable.
+ *
+ * More specifically, this triggers a {@link #revalidate()} call if the
+ * preferredWidth of one of the observed columns changes.
+ */
+ class TableColumnPropertyChangeHandler implements PropertyChangeListener
+ {
+ /**
+ * Receives notification that a property of the observed TableColumns has
+ * changed.
+ *
+ * @param ev the property change event
+ */
+ public void propertyChange(PropertyChangeEvent ev)
+ {
+ if (ev.getPropertyName().equals("preferredWidth"))
+ {
+ JTableHeader header = getTableHeader();
+ if (header != null)
+ // Do nothing if the table is in the resizing mode.
+ if (header.getResizingColumn() == null)
+ {
+ TableColumn col = (TableColumn) ev.getSource();
+ header.setResizingColumn(col);
+ doLayout();
+ header.setResizingColumn(null);
+ }
+ }
+ }
+ }
+
+ /**
+ * A cell renderer for boolean values.
+ */
+ private class BooleanCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * The CheckBox that is used for rendering.
+ */
+ private final JCheckBox checkBox;
+
+ /**
+ * Creates a new checkbox based boolean cell renderer. The checkbox is
+ * centered by default.
+ */
+ BooleanCellRenderer()
+ {
+ checkBox = new JCheckBox();
+ checkBox.setHorizontalAlignment(SwingConstants.CENTER);
+ }
+
+ /**
+ * Get the check box.
+ */
+ JCheckBox getCheckBox()
+ {
+ return checkBox;
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ if (isSelected)
+ {
+ checkBox.setBackground(table.getSelectionBackground());
+ checkBox.setForeground(table.getSelectionForeground());
+ }
+ else
+ {
+ checkBox.setBackground(table.getBackground());
+ checkBox.setForeground(table.getForeground());
+ }
+
+ if (hasFocus)
+ {
+ checkBox.setBorder(
+ UIManager.getBorder("Table.focusCellHighlightBorder"));
+ if (table.isCellEditable(row, column))
+ {
+ checkBox.setBackground(
+ UIManager.getColor("Table.focusCellBackground"));
+ checkBox.setForeground(
+ UIManager.getColor("Table.focusCellForeground"));
+ }
+ }
+ else
+ checkBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
+
+ // Null is rendered as false.
+ if (value == null)
+ checkBox.setSelected(false);
+ else
+ {
+ Boolean boolValue = (Boolean) value;
+ checkBox.setSelected(boolValue.booleanValue());
+ }
+ return checkBox;
+ }
+ }
+
+ /**
+ * A cell renderer for Date values.
+ */
+ private class DateCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Date)
+ {
+ Date dateValue = (Date) value;
+ DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
+ setText(df.format(dateValue));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Double values.
+ */
+ private class DoubleCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public DoubleCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Double)
+ {
+ Double doubleValue = (Double) value;
+ NumberFormat nf = NumberFormat.getInstance();
+ setText(nf.format(doubleValue.doubleValue()));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Float values.
+ */
+ private class FloatCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public FloatCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Float)
+ {
+ Float floatValue = (Float) value;
+ NumberFormat nf = NumberFormat.getInstance();
+ setText(nf.format(floatValue.floatValue()));
+ }
+ return this;
+ }
+ }
+
+ /**
+ * A cell renderer for Number values.
+ */
+ private class NumberCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ /**
+ * Creates a new instance of NumberCellRenderer.
+ */
+ public NumberCellRenderer()
+ {
+ setHorizontalAlignment(JLabel.RIGHT);
+ }
+ }
+
+ /**
+ * A cell renderer for Icon values.
+ */
+ private class IconCellRenderer
+ extends DefaultTableCellRenderer
+ {
+ IconCellRenderer()
+ {
+ setHorizontalAlignment(SwingConstants.CENTER);
+ }
+
+
+ /**
+ * Returns the component that is used for rendering the value.
+ *
+ * @param table the JTable
+ * @param value the value of the object
+ * @param isSelected is the cell selected?
+ * @param hasFocus has the cell the focus?
+ * @param row the row to render
+ * @param column the cell to render
+ *
+ * @return this component (the default table cell renderer)
+ */
+ public Component getTableCellRendererComponent(JTable table, Object value,
+ boolean isSelected,
+ boolean hasFocus, int row,
+ int column)
+ {
+ super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
+ row, column);
+ if (value instanceof Icon)
+ {
+ Icon iconValue = (Icon) value;
+ setIcon(iconValue);
+ }
+ else
+ {
+ setIcon(null);
+ }
+ setText("");
+ return this;
+ }
+ }
+
+ /**
+ * The JTable text component (used in editing) always has the table
+ * as its parent. The scrollRectToVisible must be adjusted taking the
+ * relative component position.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+ private class TableTextField extends JTextField
+ {
+ /**
+ * Create the text field without the border.
+ */
+ TableTextField()
+ {
+ setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
+ }
+ }
+
+
+ private static final long serialVersionUID = 3876025080382781659L;
+
+ /**
+ * This table, for referring identically name methods from inner classes.
+ */
+ final JTable this_table = this;
+
+
+ /**
+ * When resizing columns, do not automatically change any columns. In this
+ * case the table should be enclosed in a {@link JScrollPane} in order to
+ * accomodate cases in which the table size exceeds its visible area.
+ */
+ public static final int AUTO_RESIZE_OFF = 0;
+
+ /**
+ * When resizing column i, automatically change only the
+ * single column i+1 to provide or absorb excess space
+ * requirements.
+ */
+ public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
+
+ /**
+ * When resizing column i in a table of n
+ * columns, automatically change all columns in the range [i+1,
+ * n), uniformly, to provide or absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
+
+ /**
+ * When resizing column i in a table of n
+ * columns, automatically change all columns in the range [0,
+ * n) (with the exception of column i) uniformly, to provide or
+ * absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
+
+ /**
+ * When resizing column i in a table of n
+ * columns, automatically change column n-1 (the last column
+ * in the table) to provide or absorb excess space requirements.
+ */
+ public static final int AUTO_RESIZE_LAST_COLUMN = 3;
+
+ /**
+ * A table mapping {@link java.lang.Class} objects to
+ * {@link TableCellEditor} objects. This table is consulted by the
+ * FIXME
+ */
+ protected Hashtable defaultEditorsByColumnClass = new Hashtable();
+
+ /**
+ * A table mapping {@link java.lang.Class} objects to
+ * {@link TableCellEditor} objects. This table is consulted by the
+ * FIXME
+ */
+ protected Hashtable defaultRenderersByColumnClass = new Hashtable();
+
+ /**
+ * The column that is edited, -1 if the table is not edited currently.
+ */
+ protected int editingColumn;
+
+ /**
+ * The row that is edited, -1 if the table is not edited currently.
+ */
+ protected int editingRow;
+
+ /**
+ * The component that is used for editing.
+ * null if the table is not editing currently.
+ *
+ */
+ protected transient Component editorComp;
+
+
+ /**
+ * Whether or not the table should automatically compute a matching
+ * {@link TableColumnModel} and assign it to the {@link #columnModel}
+ * property when the {@link #dataModel} property is changed.
+ *
+ * @see #setModel(TableModel)
+ * @see #createDefaultColumnsFromModel()
+ * @see #setColumnModel(TableColumnModel)
+ * @see #setAutoCreateColumnsFromModel(boolean)
+ * @see #getAutoCreateColumnsFromModel()
+ */
+ protected boolean autoCreateColumnsFromModel;
+
+ /**
+ * A numeric code specifying the resizing behavior of the table. Must be
+ * one of {@link #AUTO_RESIZE_ALL_COLUMNS} (the default), {@link
+ * #AUTO_RESIZE_LAST_COLUMN}, {@link #AUTO_RESIZE_NEXT_COLUMN}, {@link
+ * #AUTO_RESIZE_SUBSEQUENT_COLUMNS}, or {@link #AUTO_RESIZE_OFF}.
+ *
+ * @see #doLayout()
+ * @see #setAutoResizeMode(int)
+ * @see #getAutoResizeMode()
+ */
+ protected int autoResizeMode;
+
+ /**
+ * The height in pixels of any row of the table. All rows in a table are
+ * of uniform height. This differs from column width, which varies on a
+ * per-column basis, and is stored in the individual columns of the
+ * {@link #columnModel}.
+ *
+ * @see #getRowHeight()
+ * @see #setRowHeight(int)
+ * @see TableColumn#getWidth()
+ * @see TableColumn#setWidth(int)
+ */
+ protected int rowHeight;
+
+ /**
+ * The height in pixels of the gap left between any two rows of the table.
+ *
+ * @see #setRowMargin(int)
+ * @see #getRowHeight()
+ * @see #getIntercellSpacing()
+ * @see #setIntercellSpacing(Dimension)
+ * @see TableColumnModel#getColumnMargin()
+ * @see TableColumnModel#setColumnMargin(int)
+ */
+ protected int rowMargin;
+
+ /**
+ * Whether or not the table should allow row selection. If the table
+ * allows both row and column selection, it is said to allow
+ * "cell selection". Previous versions of the JDK supported cell
+ * selection as an independent concept, but it is now represented solely
+ * in terms of simultaneous row and column selection.
+ *
+ * @see TableColumnModel#getColumnSelectionAllowed()
+ * @see #setRowSelectionAllowed(boolean)
+ * @see #getRowSelectionAllowed()
+ * @see #getCellSelectionEnabled()
+ * @see #setCellSelectionEnabled(boolean)
+ */
+ protected boolean rowSelectionAllowed;
+
+ /**
+ * Obsolete. Use {@link #rowSelectionAllowed}, {@link
+ * #getColumnSelectionAllowed}, or the combined methods {@link
+ * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}.
+ */
+ protected boolean cellSelectionEnabled;
+
+ /**
+ * The model for data stored in the table. Confusingly, the published API
+ * requires that this field be called dataModel, despite its
+ * property name. The table listens to its model as a {@link
+ * TableModelListener}.
+ *
+ * @see #tableChanged(TableModelEvent)
+ * @see TableModel#addTableModelListener(TableModelListener)
+ */
+ protected TableModel dataModel;
+
+ /**
+ *
A model of various aspects of the columns of the table, not
+ * including the data stored in them. The {@link TableColumnModel}
+ * is principally concerned with holding a set of {@link TableColumn}
+ * objects, each of which describes the display parameters of a column
+ * and the numeric index of the column from the data model which the
+ * column is presenting.
+ *
+ *
The TableColumnModel also contains a {@link ListSelectionModel} which
+ * indicates which columns are currently selected. This selection model
+ * works in combination with the {@link #selectionModel} of the table
+ * itself to specify a table selection: a combination of row and
+ * column selections.
+ *
+ *
Most application programmers do not need to work with this property
+ * at all: setting {@link #autoCreateColumnsFromModel} will construct the
+ * columnModel automatically, and the table acts as a facade for most of
+ * the interesting properties of the columnModel anyways.
+ *
+ * @see #setColumnModel(TableColumnModel)
+ * @see #getColumnModel()
+ */
+ protected TableColumnModel columnModel;
+
+ /**
+ * A model of the rows of this table which are currently selected. This
+ * model is used in combination with the column selection model held as a
+ * member of the {@link #columnModel} property, to represent the rows and
+ * columns (or both: cells) of the table which are currently selected.
+ *
+ * @see #rowSelectionAllowed
+ * @see #setSelectionModel(ListSelectionModel)
+ * @see #getSelectionModel()
+ * @see TableColumnModel#getSelectionModel()
+ * @see ListSelectionModel#addListSelectionListener(ListSelectionListener)
+ */
+ protected ListSelectionModel selectionModel;
+
+ /**
+ * The current cell editor.
+ */
+ protected TableCellEditor cellEditor;
+
+ /**
+ * Whether or not drag-and-drop is enabled on this table.
+ *
+ * @see #setDragEnabled(boolean)
+ * @see #getDragEnabled()
+ */
+ private boolean dragEnabled;
+
+ /**
+ * The color to paint the grid lines of the table, when either {@link
+ * #showHorizontalLines} or {@link #showVerticalLines} is set.
+ *
+ * @see #setGridColor(Color)
+ * @see #getGridColor()
+ */
+ protected Color gridColor;
+
+ /**
+ * The size this table would prefer its viewport assume, if it is
+ * contained in a {@link JScrollPane}.
+ *
+ * @see #setPreferredScrollableViewportSize(Dimension)
+ * @see #getPreferredScrollableViewportSize()
+ */
+ protected Dimension preferredViewportSize;
+
+ /**
+ * The color to paint the background of selected cells. Fires a property
+ * change event with name {@link #SELECTION_BACKGROUND_CHANGED_PROPERTY}
+ * when its value changes.
+ *
+ * @see #setSelectionBackground(Color)
+ * @see #getSelectionBackground()
+ */
+ protected Color selectionBackground;
+
+ /**
+ * The name carried in property change events when the {@link
+ * #selectionBackground} property changes.
+ */
+ private static final String SELECTION_BACKGROUND_CHANGED_PROPERTY = "selectionBackground";
+
+ /**
+ * The color to paint the foreground of selected cells. Fires a property
+ * change event with name {@link #SELECTION_FOREGROUND_CHANGED_PROPERTY}
+ * when its value changes.
+ *
+ * @see #setSelectionForeground(Color)
+ * @see #getSelectionForeground()
+ */
+ protected Color selectionForeground;
+
+ /**
+ * The name carried in property change events when the
+ * {@link #selectionForeground} property changes.
+ */
+ private static final String SELECTION_FOREGROUND_CHANGED_PROPERTY = "selectionForeground";
+
+ /**
+ * The showHorizontalLines property.
+ */
+ protected boolean showHorizontalLines;
+
+ /**
+ * The showVerticalLines property.
+ */
+ protected boolean showVerticalLines;
+
+ /**
+ * The tableHeader property.
+ */
+ protected JTableHeader tableHeader;
+
+ /**
+ * The property handler for this table's columns.
+ */
+ TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler =
+ new TableColumnPropertyChangeHandler();
+
+ /**
+ * Whether cell editors should receive keyboard focus when the table is
+ * activated.
+ */
+ private boolean surrendersFocusOnKeystroke = false;
+
+ /**
+ * A Rectangle object to be reused in {@link #getCellRect}.
+ */
+ private Rectangle rectCache = new Rectangle();
+
+ /**
+ * Indicates if the rowHeight property has been set by a client program or by
+ * the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRowHeightSet = false;
+
+ /**
+ * Stores the sizes and positions of each row, when using non-uniform row
+ * heights. Initially the height of all rows is equal and stored in
+ * {link #rowHeight}. However, when an application calls
+ * {@link #setRowHeight(int,int)}, the table switches to non-uniform
+ * row height mode which stores the row heights in the SizeSequence
+ * object instead.
+ *
+ * @see #setRowHeight(int)
+ * @see #getRowHeight()
+ * @see #getRowHeight(int)
+ * @see #setRowHeight(int, int)
+ */
+ private SizeSequence rowHeights;
+
+ /**
+ * This editor serves just a marker that the value must be simply changed to
+ * the opposite one instead of starting the editing session.
+ */
+ private transient TableCellEditor booleanInvertingEditor;
+
+ /**
+ * Creates a new JTable instance.
+ */
+ public JTable ()
+ {
+ this(null, null, null);
+ }
+
+ /**
+ * Creates a new JTable instance with the given number
+ * of rows and columns.
+ *
+ * @param numRows an int value
+ * @param numColumns an int value
+ */
+ public JTable (int numRows, int numColumns)
+ {
+ this(new DefaultTableModel(numRows, numColumns));
+ }
+
+ /**
+ * Creates a new JTable instance, storing the given data
+ * array and heaving the given column names. To see the column names,
+ * you must place the JTable into the {@link JScrollPane}.
+ *
+ * @param data an Object[][] the table data
+ * @param columnNames an Object[] the column headers
+ */
+ public JTable(Object[][] data, Object[] columnNames)
+ {
+ this(new DefaultTableModel(data, columnNames));
+ }
+
+ /**
+ * Creates a new JTable instance, using the given data model
+ * object that provides information about the table content. The table model
+ * object is asked for the table size, other features and also receives
+ * notifications in the case when the table has been edited by the user.
+ *
+ * @param model
+ * the table model.
+ */
+ public JTable (TableModel model)
+ {
+ this(model, null, null);
+ }
+
+ /**
+ * Creates a new JTable instance, using the given model object
+ * that provides information about the table content. The table data model
+ * object is asked for the table size, other features and also receives
+ * notifications in the case when the table has been edited by the user. The
+ * table column model provides more detailed control on the table column
+ * related features.
+ *
+ * @param dm
+ * the table data mode
+ * @param cm
+ * the table column model
+ */
+ public JTable (TableModel dm, TableColumnModel cm)
+ {
+ this(dm, cm, null);
+ }
+
+ /**
+ * Creates a new JTable instance, providing data model,
+ * column model and list selection model. The list selection model
+ * manages the selections.
+ *
+ * @param dm data model (manages table data)
+ * @param cm column model (manages table columns)
+ * @param sm list selection model (manages table selections)
+ */
+ public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm)
+ {
+ boolean autoCreate = false;
+ TableColumnModel columnModel;
+ if (cm != null)
+ columnModel = cm;
+ else
+ {
+ columnModel = createDefaultColumnModel();
+ autoCreate = true;
+ }
+
+ // Initialise the intercelar spacing before setting the column model to
+ // avoid firing unnecessary events.
+ // The initial incellar spacing is new Dimenstion(1,1).
+ rowMargin = 1;
+ columnModel.setColumnMargin(1);
+ setColumnModel(columnModel);
+
+ setSelectionModel(sm == null ? createDefaultSelectionModel() : sm);
+ setModel(dm == null ? createDefaultDataModel() : dm);
+ setAutoCreateColumnsFromModel(autoCreate);
+ initializeLocalVars();
+
+ // The following four lines properly set the lead selection indices.
+ // After this, the UI will handle the lead selection indices.
+ // FIXME: this should probably not be necessary, if the UI is installed
+ // before the TableModel is set then the UI will handle things on its
+ // own, but certain variables need to be set before the UI can be installed
+ // so we must get the correct order for all the method calls in this
+ // constructor.
+ // These four lines are not needed. A Mauve test that shows this is
+ // gnu.testlet.javax.swing.JTable.constructors(linesNotNeeded).
+ // selectionModel.setAnchorSelectionIndex(-1);
+ // selectionModel.setLeadSelectionIndex(-1);
+ // columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
+ // columnModel.getSelectionModel().setLeadSelectionIndex(-1);
+ updateUI();
+ }
+
+ /**
+ * Creates a new JTable instance that uses data and column
+ * names, stored in {@link Vector}s.
+ *
+ * @param data the table data
+ * @param columnNames the table column names.
+ */
+ public JTable(Vector data, Vector columnNames)
+ {
+ this(new DefaultTableModel(data, columnNames));
+ }
+
+ /**
+ * Initialize local variables to default values.
+ */
+ protected void initializeLocalVars()
+ {
+ setTableHeader(createDefaultTableHeader());
+ if (autoCreateColumnsFromModel)
+ createDefaultColumnsFromModel();
+ this.columnModel.addColumnModelListener(this);
+
+ this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
+ setRowHeight(16);
+ this.rowMargin = 1;
+ this.rowSelectionAllowed = true;
+
+ // this.accessibleContext = new AccessibleJTable();
+ this.cellEditor = null;
+
+ // COMPAT: Both Sun and IBM have drag enabled
+ this.dragEnabled = false;
+ this.preferredViewportSize = new Dimension(450,400);
+ this.showHorizontalLines = true;
+ this.showVerticalLines = true;
+ this.editingColumn = -1;
+ this.editingRow = -1;
+ }
+
+ /**
+ * Add the new table column. The table column class allows to specify column
+ * features more precisely, setting the preferred width, column data type
+ * (column class) and table headers.
+ *
+ * There is no need the add columns to the table if the default column
+ * handling is sufficient.
+ *
+ * @param column
+ * the new column to add.
+ */
+ public void addColumn(TableColumn column)
+ {
+ if (column.getHeaderValue() == null)
+ {
+ String name = dataModel.getColumnName(column.getModelIndex());
+ column.setHeaderValue(name);
+ }
+
+ columnModel.addColumn(column);
+ column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
+ }
+
+ /**
+ * Create the default editors for this table. The default method creates
+ * the editor for Booleans.
+ *
+ * Other fields are edited as strings at the moment.
+ */
+ protected void createDefaultEditors()
+ {
+ JCheckBox box = new BooleanCellRenderer().getCheckBox();
+ box.setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
+ box.setBorderPainted(true);
+ booleanInvertingEditor = new DefaultCellEditor(box);
+ setDefaultEditor(Boolean.class, booleanInvertingEditor);
+ }
+
+ /**
+ * Create the default renderers for this table. The default method creates
+ * renderers for Boolean, Number, Double, Date, Icon and ImageIcon.
+ *
+ */
+ protected void createDefaultRenderers()
+ {
+ setDefaultRenderer(Boolean.class, new BooleanCellRenderer());
+ setDefaultRenderer(Number.class, new NumberCellRenderer());
+ setDefaultRenderer(Double.class, new DoubleCellRenderer());
+ setDefaultRenderer(Double.class, new FloatCellRenderer());
+ setDefaultRenderer(Date.class, new DateCellRenderer());
+ setDefaultRenderer(Icon.class, new IconCellRenderer());
+ setDefaultRenderer(ImageIcon.class, new IconCellRenderer());
+ }
+
+ /**
+ * @deprecated 1.0.2, replaced by new JScrollPane(JTable)
+ */
+ public static JScrollPane createScrollPaneForTable(JTable table)
+ {
+ return new JScrollPane(table);
+ }
+
+ /**
+ * Create the default table column model that is used if the user-defined
+ * column model is not provided. The default method creates
+ * {@link DefaultTableColumnModel}.
+ *
+ * @return the created table column model.
+ */
+ protected TableColumnModel createDefaultColumnModel()
+ {
+ return new DefaultTableColumnModel();
+ }
+
+ /**
+ * Create the default table data model that is used if the user-defined
+ * data model is not provided. The default method creates
+ * {@link DefaultTableModel}.
+ *
+ * @return the created table data model.
+ */
+ protected TableModel createDefaultDataModel()
+ {
+ return new DefaultTableModel();
+ }
+
+ /**
+ * Create the default table selection model that is used if the user-defined
+ * selection model is not provided. The default method creates
+ * {@link DefaultListSelectionModel}.
+ *
+ * @return the created table data model.
+ */
+ protected ListSelectionModel createDefaultSelectionModel()
+ {
+ return new DefaultListSelectionModel();
+ }
+
+ /**
+ * Create the default table header, if the user - defined table header is not
+ * provided.
+ *
+ * @return the default table header.
+ */
+ protected JTableHeader createDefaultTableHeader()
+ {
+ return new JTableHeader(columnModel);
+ }
+
+ /**
+ * Invoked when the column is added. Revalidates and repains the table.
+ */
+ public void columnAdded (TableColumnModelEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column margin is changed.
+ * Revalidates and repains the table.
+ */
+ public void columnMarginChanged (ChangeEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column is moved. Revalidates and repains the table.
+ */
+ public void columnMoved (TableColumnModelEvent event)
+ {
+ if (isEditing())
+ editingCanceled(null);
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the column is removed. Revalidates and repains the table.
+ */
+ public void columnRemoved (TableColumnModelEvent event)
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Invoked when the the column selection changes, repaints the changed
+ * columns. It is not recommended to override this method, register the
+ * listener instead.
+ */
+ public void columnSelectionChanged (ListSelectionEvent event)
+ {
+ // We must limit the indices to the bounds of the JTable's model, because
+ // we might get values of -1 or greater then columnCount in the case
+ // when columns get removed.
+ int idx0 = Math.max(0, Math.min(getColumnCount() - 1,
+ event.getFirstIndex()));
+ int idxn = Math.max(0, Math.min(getColumnCount() - 1,
+ event.getLastIndex()));
+
+ int minRow = 0;
+ int maxRow = getRowCount() - 1;
+ if (getRowSelectionAllowed())
+ {
+ minRow = selectionModel.getMinSelectionIndex();
+ maxRow = selectionModel.getMaxSelectionIndex();
+ int leadRow = selectionModel.getLeadSelectionIndex();
+ if (minRow == -1 && maxRow == -1)
+ {
+ minRow = leadRow;
+ maxRow = leadRow;
+ }
+ else
+ {
+ // In this case we need to repaint also the range to leadRow, not
+ // only between min and max.
+ if (leadRow != -1)
+ {
+ minRow = Math.min(minRow, leadRow);
+ maxRow = Math.max(maxRow, leadRow);
+ }
+ }
+ }
+ if (minRow != -1 && maxRow != -1)
+ {
+ Rectangle first = getCellRect(minRow, idx0, false);
+ Rectangle last = getCellRect(maxRow, idxn, false);
+ Rectangle dirty = SwingUtilities.computeUnion(first.x, first.y,
+ first.width,
+ first.height, last);
+ repaint(dirty);
+ }
+ }
+
+ /**
+ * Invoked when the editing is cancelled.
+ */
+ public void editingCanceled (ChangeEvent event)
+ {
+ if (editorComp!=null)
+ {
+ remove(editorComp);
+ repaint(editorComp.getBounds());
+ editorComp = null;
+ }
+ }
+
+ /**
+ * Finish the current editing session and update the table with the
+ * new value by calling {@link #setValueAt}.
+ *
+ * @param event the change event
+ */
+ public void editingStopped (ChangeEvent event)
+ {
+ if (editorComp!=null)
+ {
+ remove(editorComp);
+ setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn);
+ repaint(editorComp.getBounds());
+ editorComp = null;
+ }
+ requestFocusInWindow();
+ }
+
+ /**
+ * Invoked when the table changes.
+ * null means everything changed.
+ */
+ public void tableChanged (TableModelEvent event)
+ {
+ // update the column model from the table model if the structure has
+ // changed and the flag autoCreateColumnsFromModel is set
+ if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW))
+ handleCompleteChange(event);
+ else if (event.getType() == TableModelEvent.INSERT)
+ handleInsert(event);
+ else if (event.getType() == TableModelEvent.DELETE)
+ handleDelete(event);
+ else
+ handleUpdate(event);
+ }
+
+ /**
+ * Handles a request for complete relayout. This is the case when
+ * event.getFirstRow() == TableModelEvent.HEADER_ROW.
+ *
+ * @param ev the table model event
+ */
+ private void handleCompleteChange(TableModelEvent ev)
+ {
+ clearSelection();
+ checkSelection();
+ rowHeights = null;
+ if (getAutoCreateColumnsFromModel())
+ createDefaultColumnsFromModel();
+ else
+ resizeAndRepaint();
+ }
+
+ /**
+ * Handles table model insertions.
+ *
+ * @param ev the table model event
+ */
+ private void handleInsert(TableModelEvent ev)
+ {
+ // Sync selection model with data model.
+ int first = ev.getFirstRow();
+ if (first < 0)
+ first = 0;
+ int last = ev.getLastRow();
+ if (last < 0)
+ last = getRowCount() - 1;
+ selectionModel.insertIndexInterval(first, last - first + 1, true);
+ checkSelection();
+
+ // For variable height rows we must update the SizeSequence thing.
+ if (rowHeights != null)
+ {
+ rowHeights.insertEntries(first, last - first + 1, rowHeight);
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ else
+ {
+ // Repaint the dirty region and revalidate.
+ int rowHeight = getRowHeight();
+ Rectangle dirty = new Rectangle(0, first * rowHeight,
+ getColumnModel().getTotalColumnWidth(),
+ (getRowCount() - first) * rowHeight);
+ repaint(dirty);
+ }
+ revalidate();
+ }
+
+ /**
+ * Handles table model deletions.
+ *
+ * @param ev the table model event
+ */
+ private void handleDelete(TableModelEvent ev)
+ {
+ // Sync selection model with data model.
+ int first = ev.getFirstRow();
+ if (first < 0)
+ first = 0;
+ int last = ev.getLastRow();
+ if (last < 0)
+ last = getRowCount() - 1;
+
+ selectionModel.removeIndexInterval(first, last);
+
+ checkSelection();
+
+ if (dataModel.getRowCount() == 0)
+ clearSelection();
+
+ // For variable height rows we must update the SizeSequence thing.
+ if (rowHeights != null)
+ {
+ rowHeights.removeEntries(first, last - first + 1);
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ else
+ {
+ // Repaint the dirty region and revalidate.
+ int rowHeight = getRowHeight();
+ int oldRowCount = getRowCount() + last - first + 1;
+ Rectangle dirty = new Rectangle(0, first * rowHeight,
+ getColumnModel().getTotalColumnWidth(),
+ (oldRowCount - first) * rowHeight);
+ repaint(dirty);
+ }
+ revalidate();
+ }
+
+ /**
+ * Handles table model updates without structural changes.
+ *
+ * @param ev the table model event
+ */
+ private void handleUpdate(TableModelEvent ev)
+ {
+ if (rowHeights == null)
+ {
+ // Some cells have been changed without changing the structure.
+ // Figure out the dirty rectangle and repaint.
+ int firstRow = ev.getFirstRow();
+ int lastRow = ev.getLastRow();
+ int col = ev.getColumn();
+ Rectangle dirty;
+ if (col == TableModelEvent.ALL_COLUMNS)
+ {
+ // All columns changed.
+ dirty = new Rectangle(0, firstRow * getRowHeight(),
+ getColumnModel().getTotalColumnWidth(), 0);
+ }
+ else
+ {
+ // Only one cell or column of cells changed.
+ // We need to convert to view column first.
+ int column = convertColumnIndexToModel(col);
+ dirty = getCellRect(firstRow, column, false);
+ }
+
+ // Now adjust the height of the dirty region.
+ dirty.height = (lastRow + 1) * getRowHeight();
+ // .. and repaint.
+ repaint(dirty);
+ }
+ else
+ {
+ // TODO: We repaint the whole thing when the rows have variable
+ // heights. We might want to handle this better though.
+ repaint();
+ }
+ }
+
+ /**
+ * Helper method for adjusting the lead and anchor indices when the
+ * table structure changed. This sets the lead and anchor to -1 if there's
+ * no more rows, or set them to 0 when they were at -1 and there are actually
+ * some rows now.
+ */
+ private void checkSelection()
+ {
+ TableModel m = getModel();
+ ListSelectionModel sm = selectionModel;
+ if (m != null)
+ {
+ int lead = sm.getLeadSelectionIndex();
+ int c = m.getRowCount();
+ if (c == 0 && lead != -1)
+ {
+ // No rows in the model, reset lead and anchor to -1.
+ sm.setValueIsAdjusting(true);
+ sm.setAnchorSelectionIndex(-1);
+ sm.setLeadSelectionIndex(-1);
+ sm.setValueIsAdjusting(false);
+ }
+ else if (c != 0 && lead == -1)
+ {
+ // We have rows, but no lead/anchor. Set them to 0. We
+ // do a little trick here so that the actual selection is not
+ // touched.
+ if (sm.isSelectedIndex(0))
+ sm.addSelectionInterval(0, 0);
+ else
+ sm.removeSelectionInterval(0, 0);
+ }
+ // Nothing to do in the other cases.
+ }
+ }
+
+ /**
+ * Invoked when another table row is selected. It is not recommended
+ * to override thid method, register the listener instead.
+ */
+ public void valueChanged (ListSelectionEvent event)
+ {
+ // If we are in the editing process, end the editing session.
+ if (isEditing())
+ editingStopped(null);
+
+ // Repaint the changed region.
+ int first = Math.max(0, Math.min(getRowCount() - 1, event.getFirstIndex()));
+ int last = Math.max(0, Math.min(getRowCount() - 1, event.getLastIndex()));
+ Rectangle rect1 = getCellRect(first, 0, false);
+ Rectangle rect2 = getCellRect(last, getColumnCount() - 1, false);
+ Rectangle dirty = SwingUtilities.computeUnion(rect2.x, rect2.y,
+ rect2.width, rect2.height,
+ rect1);
+ repaint(dirty);
+ }
+
+ /**
+ * Returns index of the column that contains specified point
+ * or -1 if this table doesn't contain this point.
+ *
+ * @param point point to identify the column
+ * @return index of the column that contains specified point or
+ * -1 if this table doesn't contain this point.
+ */
+ public int columnAtPoint(Point point)
+ {
+ int ncols = getColumnCount();
+ Dimension gap = getIntercellSpacing();
+ TableColumnModel cols = getColumnModel();
+ int x = point.x;
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ int width = cols.getColumn(i).getWidth()
+ + (gap == null ? 0 : gap.width);
+ if (0 <= x && x < width)
+ return i;
+ x -= width;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns index of the row that contains specified point or -1 if this table
+ * doesn't contain this point.
+ *
+ * @param point point to identify the row
+ * @return index of the row that contains specified point or -1 if this table
+ * doesn't contain this point.
+ */
+ public int rowAtPoint(Point point)
+ {
+ if (point != null)
+ {
+ int nrows = getRowCount();
+ int r;
+ int y = point.y;
+ if (rowHeights == null)
+ {
+ int height = getRowHeight();
+ r = y / height;
+ }
+ else
+ r = rowHeights.getIndex(y);
+
+ if (r < 0 || r >= nrows)
+ return -1;
+ else
+ return r;
+ }
+ else
+ return -1;
+ }
+
+ /**
+ * Calculate the visible rectangle for a particular row and column. The
+ * row and column are specified in visual terms; the column may not match
+ * the {@link #dataModel} column.
+ *
+ * @param row the visible row to get the cell rectangle of
+ *
+ * @param column the visible column to get the cell rectangle of, which may
+ * differ from the {@link #dataModel} column
+ *
+ * @param includeSpacing whether or not to include the cell margins in the
+ * resulting cell. If false, the result will only contain the
+ * inner area of the target cell, not including its margins.
+ *
+ * @return a rectangle enclosing the specified cell
+ */
+ public Rectangle getCellRect(int row,
+ int column,
+ boolean includeSpacing)
+ {
+ Rectangle cellRect = new Rectangle(0, 0, 0, 0);
+
+ // Check for valid range vertically.
+ if (row >= getRowCount())
+ {
+ cellRect.height = getHeight();
+ }
+ else if (row >= 0)
+ {
+ cellRect.height = getRowHeight(row);
+ if (rowHeights == null)
+ cellRect.y = row * cellRect.height;
+ else
+ cellRect.y = rowHeights.getPosition(row);
+
+ if (! includeSpacing)
+ {
+ // The rounding here is important.
+ int rMargin = getRowMargin();
+ cellRect.y += rMargin / 2;
+ cellRect.height -= rMargin;
+ }
+ }
+ // else row < 0, y = height = 0
+
+ // Check for valid range horizontally.
+ if (column < 0)
+ {
+ if (! getComponentOrientation().isLeftToRight())
+ {
+ cellRect.x = getWidth();
+ }
+ }
+ else if (column >= getColumnCount())
+ {
+ if (getComponentOrientation().isLeftToRight())
+ {
+ cellRect.x = getWidth();
+ }
+ }
+ else
+ {
+ TableColumnModel tcm = getColumnModel();
+ if (getComponentOrientation().isLeftToRight())
+ {
+ for (int i = 0; i < column; i++)
+ cellRect.x += tcm.getColumn(i).getWidth();
+ }
+ else
+ {
+ for (int i = tcm.getColumnCount() - 1; i > column; i--)
+ cellRect.x += tcm.getColumn(i).getWidth();
+ }
+ cellRect.width = tcm.getColumn(column).getWidth();
+ if (! includeSpacing)
+ {
+ // The rounding here is important.
+ int cMargin = tcm.getColumnMargin();
+ cellRect.x += cMargin / 2;
+ cellRect.width -= cMargin;
+ }
+ }
+
+ return cellRect;
+ }
+
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ getColumnModel().getSelectionModel().clearSelection();
+ }
+
+ /**
+ * Get the value of the selectedRow property by delegation to
+ * the {@link ListSelectionModel#getMinSelectionIndex} method of the
+ * {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRow property
+ */
+ public int getSelectedRow ()
+ {
+ return selectionModel.getMinSelectionIndex();
+ }
+
+ /**
+ * Get the value of the {@link #selectionModel} property.
+ *
+ * @return The current value of the property
+ */
+ public ListSelectionModel getSelectionModel()
+ {
+ //Neither Sun nor IBM returns null if rowSelection not allowed
+ return selectionModel;
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
+ {
+ int block;
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ block = visibleRect.width;
+ }
+ else
+ {
+ int rowHeight = getRowHeight();
+ if (rowHeight > 0)
+ block = Math.max(rowHeight, // Little hack for useful rounding.
+ (visibleRect.height / rowHeight) * rowHeight);
+ else
+ block = visibleRect.height;
+ }
+ return block;
+ }
+
+ /**
+ * Get the value of the scrollableTracksViewportHeight property.
+ *
+ * @return The constant value false
+ */
+ public boolean getScrollableTracksViewportHeight()
+ {
+ return false;
+ }
+
+ /**
+ * Get the value of the scrollableTracksViewportWidth property.
+ *
+ * @return true unless the {@link #autoResizeMode} property is
+ * AUTO_RESIZE_OFF
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ if (autoResizeMode == AUTO_RESIZE_OFF)
+ return false;
+ else
+ return true;
+ }
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given scrolling
+ * direction and orientation. This method handles a partially exposed row by
+ * returning the distance required to completely expose the item. When
+ * scrolling the top item is completely exposed.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ int unit;
+ if (orientation == SwingConstants.HORIZONTAL)
+ unit = 100;
+ else
+ {
+ unit = getRowHeight();
+ // The following adjustment doesn't work for variable height rows.
+ // It fully exposes partially visible rows in the scrolling direction.
+ if (rowHeights == null)
+ {
+ if (direction > 0)
+ {
+ // Scroll down.
+ // How much pixles are exposed from the last item?
+ int exposed = (visibleRect.y + visibleRect.height) % unit;
+ if (exposed > 0 && exposed < unit - 1)
+ unit = unit - exposed - 1;
+ }
+ else
+ {
+ // Scroll up.
+ int exposed = visibleRect.y % unit;
+ if (exposed > 0 && exposed < unit)
+ unit = exposed;
+ }
+ }
+ }
+ return unit;
+ }
+
+ /**
+ * Get the cell editor, suitable for editing the given cell. The default
+ * method requests the editor from the column model. If the column model does
+ * not provide the editor, the call is forwarded to the
+ * {@link #getDefaultEditor(Class)} with the parameter, obtained from
+ * {@link TableModel#getColumnClass(int)}.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ * @return the editor to edit that cell
+ */
+ public TableCellEditor getCellEditor(int row, int column)
+ {
+ TableCellEditor editor = columnModel.getColumn(column).getCellEditor();
+
+ if (editor == null)
+ {
+ int mcolumn = convertColumnIndexToModel(column);
+ editor = getDefaultEditor(dataModel.getColumnClass(mcolumn));
+ }
+
+ return editor;
+ }
+
+ /**
+ * Get the default editor for editing values of the given type
+ * (String, Boolean and so on).
+ *
+ * @param columnClass the class of the value that will be edited.
+ *
+ * @return the editor, suitable for editing this data type
+ */
+ public TableCellEditor getDefaultEditor(Class> columnClass)
+ {
+ if (defaultEditorsByColumnClass.containsKey(columnClass))
+ return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass);
+ else
+ {
+ JTextField t = new TableTextField();
+ TableCellEditor r = new DefaultCellEditor(t);
+ defaultEditorsByColumnClass.put(columnClass, r);
+ return r;
+ }
+ }
+
+ /**
+ * Get the cell renderer for rendering the given cell.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ * @return the cell renderer to render that cell.
+ */
+ public TableCellRenderer getCellRenderer(int row, int column)
+ {
+ TableCellRenderer renderer = null;
+ if (columnModel.getColumnCount() > 0)
+ renderer = columnModel.getColumn(column).getCellRenderer();
+ if (renderer == null)
+ {
+ int mcolumn = convertColumnIndexToModel(column);
+ renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn));
+ }
+ return renderer;
+ }
+
+ /**
+ * Set default renderer for rendering the given data type.
+ *
+ * @param columnClass the data type (String, Boolean and so on) that must be
+ * rendered.
+ * @param rend the renderer that will rend this data type
+ */
+ public void setDefaultRenderer(Class> columnClass, TableCellRenderer rend)
+ {
+ defaultRenderersByColumnClass.put(columnClass, rend);
+ }
+
+ /**
+ * Get the default renderer for rendering the given data type.
+ *
+ * @param columnClass the data that must be rendered
+ *
+ * @return the appropriate defauld renderer for rendering that data type.
+ */
+ public TableCellRenderer getDefaultRenderer(Class> columnClass)
+ {
+ if (defaultRenderersByColumnClass.containsKey(columnClass))
+ return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass);
+ else
+ {
+ TableCellRenderer r = new DefaultTableCellRenderer();
+ defaultRenderersByColumnClass.put(columnClass, r);
+ return r;
+ }
+ }
+
+ /**
+ * Convert the table model index into the table column number.
+ * The model number need not match the real column position. The columns
+ * may be rearranged by the user with mouse at any time by dragging the
+ * column headers.
+ *
+ * @param vc the column number (0=first).
+ *
+ * @return the table column model index of this column.
+ *
+ * @see TableColumn#getModelIndex()
+ */
+ public int convertColumnIndexToModel(int vc)
+ {
+ if (vc < 0)
+ return vc;
+ else
+ return columnModel.getColumn(vc).getModelIndex();
+ }
+
+ /**
+ * Convert the table column number to the table column model index.
+ * The model number need not match the real column position. The columns
+ * may be rearranged by the user with mouse at any time by dragging the
+ * column headers.
+ *
+ * @param mc the table column index (0=first).
+ *
+ * @return the table column number in the model
+ *
+ * @see TableColumn#getModelIndex()
+ */
+ public int convertColumnIndexToView(int mc)
+ {
+ if (mc < 0)
+ return mc;
+ int ncols = getColumnCount();
+ for (int vc = 0; vc < ncols; ++vc)
+ {
+ if (columnModel.getColumn(vc).getModelIndex() == mc)
+ return vc;
+ }
+ return -1;
+ }
+
+ /**
+ * Prepare the renderer for rendering the given cell.
+ *
+ * @param renderer the renderer being prepared
+ * @param row the row of the cell being rendered
+ * @param column the column of the cell being rendered
+ *
+ * @return the component which .paint() method will paint the cell.
+ */
+ public Component prepareRenderer(TableCellRenderer renderer,
+ int row,
+ int column)
+ {
+ boolean rowSelAllowed = getRowSelectionAllowed();
+ boolean colSelAllowed = getColumnSelectionAllowed();
+ boolean isSel = false;
+ if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed)
+ isSel = isCellSelected(row, column);
+ else
+ isSel = isRowSelected(row) && getRowSelectionAllowed()
+ || isColumnSelected(column) && getColumnSelectionAllowed();
+
+ // Determine the focused cell. The focused cell is the cell at the
+ // leadSelectionIndices of the row and column selection model.
+ ListSelectionModel rowSel = getSelectionModel();
+ ListSelectionModel colSel = getColumnModel().getSelectionModel();
+ boolean hasFocus = hasFocus() && isEnabled()
+ && rowSel.getLeadSelectionIndex() == row
+ && colSel.getLeadSelectionIndex() == column;
+
+ return renderer.getTableCellRendererComponent(this,
+ dataModel.getValueAt(row,
+ convertColumnIndexToModel(column)),
+ isSel,
+ hasFocus,
+ row, column);
+ }
+
+
+ /**
+ * Get the value of the {@link #autoCreateColumnsFromModel} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getAutoCreateColumnsFromModel()
+ {
+ return autoCreateColumnsFromModel;
+ }
+
+ /**
+ * Get the value of the {@link #autoResizeMode} property.
+ *
+ * @return The current value of the property
+ */
+ public int getAutoResizeMode()
+ {
+ return autoResizeMode;
+ }
+
+ /**
+ * Get the value of the {@link #rowHeight} property.
+ *
+ * @return The current value of the property
+ */
+ public int getRowHeight()
+ {
+ return rowHeight;
+ }
+
+ /**
+ * Get the height of the specified row.
+ *
+ * @param row the row whose height to return
+ */
+ public int getRowHeight(int row)
+ {
+ int rh = rowHeight;
+ if (rowHeights != null)
+ rh = rowHeights.getSize(row);
+ return rh;
+ }
+
+
+ /**
+ * Get the value of the {@link #rowMargin} property.
+ *
+ * @return The current value of the property
+ */
+ public int getRowMargin()
+ {
+ return rowMargin;
+ }
+
+ /**
+ * Get the value of the {@link #rowSelectionAllowed} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setRowSelectionAllowed(boolean)
+ */
+ public boolean getRowSelectionAllowed()
+ {
+ return rowSelectionAllowed;
+ }
+
+ /**
+ * Get the value of the {@link #cellSelectionEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getCellSelectionEnabled()
+ {
+ return getColumnSelectionAllowed() && getRowSelectionAllowed();
+ }
+
+ /**
+ * Get the value of the {@link #dataModel} property.
+ *
+ * @return The current value of the property
+ */
+ public TableModel getModel()
+ {
+ return dataModel;
+ }
+
+ /**
+ * Get the value of the columnCount property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the columnCount property
+ */
+ public int getColumnCount()
+ {
+ return columnModel.getColumnCount();
+ }
+
+ /**
+ * Get the value of the rowCount property by
+ * delegation to the {@link #dataModel} field.
+ *
+ * @return The current value of the rowCount property
+ */
+ public int getRowCount()
+ {
+ return dataModel.getRowCount();
+ }
+
+ /**
+ * Get the value of the {@link #columnModel} property.
+ *
+ * @return The current value of the property
+ */
+ public TableColumnModel getColumnModel()
+ {
+ return columnModel;
+ }
+
+ /**
+ * Get the value of the selectedColumn property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumn property
+ */
+ public int getSelectedColumn()
+ {
+ return columnModel.getSelectionModel().getMinSelectionIndex();
+ }
+
+ private static int countSelections(ListSelectionModel lsm)
+ {
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int sum = 0;
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ sum = 1;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ sum = hi - lo + 1;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ++sum;
+ break;
+ }
+ }
+ return sum;
+ }
+
+ private static int[] getSelections(ListSelectionModel lsm)
+ {
+ int sz = countSelections(lsm);
+ int [] ret = new int[sz];
+
+ int lo = lsm.getMinSelectionIndex();
+ int hi = lsm.getMaxSelectionIndex();
+ int j = 0;
+ if (lo != -1 && hi != -1)
+ {
+ switch (lsm.getSelectionMode())
+ {
+ case ListSelectionModel.SINGLE_SELECTION:
+ ret[0] = lo;
+ break;
+
+ case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ ret[j++] = i;
+ break;
+
+ case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
+ for (int i = lo; i <= hi; ++i)
+ if (lsm.isSelectedIndex(i))
+ ret[j++] = i;
+ break;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Get the value of the selectedColumnCount property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumnCount property
+ */
+ public int getSelectedColumnCount()
+ {
+ return countSelections(columnModel.getSelectionModel());
+ }
+
+ /**
+ * Get the value of the selectedColumns property by
+ * delegation to the {@link #columnModel} field.
+ *
+ * @return The current value of the selectedColumns property
+ */
+ public int[] getSelectedColumns()
+ {
+ return getSelections(columnModel.getSelectionModel());
+ }
+
+ /**
+ * Get the value of the columnSelectionAllowed property.
+ *
+ * @return The current value of the columnSelectionAllowed property
+ *
+ * @see #setColumnSelectionAllowed(boolean)
+ */
+ public boolean getColumnSelectionAllowed()
+ {
+ return getColumnModel().getColumnSelectionAllowed();
+ }
+
+ /**
+ * Get the value of the selectedRowCount property by
+ * delegation to the {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRowCount property
+ */
+ public int getSelectedRowCount()
+ {
+ return countSelections(selectionModel);
+ }
+
+ /**
+ * Get the value of the selectedRows property by
+ * delegation to the {@link #selectionModel} field.
+ *
+ * @return The current value of the selectedRows property
+ */
+ public int[] getSelectedRows()
+ {
+ return getSelections(selectionModel);
+ }
+
+ /**
+ * Get the value of the {@link #accessibleContext} property.
+ *
+ * @return The current value of the property
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ {
+ AccessibleJTable ctx = new AccessibleJTable();
+ addPropertyChangeListener(ctx);
+ TableColumnModel tcm = getColumnModel();
+ tcm.addColumnModelListener(ctx);
+ tcm.getSelectionModel().addListSelectionListener(ctx);
+ getSelectionModel().addListSelectionListener(ctx);
+
+ accessibleContext = ctx;
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Get the value of the {@link #cellEditor} property.
+ *
+ * @return The current value of the property
+ */
+ public TableCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ /**
+ * Get the value of the {@link #dragEnabled} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Get the value of the {@link #gridColor} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getGridColor()
+ {
+ return gridColor;
+ }
+
+ /**
+ * Get the value of the intercellSpacing property.
+ *
+ * @return The current value of the property
+ */
+ public Dimension getIntercellSpacing()
+ {
+ return new Dimension(columnModel.getColumnMargin(), rowMargin);
+ }
+
+ /**
+ * Get the value of the {@link #preferredViewportSize} property.
+ *
+ * @return The current value of the property
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return preferredViewportSize;
+ }
+
+ /**
+ * Get the value of the {@link #selectionBackground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionBackground()
+ {
+ return selectionBackground;
+ }
+
+ /**
+ * Get the value of the {@link #selectionForeground} property.
+ *
+ * @return The current value of the property
+ */
+ public Color getSelectionForeground()
+ {
+ return selectionForeground;
+ }
+
+ /**
+ * Get the value of the {@link #showHorizontalLines} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getShowHorizontalLines()
+ {
+ return showHorizontalLines;
+ }
+
+ /**
+ * Get the value of the {@link #showVerticalLines} property.
+ *
+ * @return The current value of the property
+ */
+ public boolean getShowVerticalLines()
+ {
+ return showVerticalLines;
+ }
+
+ /**
+ * Get the value of the {@link #tableHeader} property.
+ *
+ * @return The current value of the property
+ */
+ public JTableHeader getTableHeader()
+ {
+ return tableHeader;
+ }
+
+ /**
+ * Removes specified column from displayable columns of this table.
+ *
+ * @param column column to removed
+ */
+ public void removeColumn(TableColumn column)
+ {
+ columnModel.removeColumn(column);
+ }
+
+ /**
+ * Moves column at the specified index to new given location.
+ *
+ * @param column index of the column to move
+ * @param targetColumn index specifying new location of the column
+ */
+ public void moveColumn(int column,int targetColumn)
+ {
+ columnModel.moveColumn(column, targetColumn);
+ }
+
+ /**
+ * Set the value of the {@link #autoCreateColumnsFromModel} flag. If the
+ * flag changes from false to true, the
+ * {@link #createDefaultColumnsFromModel()} method is called.
+ *
+ * @param autoCreate the new value of the flag.
+ */
+ public void setAutoCreateColumnsFromModel(boolean autoCreate)
+ {
+ if (autoCreateColumnsFromModel != autoCreate)
+ {
+ autoCreateColumnsFromModel = autoCreate;
+ if (autoCreate)
+ createDefaultColumnsFromModel();
+ }
+ }
+
+ /**
+ * Set the value of the {@link #autoResizeMode} property.
+ *
+ * @param a The new value of the autoResizeMode property
+ */
+ public void setAutoResizeMode(int a)
+ {
+ autoResizeMode = a;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the height for all rows in the table. If you want to change the
+ * height of a single row instead, use {@link #setRowHeight(int, int)}.
+ *
+ * @param r the height to set for all rows
+ *
+ * @see #getRowHeight()
+ * @see #setRowHeight(int, int)
+ * @see #getRowHeight(int)
+ */
+ public void setRowHeight(int r)
+ {
+ if (r < 1)
+ throw new IllegalArgumentException();
+
+ clientRowHeightSet = true;
+
+ rowHeight = r;
+ rowHeights = null;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the height of a single row in the table.
+ *
+ * @param rh the new row height
+ * @param row the row to change the height of
+ */
+ public void setRowHeight(int row, int rh)
+ {
+ if (rowHeights == null)
+ {
+ rowHeights = new SizeSequence(getRowCount(), rowHeight);
+ }
+ rowHeights.setSize(row, rh);
+ }
+
+ /**
+ * Set the value of the {@link #rowMargin} property.
+ *
+ * @param r The new value of the rowMargin property
+ */
+ public void setRowMargin(int r)
+ {
+ rowMargin = r;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #rowSelectionAllowed} property.
+ *
+ * @param r The new value of the rowSelectionAllowed property
+ *
+ * @see #getRowSelectionAllowed()
+ */
+ public void setRowSelectionAllowed(boolean r)
+ {
+ if (rowSelectionAllowed != r)
+ {
+ rowSelectionAllowed = r;
+ firePropertyChange("rowSelectionAllowed", !r, r);
+ repaint();
+ }
+ }
+
+ /**
+ * Set the value of the {@link #cellSelectionEnabled} property.
+ *
+ * @param c The new value of the cellSelectionEnabled property
+ */
+ public void setCellSelectionEnabled(boolean c)
+ {
+ setColumnSelectionAllowed(c);
+ setRowSelectionAllowed(c);
+ // for backward-compatibility sake:
+ cellSelectionEnabled = true;
+ }
+
+ /**
+ *
Set the value of the {@link #dataModel} property.
+ *
+ *
Unregister this as a {@link TableModelListener} from
+ * previous {@link #dataModel} and register it with new parameter
+ * m.
+ *
+ * @param m The new value of the model property
+ */
+ public void setModel(TableModel m)
+ {
+ // Throw exception is m is null.
+ if (m == null)
+ throw new IllegalArgumentException();
+
+ // Don't do anything if setting the current model again.
+ if (dataModel == m)
+ return;
+
+ TableModel oldModel = dataModel;
+
+ // Remove table as TableModelListener from old model.
+ if (dataModel != null)
+ dataModel.removeTableModelListener(this);
+
+ if (m != null)
+ {
+ // Set property.
+ dataModel = m;
+
+ // Add table as TableModelListener to new model.
+ dataModel.addTableModelListener(this);
+
+ // Notify the tableChanged method.
+ tableChanged(new TableModelEvent(dataModel,
+ TableModelEvent.HEADER_ROW));
+
+ // Automatically create columns.
+ if (autoCreateColumnsFromModel)
+ createDefaultColumnsFromModel();
+ }
+
+ // This property is bound, so we fire a property change event.
+ firePropertyChange("model", oldModel, dataModel);
+
+ // Repaint table.
+ revalidate();
+ repaint();
+ }
+
+ /**
+ *
Set the value of the {@link #columnModel} property.
+ *
+ *
Unregister this as a {@link TableColumnModelListener}
+ * from previous {@link #columnModel} and register it with new parameter
+ * c.
+ *
+ * @param c The new value of the columnModel property
+ */
+ public void setColumnModel(TableColumnModel c)
+ {
+ if (c == null)
+ throw new IllegalArgumentException();
+ TableColumnModel tmp = columnModel;
+ if (tmp != null)
+ tmp.removeColumnModelListener(this);
+ if (c != null)
+ c.addColumnModelListener(this);
+ columnModel = c;
+ if (dataModel != null && columnModel != null)
+ {
+ int ncols = getColumnCount();
+ TableColumn column;
+ for (int i = 0; i < ncols; ++i)
+ {
+ column = columnModel.getColumn(i);
+ if (column.getHeaderValue()==null)
+ column.setHeaderValue(dataModel.getColumnName(i));
+ }
+ }
+
+ // according to Sun's spec we also have to set the tableHeader's
+ // column model here
+ if (tableHeader != null)
+ tableHeader.setColumnModel(c);
+
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Set the value of the columnSelectionAllowed property.
+ *
+ * @param c The new value of the property
+ *
+ * @see #getColumnSelectionAllowed()
+ */
+ public void setColumnSelectionAllowed(boolean c)
+ {
+ if (columnModel.getColumnSelectionAllowed() != c)
+ {
+ columnModel.setColumnSelectionAllowed(c);
+ firePropertyChange("columnSelectionAllowed", !c, c);
+ repaint();
+ }
+ }
+
+ /**
+ *
Set the value of the {@link #selectionModel} property.
+ *
+ *
Unregister this as a {@link ListSelectionListener}
+ * from previous {@link #selectionModel} and register it with new
+ * parameter s.
+ *
+ * @param s The new value of the selectionModel property
+ */
+ public void setSelectionModel(ListSelectionModel s)
+ {
+ if (s == null)
+ throw new IllegalArgumentException();
+ ListSelectionModel tmp = selectionModel;
+ if (tmp != null)
+ tmp.removeListSelectionListener(this);
+ if (s != null)
+ s.addListSelectionListener(this);
+ selectionModel = s;
+ checkSelection();
+ }
+
+ /**
+ * Set the value of the selectionMode property by
+ * delegation to the {@link #selectionModel} field. The same selection
+ * mode is set for row and column selection models.
+ *
+ * @param s The new value of the property
+ */
+ public void setSelectionMode(int s)
+ {
+ selectionModel.setSelectionMode(s);
+ columnModel.getSelectionModel().setSelectionMode(s);
+
+ repaint();
+ }
+
+ /**
+ *
Set the value of the {@link #cellEditor} property.
+ *
+ *
Unregister this as a {@link CellEditorListener} from
+ * previous {@link #cellEditor} and register it with new parameter
+ * c.
+ *
+ * @param c The new value of the cellEditor property
+ */
+ public void setCellEditor(TableCellEditor c)
+ {
+ TableCellEditor tmp = cellEditor;
+ if (tmp != null)
+ tmp.removeCellEditorListener(this);
+ if (c != null)
+ c.addCellEditorListener(this);
+ cellEditor = c;
+ }
+
+ /**
+ * Set the value of the {@link #dragEnabled} property.
+ *
+ * @param d The new value of the dragEnabled property
+ */
+ public void setDragEnabled(boolean d)
+ {
+ dragEnabled = d;
+ }
+
+ /**
+ * Set the value of the {@link #gridColor} property.
+ *
+ * @param g The new value of the gridColor property
+ */
+ public void setGridColor(Color g)
+ {
+ gridColor = g;
+ repaint();
+ }
+
+ /**
+ * Set the value of the intercellSpacing property.
+ *
+ * @param i The new value of the intercellSpacing property
+ */
+ public void setIntercellSpacing(Dimension i)
+ {
+ rowMargin = i.height;
+ columnModel.setColumnMargin(i.width);
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #preferredViewportSize} property.
+ *
+ * @param p The new value of the preferredViewportSize property
+ */
+ public void setPreferredScrollableViewportSize(Dimension p)
+ {
+ preferredViewportSize = p;
+ revalidate();
+ repaint();
+ }
+
+ /**
+ *
Set the value of the {@link #selectionBackground} property.
+ *
+ *
Fire a PropertyChangeEvent with name {@link
+ * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if
+ * selectionBackground changed.
+ *
+ * @param s The new value of the selectionBackground property
+ */
+ public void setSelectionBackground(Color s)
+ {
+ Color tmp = selectionBackground;
+ selectionBackground = s;
+ if (((tmp == null && s != null)
+ || (s == null && tmp != null)
+ || (tmp != null && s != null && !tmp.equals(s))))
+ firePropertyChange(SELECTION_BACKGROUND_CHANGED_PROPERTY, tmp, s);
+ repaint();
+ }
+
+ /**
+ *
Set the value of the {@link #selectionForeground} property.
+ *
+ *
Fire a PropertyChangeEvent with name {@link
+ * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if
+ * selectionForeground changed.
+ *
+ * @param s The new value of the selectionForeground property
+ */
+ public void setSelectionForeground(Color s)
+ {
+ Color tmp = selectionForeground;
+ selectionForeground = s;
+ if (((tmp == null && s != null)
+ || (s == null && tmp != null)
+ || (tmp != null && s != null && !tmp.equals(s))))
+ firePropertyChange(SELECTION_FOREGROUND_CHANGED_PROPERTY, tmp, s);
+ repaint();
+ }
+
+ /**
+ * Set the value of the showGrid property.
+ *
+ * @param s The new value of the showGrid property
+ */
+ public void setShowGrid(boolean s)
+ {
+ setShowVerticalLines(s);
+ setShowHorizontalLines(s);
+ }
+
+ /**
+ * Set the value of the {@link #showHorizontalLines} property.
+ *
+ * @param s The new value of the showHorizontalLines property
+ */
+ public void setShowHorizontalLines(boolean s)
+ {
+ showHorizontalLines = s;
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #showVerticalLines} property.
+ *
+ * @param s The new value of the showVerticalLines property
+ */
+ public void setShowVerticalLines(boolean s)
+ {
+ showVerticalLines = s;
+ repaint();
+ }
+
+ /**
+ * Set the value of the {@link #tableHeader} property.
+ *
+ * @param t The new value of the tableHeader property
+ */
+ public void setTableHeader(JTableHeader t)
+ {
+ if (tableHeader != null)
+ tableHeader.setTable(null);
+ tableHeader = t;
+ if (tableHeader != null)
+ tableHeader.setTable(this);
+ revalidate();
+ repaint();
+ }
+
+ protected void configureEnclosingScrollPane()
+ {
+ JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
+ if (jsp != null && tableHeader != null)
+ {
+ jsp.setColumnHeaderView(tableHeader);
+ }
+ }
+
+ protected void unconfigureEnclosingScrollPane()
+ {
+ JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
+ if (jsp != null)
+ {
+ jsp.setColumnHeaderView(null);
+ }
+ }
+
+
+ public void addNotify()
+ {
+ super.addNotify();
+ configureEnclosingScrollPane();
+ }
+
+ public void removeNotify()
+ {
+ super.addNotify();
+ unconfigureEnclosingScrollPane();
+ }
+
+
+ /**
+ * This distributes the superfluous width in a table evenly on its columns.
+ *
+ * The implementation used here is different to that one described in
+ * the JavaDocs. It is much simpler, and seems to work very well.
+ *
+ * TODO: correctly implement the algorithm described in the JavaDoc
+ */
+ private void distributeSpill(TableColumn[] cols, int spill)
+ {
+ int average = spill / cols.length;
+ for (int i = 0; i < cols.length; i++)
+ {
+ if (cols[i] != null)
+ cols[i].setWidth(cols[i].getPreferredWidth() + average);
+ }
+ }
+
+ /**
+ * This distributes the superfluous width in a table, setting the width of the
+ * column being resized strictly to its preferred width.
+ */
+ private void distributeSpillResizing(TableColumn[] cols, int spill,
+ TableColumn resizeIt)
+ {
+ int average = 0;
+ if (cols.length != 1)
+ average = spill / (cols.length-1);
+ for (int i = 0; i < cols.length; i++)
+ {
+ if (cols[i] != null && !cols[i].equals(resizeIt))
+ cols[i].setWidth(cols[i].getPreferredWidth() + average);
+ }
+ resizeIt.setWidth(resizeIt.getPreferredWidth());
+ }
+
+ /**
+ * Set the widths of all columns, taking they preferred widths into
+ * consideration. The excess space, if any, will be distrubuted between
+ * all columns. This method also handles special cases when one of the
+ * collumns is currently being resized.
+ *
+ * @see TableColumn#setPreferredWidth(int)
+ */
+ public void doLayout()
+ {
+ TableColumn resizingColumn = null;
+
+ int ncols = columnModel.getColumnCount();
+ if (ncols < 1)
+ return;
+
+ int prefSum = 0;
+ int rCol = -1;
+
+ if (tableHeader != null)
+ resizingColumn = tableHeader.getResizingColumn();
+
+ for (int i = 0; i < ncols; ++i)
+ {
+ TableColumn col = columnModel.getColumn(i);
+ int p = col.getPreferredWidth();
+ prefSum += p;
+ if (resizingColumn == col)
+ rCol = i;
+ }
+
+ int spill = getWidth() - prefSum;
+
+ if (resizingColumn != null)
+ {
+ TableColumn col;
+ TableColumn [] cols;
+
+ switch (getAutoResizeMode())
+ {
+ case AUTO_RESIZE_LAST_COLUMN:
+ col = columnModel.getColumn(ncols-1);
+ col.setWidth(col.getPreferredWidth() + spill);
+ break;
+
+ case AUTO_RESIZE_NEXT_COLUMN:
+ col = columnModel.getColumn(ncols-1);
+ col.setWidth(col.getPreferredWidth() + spill);
+ break;
+
+ case AUTO_RESIZE_ALL_COLUMNS:
+ cols = new TableColumn[ncols];
+ for (int i = 0; i < ncols; ++i)
+ cols[i] = columnModel.getColumn(i);
+ distributeSpillResizing(cols, spill, resizingColumn);
+ break;
+
+ case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
+
+ // Subtract the width of the non-resized columns from the spill.
+ int w = 0;
+ int wp = 0;
+ TableColumn column;
+ for (int i = 0; i < rCol; i++)
+ {
+ column = columnModel.getColumn(i);
+ w += column.getWidth();
+ wp+= column.getPreferredWidth();
+ }
+
+ // The number of columns right from the column being resized.
+ int n = ncols-rCol-1;
+ if (n>0)
+ {
+ // If there are any columns on the right sied to resize.
+ spill = (getWidth()-w) - (prefSum-wp);
+ int average = spill / n;
+
+ // For all columns right from the column being resized:
+ for (int i = rCol+1; i < ncols; i++)
+ {
+ column = columnModel.getColumn(i);
+ column.setWidth(column.getPreferredWidth() + average);
+ }
+ }
+ resizingColumn.setWidth(resizingColumn.getPreferredWidth());
+ break;
+
+ case AUTO_RESIZE_OFF:
+ default:
+ int prefWidth = resizingColumn.getPreferredWidth();
+ resizingColumn.setWidth(prefWidth);
+ }
+ }
+ else
+ {
+ TableColumn[] cols = new TableColumn[ncols];
+
+ for (int i = 0; i < ncols; ++i)
+ cols[i] = columnModel.getColumn(i);
+
+ distributeSpill(cols, spill);
+ }
+
+ if (editorComp!=null)
+ moveToCellBeingEdited(editorComp);
+
+ int leftBoundary = getLeftResizingBoundary();
+ int width = getWidth() - leftBoundary;
+ repaint(leftBoundary, 0, width, getHeight());
+ if (tableHeader != null)
+ tableHeader.repaint(leftBoundary, 0, width, tableHeader.getHeight());
+ }
+
+ /**
+ * Get the left boundary of the rectangle which changes during the column
+ * resizing.
+ */
+ int getLeftResizingBoundary()
+ {
+ if (tableHeader == null || getAutoResizeMode() == AUTO_RESIZE_ALL_COLUMNS)
+ return 0;
+ else
+ {
+ TableColumn resizingColumn = tableHeader.getResizingColumn();
+ if (resizingColumn == null)
+ return 0;
+
+ int rc = convertColumnIndexToView(resizingColumn.getModelIndex());
+ int p = 0;
+
+ for (int i = 0; i < rc; i++)
+ p += columnModel.getColumn(i).getWidth();
+
+ return p;
+ }
+ }
+
+
+ /**
+ * @deprecated Replaced by doLayout()
+ */
+ public void sizeColumnsToFit(boolean lastColumnOnly)
+ {
+ doLayout();
+ }
+
+ /**
+ * Obsolete since JDK 1.4. Please use doLayout().
+ */
+ public void sizeColumnsToFit(int resizingColumn)
+ {
+ doLayout();
+ }
+
+ public String getUIClassID()
+ {
+ return "TableUI";
+ }
+
+ /**
+ * This method returns the table's UI delegate.
+ *
+ * @return The table's UI delegate.
+ */
+ public TableUI getUI()
+ {
+ return (TableUI) ui;
+ }
+
+ /**
+ * This method sets the table's UI delegate.
+ *
+ * @param ui The table's UI delegate.
+ */
+ public void setUI(TableUI ui)
+ {
+ super.setUI(ui);
+ // The editors and renderers must be recreated because they constructors
+ // may use the look and feel properties.
+ createDefaultEditors();
+ createDefaultRenderers();
+ }
+
+ public void updateUI()
+ {
+ setUI((TableUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Get the class (datatype) of the column. The cells are rendered and edited
+ * differently, depending from they data type.
+ *
+ * @param column the column (not the model index).
+ *
+ * @return the class, defining data type of that column (String.class for
+ * String, Boolean.class for boolean and so on).
+ */
+ public Class> getColumnClass(int column)
+ {
+ return getModel().getColumnClass(convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Get the name of the column. If the column has the column identifier set,
+ * the return value is the result of the .toString() method call on that
+ * identifier. If the identifier is not explicitly set, the returned value
+ * is calculated by
+ * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}.
+ *
+ * @param column the column
+ *
+ * @return the name of that column.
+ */
+ public String getColumnName(int column)
+ {
+ int modelColumn = columnModel.getColumn(column).getModelIndex();
+ return dataModel.getColumnName(modelColumn);
+ }
+
+ /**
+ * Get the column, currently being edited
+ *
+ * @return the column, currently being edited.
+ */
+ public int getEditingColumn()
+ {
+ return editingColumn;
+ }
+
+ /**
+ * Set the column, currently being edited
+ *
+ * @param column the column, currently being edited.
+ */
+ public void setEditingColumn(int column)
+ {
+ editingColumn = column;
+ }
+
+ /**
+ * Get the row currently being edited.
+ *
+ * @return the row, currently being edited.
+ */
+ public int getEditingRow()
+ {
+ return editingRow;
+ }
+
+ /**
+ * Set the row currently being edited.
+ *
+ * @param row the row, that will be edited
+ */
+ public void setEditingRow(int row)
+ {
+ editingRow = row;
+ }
+
+ /**
+ * Get the editor component that is currently editing one of the cells
+ *
+ * @return the editor component or null, if none of the cells is being
+ * edited.
+ */
+ public Component getEditorComponent()
+ {
+ return editorComp;
+ }
+
+ /**
+ * Check if one of the table cells is currently being edited.
+ *
+ * @return true if there is a cell being edited.
+ */
+ public boolean isEditing()
+ {
+ return editorComp != null;
+ }
+
+ /**
+ * Set the default editor for the given column class (column data type).
+ * By default, String is handled by text field and Boolean is handled by
+ * the check box.
+ *
+ * @param columnClass the column data type
+ * @param editor the editor that will edit this data type
+ *
+ * @see TableModel#getColumnClass(int)
+ */
+ public void setDefaultEditor(Class> columnClass, TableCellEditor editor)
+ {
+ if (editor != null)
+ defaultEditorsByColumnClass.put(columnClass, editor);
+ else
+ defaultEditorsByColumnClass.remove(columnClass);
+ }
+
+ public void addColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().addSelectionInterval(index0, index1);
+ }
+
+ public void addRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().addSelectionInterval(index0, index1);
+ }
+
+ public void setColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().setSelectionInterval(index0, index1);
+ }
+
+ public void setRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().setSelectionInterval(index0, index1);
+ }
+
+ public void removeColumnSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getColumnCount()-1)
+ || index1 < 0 || index1 > (getColumnCount()-1)))
+ throw new IllegalArgumentException("Column index out of range.");
+
+ getColumnModel().getSelectionModel().removeSelectionInterval(index0, index1);
+ }
+
+ public void removeRowSelectionInterval(int index0, int index1)
+ {
+ if ((index0 < 0 || index0 > (getRowCount()-1)
+ || index1 < 0 || index1 > (getRowCount()-1)))
+ throw new IllegalArgumentException("Row index out of range.");
+
+ getSelectionModel().removeSelectionInterval(index0, index1);
+ }
+
+ /**
+ * Checks if the given column is selected.
+ *
+ * @param column the column
+ *
+ * @return true if the column is selected (as reported by the selection
+ * model, associated with the column model), false otherwise.
+ */
+ public boolean isColumnSelected(int column)
+ {
+ return getColumnModel().getSelectionModel().isSelectedIndex(column);
+ }
+
+ /**
+ * Checks if the given row is selected.
+ *
+ * @param row the row
+ *
+ * @return true if the row is selected (as reported by the selection model),
+ * false otherwise.
+ */
+ public boolean isRowSelected(int row)
+ {
+ return getSelectionModel().isSelectedIndex(row);
+ }
+
+ /**
+ * Checks if the given cell is selected. The cell is selected if both
+ * the cell row and the cell column are selected.
+ *
+ * @param row the cell row
+ * @param column the cell column
+ *
+ * @return true if the cell is selected, false otherwise
+ */
+ public boolean isCellSelected(int row, int column)
+ {
+ return isRowSelected(row) && isColumnSelected(column);
+ }
+
+ /**
+ * Select all table.
+ */
+ public void selectAll()
+ {
+ // The table is empty - nothing to do!
+ if (getRowCount() == 0 || getColumnCount() == 0)
+ return;
+
+ // rowLead and colLead store the current lead selection indices
+ int rowLead = selectionModel.getLeadSelectionIndex();
+ int colLead = getColumnModel().getSelectionModel().getLeadSelectionIndex();
+ // the following calls to setSelectionInterval change the lead selection
+ // indices
+ setColumnSelectionInterval(0, getColumnCount() - 1);
+ setRowSelectionInterval(0, getRowCount() - 1);
+ // the following addSelectionInterval calls restore the lead selection
+ // indices to their previous values
+ addColumnSelectionInterval(colLead,colLead);
+ addRowSelectionInterval(rowLead, rowLead);
+ }
+
+ /**
+ * Get the cell value at the given position.
+ *
+ * @param row the row to get the value
+ * @param column the actual column number (not the model index)
+ * to get the value.
+ *
+ * @return the cell value, as returned by model.
+ */
+ public Object getValueAt(int row, int column)
+ {
+ return dataModel.getValueAt(row, convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Set value for the cell at the given position. The modified cell is
+ * repainted.
+ *
+ * @param value the value to set
+ * @param row the row of the cell being modified
+ * @param column the column of the cell being modified
+ */
+ public void setValueAt(Object value, int row, int column)
+ {
+ dataModel.setValueAt(value, row, convertColumnIndexToModel(column));
+
+ repaint(getCellRect(row, column, true));
+ }
+
+ /**
+ * Get table column with the given identified.
+ *
+ * @param identifier the column identifier
+ *
+ * @return the table column with this identifier
+ *
+ * @throws IllegalArgumentException if identifier is
+ * null or there is no column with that identifier.
+ *
+ * @see TableColumn#setIdentifier(Object)
+ */
+ public TableColumn getColumn(Object identifier)
+ {
+ return columnModel.getColumn(columnModel.getColumnIndex(identifier));
+ }
+
+ /**
+ * Returns true if the specified cell is editable, and
+ * false otherwise.
+ *
+ * @param row the row index.
+ * @param column the column index.
+ *
+ * @return true if the cell is editable, false otherwise.
+ */
+ public boolean isCellEditable(int row, int column)
+ {
+ return dataModel.isCellEditable(row, convertColumnIndexToModel(column));
+ }
+
+ /**
+ * Clears any existing columns from the JTable's
+ * {@link TableColumnModel} and creates new columns to match the values in
+ * the data ({@link TableModel}) used by the table.
+ *
+ * @see #setAutoCreateColumnsFromModel(boolean)
+ */
+ public void createDefaultColumnsFromModel()
+ {
+ assert columnModel != null : "The columnModel must not be null.";
+
+ // remove existing columns
+ int columnIndex = columnModel.getColumnCount() - 1;
+ while (columnIndex >= 0)
+ {
+ columnModel.removeColumn(columnModel.getColumn(columnIndex));
+ columnIndex--;
+ }
+
+ // add new columns to match the TableModel
+ int columnCount = dataModel.getColumnCount();
+ for (int c = 0; c < columnCount; c++)
+ {
+ TableColumn column = new TableColumn(c);
+ column.setIdentifier(dataModel.getColumnName(c));
+ column.setHeaderValue(dataModel.getColumnName(c));
+ columnModel.addColumn(column);
+ column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
+ }
+ }
+
+ public void changeSelection (int rowIndex, int columnIndex, boolean toggle, boolean extend)
+ {
+ if (toggle && extend)
+ {
+ // Leave the selection state as is, but move the anchor
+ // index to the specified location
+ selectionModel.setAnchorSelectionIndex(rowIndex);
+ getColumnModel().getSelectionModel().setAnchorSelectionIndex(columnIndex);
+ }
+ else if (toggle)
+ {
+ // Toggle the state of the specified cell
+ if (isCellSelected(rowIndex,columnIndex))
+ {
+ selectionModel.removeSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().removeSelectionInterval(columnIndex,columnIndex);
+ }
+ else
+ {
+ selectionModel.addSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().addSelectionInterval(columnIndex,columnIndex);
+ }
+ }
+ else if (extend)
+ {
+ // Extend the previous selection from the anchor to the
+ // specified cell, clearing all other selections
+ selectionModel.setLeadSelectionIndex(rowIndex);
+ getColumnModel().getSelectionModel().setLeadSelectionIndex(columnIndex);
+ }
+ else
+ {
+ // Clear the previous selection and ensure the new cell
+ // is selected
+ selectionModel.clearSelection();
+ selectionModel.setSelectionInterval(rowIndex,rowIndex);
+ getColumnModel().getSelectionModel().clearSelection();
+ getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex);
+
+
+ }
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt(int row, int column)
+ {
+ // Complete the previous editing session, if still active.
+ if (isEditing())
+ editingStopped(new ChangeEvent("editingStopped"));
+
+ TableCellEditor editor = getCellEditor(row, column);
+
+ // The boolean values are inverted by the single click without the
+ // real editing session.
+ if (editor == booleanInvertingEditor && isCellEditable(row, column))
+ {
+ if (Boolean.TRUE.equals(getValueAt(row, column)))
+ setValueAt(Boolean.FALSE, row, column);
+ else
+ setValueAt(Boolean.TRUE, row, column);
+ return false;
+ }
+ else
+ {
+ editingRow = row;
+ editingColumn = column;
+
+ setCellEditor(editor);
+ editorComp = prepareEditor(cellEditor, row, column);
+
+ // Remove the previous editor components, if present. Only one
+ // editor component at time is allowed in the table.
+ removeAll();
+ add(editorComp);
+ moveToCellBeingEdited(editorComp);
+ scrollRectToVisible(editorComp.getBounds());
+ editorComp.requestFocusInWindow();
+
+ // Deliver the should select event.
+ return editor.shouldSelectCell(null);
+ }
+ }
+
+ /**
+ * Move the given component under the cell being edited.
+ * The table must be in the editing mode.
+ *
+ * @param component the component to move.
+ */
+ private void moveToCellBeingEdited(Component component)
+ {
+ Rectangle r = getCellRect(editingRow, editingColumn, true);
+ // Adjust bounding box of the editing component, so that it lies
+ // 'above' the grid on all edges, not only right and bottom.
+ // The table grid is painted only at the right and bottom edge of a cell.
+ r.x -= 1;
+ r.y -= 1;
+ r.width += 1;
+ r.height += 1;
+ component.setBounds(r);
+ }
+
+ /**
+ * Programmatically starts editing the specified cell.
+ *
+ * @param row the row of the cell to edit.
+ * @param column the column of the cell to edit.
+ */
+ public boolean editCellAt (int row, int column, EventObject e)
+ {
+ return editCellAt(row, column);
+ }
+
+ /**
+ * Discards the editor object.
+ */
+ public void removeEditor()
+ {
+ editingStopped(new ChangeEvent(this));
+ }
+
+ /**
+ * Prepares the editor by querying for the value and selection state of the
+ * cell at (row, column).
+ *
+ * @param editor the TableCellEditor to set up
+ * @param row the row of the cell to edit
+ * @param column the column of the cell to edit
+ * @return the Component being edited
+ */
+ public Component prepareEditor (TableCellEditor editor, int row, int column)
+ {
+ return editor.getTableCellEditorComponent
+ (this, getValueAt(row, column), isCellSelected(row, column), row, column);
+ }
+
+ /**
+ * This revalidates the JTable and queues a repaint.
+ */
+ protected void resizeAndRepaint()
+ {
+ revalidate();
+ repaint();
+ }
+
+ /**
+ * Sets whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke. The default setting is
+ * false which means that the table should keep the keyboard
+ * focus until the cell is selected by a mouse click.
+ *
+ * @param value the value to set
+ *
+ * @since 1.4
+ */
+ public void setSurrendersFocusOnKeystroke(boolean value)
+ {
+ // TODO: Implement functionality of this property (in UI impl).
+ surrendersFocusOnKeystroke = value;
+ }
+
+ /**
+ * Returns whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke. The default setting is
+ * false which means that the table should keep the keyboard
+ * focus until the cell is selected by a mouse click.
+ *
+ * @return whether cell editors of this table should receive keyboard focus
+ * when the editor is activated by a keystroke
+ *
+ * @since 1.4
+ */
+ public boolean getSurrendersFocusOnKeystroke()
+ {
+ // TODO: Implement functionality of this property (in UI impl).
+ return surrendersFocusOnKeystroke;
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("rowHeight"))
+ {
+ if (! clientRowHeightSet)
+ {
+ setRowHeight(((Integer) value).intValue());
+ clientRowHeightSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTextArea.java b/libjava/classpath/javax/swing/JTextArea.java
new file mode 100644
index 000000000..51921cc89
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextArea.java
@@ -0,0 +1,607 @@
+/* JTextArea.java --
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Dimension;
+import java.awt.FontMetrics;
+import java.awt.Rectangle;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.View;
+
+/**
+ * The JTextArea component provides a multi-line area for displaying
+ * and editing plain text. The component is designed to act as a lightweight
+ * replacement for the heavyweight java.awt.TextArea component,
+ * which provides similar functionality using native widgets.
+ *
+ *
+ * This component has additional functionality to the AWT class. It follows
+ * the same design pattern as seen in other text components, such as
+ * JTextField, JTextPane and JEditorPane,
+ * and embodied in JTextComponent. These classes separate the text
+ * (the model) from its appearance within the onscreen component (the view). The
+ * text is held within a javax.swing.text.Document object, which can
+ * also maintain relevant style information where necessary. As a result, it is the
+ * document that should be monitored for textual changes, via
+ * DocumentEvents delivered to registered
+ * DocumentListeners, rather than this component.
+ *
+ *
+ * Unlike java.awt.TextArea, JTextArea does not
+ * handle scrolling. Instead, this functionality is delegated to a
+ * JScrollPane, which can contain the text area and handle
+ * scrolling when required. Likewise, the word wrapping functionality
+ * of the AWT component is converted to a property of this component
+ * and the rows and columns properties
+ * are used in calculating the preferred size of the scroll pane's
+ * view port.
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see java.awt.TextArea
+ * @see javax.swing.text.JTextComponent
+ * @see javax.swing.JTextField
+ * @see javax.swing.JTextPane
+ * @see javax.swing.JEditorPane
+ * @see javax.swing.text.Document
+ * @see javax.swing.event.DocumentEvent
+ * @see javax.swing.event.DocumentListener
+ */
+
+public class JTextArea extends JTextComponent
+{
+ /**
+ * Provides accessibility support for JTextArea.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ protected class AccessibleJTextArea extends AccessibleJTextComponent
+ {
+
+ /**
+ * Creates a new AccessibleJTextArea object.
+ */
+ protected AccessibleJTextArea()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible state of this AccessibleJTextArea.
+ *
+ * @return the accessible state of this AccessibleJTextArea
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ /**
+ * Compatible with Sun's JDK
+ */
+ private static final long serialVersionUID = -6141680179310439825L;
+
+ /**
+ * The number of rows used by the component.
+ */
+ private int rows;
+
+ /**
+ * The number of columns used by the component.
+ */
+ private int columns;
+
+ /**
+ * Whether line wrapping is enabled or not.
+ */
+ private boolean lineWrap;
+
+ /**
+ * The number of characters equal to a tab within the text.
+ */
+ private int tabSize = 8;
+
+ private boolean wrapStyleWord;
+
+ /**
+ * Creates a new JTextArea object.
+ */
+ public JTextArea()
+ {
+ this(null, null, 0, 0);
+ }
+
+ /**
+ * Creates a new JTextArea object.
+ *
+ * @param text the initial text
+ */
+ public JTextArea(String text)
+ {
+ this(null, text, 0, 0);
+ }
+
+ /**
+ * Creates a new JTextArea object.
+ *
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(int rows, int columns)
+ {
+ this(null, null, rows, columns);
+ }
+
+ /**
+ * Creates a new JTextArea object.
+ *
+ * @param text the initial text
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(String text, int rows, int columns)
+ {
+ this(null, text, rows, columns);
+ }
+
+ /**
+ * Creates a new JTextArea object.
+ *
+ * @param doc the document model to use
+ */
+ public JTextArea(Document doc)
+ {
+ this(doc, null, 0, 0);
+ }
+
+ /**
+ * Creates a new JTextArea object.
+ *
+ * @param doc the document model to use
+ * @param text the initial text
+ * @param rows the number of rows
+ * @param columns the number of cols
+ *
+ * @exception IllegalArgumentException if rows or columns are negative
+ */
+ public JTextArea(Document doc, String text, int rows, int columns)
+ {
+ setDocument(doc == null ? createDefaultModel() : doc);
+ // Only explicitly setText() when there is actual text since
+ // setText() might be overridden and not expected to be called
+ // from the constructor (as in JEdit).
+ if (text != null)
+ setText(text);
+ setRows(rows);
+ setColumns(columns);
+ }
+
+ /**
+ * Appends the supplied text to the current contents
+ * of the document model.
+ *
+ * @param toAppend the text to append
+ */
+ public void append(String toAppend)
+ {
+ try
+ {
+ getDocument().insertString(getText().length(), toAppend, null);
+ }
+ catch (BadLocationException exception)
+ {
+ /* This shouldn't happen in theory -- but, if it does... */
+ throw new RuntimeException("Unexpected exception occurred.", exception);
+ }
+ if (toAppend != null && toAppend.length() > 0)
+ revalidate();
+ }
+
+ /**
+ * Creates the default document model.
+ *
+ * @return a new default model
+ */
+ protected Document createDefaultModel()
+ {
+ return new PlainDocument();
+ }
+
+ /**
+ * Returns true if the width of this component should be forced
+ * to match the width of a surrounding view port. When line wrapping
+ * is turned on, this method returns true.
+ *
+ * @return true if lines are wrapped.
+ */
+ public boolean getScrollableTracksViewportWidth()
+ {
+ return lineWrap ? true : super.getScrollableTracksViewportWidth();
+ }
+
+ /**
+ * Returns the increment that is needed to expose exactly one new line
+ * of text. This is implemented here to return the values of
+ * {@link #getRowHeight} and {@link #getColumnWidth}, depending on
+ * the value of the argument direction.
+ *
+ * @param visibleRect the view area that is visible in the viewport
+ * @param orientation either {@link SwingConstants#VERTICAL} or
+ * {@link SwingConstants#HORIZONTAL}
+ * @param direction less than zero for up/left scrolling, greater
+ * than zero for down/right scrolling
+ *
+ * @return the increment that is needed to expose exactly one new row
+ * or column of text
+ *
+ * @throws IllegalArgumentException if orientation is invalid
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ if (orientation == SwingConstants.VERTICAL)
+ return getRowHeight();
+ else if (orientation == SwingConstants.HORIZONTAL)
+ return getColumnWidth();
+ else
+ throw new IllegalArgumentException("orientation must be either "
+ + "javax.swing.SwingConstants.VERTICAL "
+ + "or "
+ + "javax.swing.SwingConstants.HORIZONTAL"
+ );
+ }
+
+ /**
+ * Returns the preferred size of that text component in the case
+ * it is embedded within a JScrollPane. This uses the column and
+ * row settings if they are explicitly set, or fall back to
+ * the superclass's behaviour.
+ *
+ * @return the preferred size of that text component in the case
+ * it is embedded within a JScrollPane
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ if ((rows > 0) && (columns > 0))
+ return new Dimension(columns * getColumnWidth(), rows * getRowHeight());
+ else
+ return super.getPreferredScrollableViewportSize();
+ }
+
+ /**
+ * Returns the UI class ID string.
+ *
+ * @return the string "TextAreaUI"
+ */
+ public String getUIClassID()
+ {
+ return "TextAreaUI";
+ }
+
+ /**
+ * Returns the current number of columns.
+ *
+ * @return number of columns
+ */
+ public int getColumns()
+ {
+ return columns;
+ }
+
+ /**
+ * Sets the number of rows.
+ *
+ * @param columns number of columns
+ *
+ * @exception IllegalArgumentException if columns is negative
+ */
+ public void setColumns(int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ if (columns != this.columns)
+ {
+ this.columns = columns;
+ revalidate();
+ }
+ }
+
+ /**
+ * Returns the current number of rows.
+ *
+ * @return number of rows
+ */
+ public int getRows()
+ {
+ return rows;
+ }
+
+ /**
+ * Sets the number of rows.
+ *
+ * @param rows number of rows
+ *
+ * @exception IllegalArgumentException if rows is negative
+ */
+ public void setRows(int rows)
+ {
+ if (rows < 0)
+ throw new IllegalArgumentException();
+
+ if (rows != this.rows)
+ {
+ this.rows = rows;
+ revalidate();
+ }
+ }
+
+ /**
+ * Checks whether line wrapping is enabled.
+ *
+ * @return true if line wrapping is enabled,
+ * false otherwise
+ */
+ public boolean getLineWrap()
+ {
+ return lineWrap;
+ }
+
+ /**
+ * Enables/disables line wrapping.
+ *
+ * @param flag true to enable line wrapping,
+ * false otherwise
+ */
+ public void setLineWrap(boolean flag)
+ {
+ if (lineWrap == flag)
+ return;
+
+ boolean oldValue = lineWrap;
+ lineWrap = flag;
+ firePropertyChange("lineWrap", oldValue, lineWrap);
+ }
+
+ /**
+ * Checks whether word style wrapping is enabled.
+ *
+ * @return true if word style wrapping is enabled,
+ * false otherwise
+ */
+ public boolean getWrapStyleWord()
+ {
+ return wrapStyleWord;
+ }
+
+ /**
+ * Enables/Disables word style wrapping.
+ *
+ * @param flag true to enable word style wrapping,
+ * false otherwise
+ */
+ public void setWrapStyleWord(boolean flag)
+ {
+ if (wrapStyleWord == flag)
+ return;
+
+ boolean oldValue = wrapStyleWord;
+ wrapStyleWord = flag;
+ firePropertyChange("wrapStyleWord", oldValue, wrapStyleWord);
+ }
+
+ /**
+ * Returns the number of characters used for a tab.
+ * This defaults to 8.
+ *
+ * @return the current number of spaces used for a tab.
+ */
+ public int getTabSize()
+ {
+ return tabSize;
+ }
+
+ /**
+ * Sets the number of characters used for a tab to the
+ * supplied value. If a change to the tab size property
+ * occurs (i.e. newSize != tabSize), a property change event
+ * is fired.
+ *
+ * @param newSize The new number of characters to use for a tab.
+ */
+ public void setTabSize(int newSize)
+ {
+ if (tabSize == newSize)
+ return;
+
+ int oldValue = tabSize;
+ tabSize = newSize;
+ firePropertyChange("tabSize", oldValue, tabSize);
+ }
+
+ protected int getColumnWidth()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.charWidth('m');
+ }
+
+ public int getLineCount()
+ {
+ return getDocument().getDefaultRootElement().getElementCount();
+ }
+
+ public int getLineStartOffset(int line)
+ throws BadLocationException
+ {
+ int lineCount = getLineCount();
+
+ if (line < 0 || line > lineCount)
+ throw new BadLocationException("Non-existing line number", line);
+
+ Element lineElem = getDocument().getDefaultRootElement().getElement(line);
+ return lineElem.getStartOffset();
+ }
+
+ public int getLineEndOffset(int line)
+ throws BadLocationException
+ {
+ int lineCount = getLineCount();
+
+ if (line < 0 || line > lineCount)
+ throw new BadLocationException("Non-existing line number", line);
+
+ Element lineElem = getDocument().getDefaultRootElement().getElement(line);
+ return lineElem.getEndOffset();
+ }
+
+ public int getLineOfOffset(int offset)
+ throws BadLocationException
+ {
+ Document doc = getDocument();
+
+ if (offset < doc.getStartPosition().getOffset()
+ || offset >= doc.getEndPosition().getOffset())
+ throw new BadLocationException("offset outside of document", offset);
+
+ return doc.getDefaultRootElement().getElementIndex(offset);
+ }
+
+ protected int getRowHeight()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.getHeight();
+ }
+
+ /**
+ * Inserts the supplied text at the specified position. Nothing
+ * happens in the case that the model or the supplied string is null
+ * or of zero length.
+ *
+ * @param string The string of text to insert.
+ * @param position The position at which to insert the supplied text.
+ * @throws IllegalArgumentException if the position is < 0 or greater
+ * than the length of the current text.
+ */
+ public void insert(String string, int position)
+ {
+ // Retrieve the document model.
+ Document doc = getDocument();
+
+ // Check the model and string for validity.
+ if (doc == null
+ || string == null
+ || string.length() == 0)
+ return;
+
+ // Insert the text into the model.
+ try
+ {
+ doc.insertString(position, string, null);
+ }
+ catch (BadLocationException e)
+ {
+ throw new IllegalArgumentException("The supplied position, "
+ + position + ", was invalid.");
+ }
+ }
+
+ public void replaceRange(String text, int start, int end)
+ {
+ Document doc = getDocument();
+
+ if (start > end
+ || start < doc.getStartPosition().getOffset()
+ || end >= doc.getEndPosition().getOffset())
+ throw new IllegalArgumentException();
+
+ try
+ {
+ doc.remove(start, end - start);
+ doc.insertString(start, text, null);
+ }
+ catch (BadLocationException e)
+ {
+ // This cannot happen as we check offset above.
+ }
+ }
+
+ /**
+ * Returns the preferred size for the JTextArea. This is the maximum of
+ * the size that is needed to display the content and the requested size
+ * as per {@link #getColumns} and {@link #getRows}.
+ *
+ * @return the preferred size of the JTextArea
+ */
+ public Dimension getPreferredSize()
+ {
+ int reqWidth = getColumns() * getColumnWidth();
+ int reqHeight = getRows() * getRowHeight();
+ View view = getUI().getRootView(this);
+ int neededWidth = (int) view.getPreferredSpan(View.HORIZONTAL);
+ int neededHeight = (int) view.getPreferredSpan(View.VERTICAL);
+ return new Dimension(Math.max(reqWidth, neededWidth),
+ Math.max(reqHeight, neededHeight));
+ }
+
+ /**
+ * Returns the accessible context associated with the JTextArea.
+ *
+ * @return the accessible context associated with the JTextArea
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJTextArea();
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTextField.java b/libjava/classpath/javax/swing/JTextField.java
new file mode 100644
index 000000000..69b70b068
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextField.java
@@ -0,0 +1,570 @@
+/* JTextField.java --
+ Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Insets;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.PlainDocument;
+import javax.swing.text.TextAction;
+
+public class JTextField extends JTextComponent
+ implements SwingConstants
+{
+ /**
+ * AccessibleJTextField
+ */
+ protected class AccessibleJTextField extends AccessibleJTextComponent
+ {
+ private static final long serialVersionUID = 8255147276740453036L;
+
+ /**
+ * Constructor AccessibleJTextField
+ */
+ protected AccessibleJTextField()
+ {
+ super();
+ }
+
+ /**
+ * Returns the accessible state of this AccessibleJTextField.
+ *
+ * @return the accessible state of this AccessibleJTextField
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ AccessibleStateSet state = super.getAccessibleStateSet();
+ // TODO: Figure out what state must be added here to the super's state.
+ return state;
+ }
+ }
+
+ private static final long serialVersionUID = 353853209832607592L;
+
+ private static final Action[] actions;
+
+ /**
+ * Name of the action that gets sent when the content of the text field
+ * gets accepted.
+ */
+ public static final String notifyAction = "notify-field-accept";
+
+ static
+ {
+ actions = new Action[1];
+ actions[0] = new TextAction(notifyAction)
+ {
+ public void actionPerformed(ActionEvent event)
+ {
+ JTextField textField = (JTextField) event.getSource();
+ textField.fireActionPerformed();
+ }
+ };
+ }
+
+ private int columns;
+ private int align;
+
+ /** @since 1.3 */
+ private Action action;
+
+ /** @since 1.3 */
+ private String actionCommand;
+
+ private PropertyChangeListener actionPropertyChangeListener;
+
+ /**
+ * The horizontal visibility of the textfield.
+ */
+ private BoundedRangeModel horizontalVisibility;
+
+ /**
+ * Creates a new instance of JTextField.
+ */
+ public JTextField()
+ {
+ this(null, null, 0);
+ }
+
+ /**
+ * Creates a new instance of JTextField.
+ *
+ * @param text the initial text
+ */
+ public JTextField(String text)
+ {
+ this(null, text, 0);
+ }
+
+ /**
+ * Creates a new instance of JTextField.
+ *
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(int columns)
+ {
+ this(null, null, columns);
+ }
+
+ /**
+ * Creates a new instance of JTextField.
+ *
+ * @param text the initial text
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(String text, int columns)
+ {
+ this(null, text, columns);
+ }
+
+ /**
+ * Creates a new instance of JTextField.
+ *
+ * @param doc the document to use
+ * @param text the initial text
+ * @param columns the number of columns
+ *
+ * @exception IllegalArgumentException if columns %lt; 0
+ */
+ public JTextField(Document doc, String text, int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ this.columns = columns;
+
+ // Initialize the horizontal visibility model.
+ horizontalVisibility = new DefaultBoundedRangeModel();
+
+ setDocument(doc == null ? createDefaultModel() : doc);
+
+ if (text != null)
+ setText(text);
+
+ // default value for alignment
+ align = LEADING;
+ }
+
+ /**
+ * Creates the default model for this text field.
+ * This implementation returns an instance of PlainDocument.
+ *
+ * @return a new instance of the default model
+ */
+ protected Document createDefaultModel()
+ {
+ return new PlainDocument();
+ }
+
+ /**
+ * Sets the document to be used for this JTextField.
+ *
+ * This sets the document property filterNewlines to
+ * true and then calls the super behaviour to setup a view and
+ * revalidate the text field.
+ *
+ * @param doc the document to set
+ */
+ public void setDocument(Document doc)
+ {
+ doc.putProperty("filterNewlines", Boolean.TRUE);
+ super.setDocument(doc);
+ }
+
+ /**
+ * Returns the class ID for the UI.
+ *
+ * @return "TextFieldUI";
+ */
+ public String getUIClassID()
+ {
+ return "TextFieldUI";
+ }
+
+ /**
+ * Adds a new listener object to this text field.
+ *
+ * @param listener the listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * Removes a listener object from this text field.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * Returns all registered ActionListener objects.
+ *
+ * @return an array of listeners
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sends an action event to all registered
+ * ActionListener objects.
+ */
+ protected void fireActionPerformed()
+ {
+ ActionEvent event = new ActionEvent(this, 0,
+ actionCommand == null ? getText() : actionCommand);
+ ActionListener[] listeners = getActionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].actionPerformed(event);
+ }
+
+ /**
+ * Returns the number of columns of this text field.
+ *
+ * @return the number of columns
+ */
+ public int getColumns()
+ {
+ return columns;
+ }
+
+ /**
+ * Sets the number of columns and then invalidates the layout.
+ * @param columns the number of columns
+ * @throws IllegalArgumentException if columns < 0
+ */
+ public void setColumns(int columns)
+ {
+ if (columns < 0)
+ throw new IllegalArgumentException();
+
+ this.columns = columns;
+ invalidate();
+ //FIXME: do we need this repaint call?
+ repaint();
+ }
+
+ /**
+ * Returns the horizontal alignment, which is one of: JTextField.LEFT,
+ * JTextField.CENTER, JTextField.RIGHT, JTextField.LEADING,
+ * JTextField.TRAILING.
+ * @return the horizontal alignment
+ */
+ public int getHorizontalAlignment()
+ {
+ return align;
+ }
+
+ /**
+ * Sets the horizontal alignment of the text. Calls invalidate and repaint
+ * and fires a property change event.
+ * @param newAlign must be one of: JTextField.LEFT, JTextField.CENTER,
+ * JTextField.RIGHT, JTextField.LEADING, JTextField.TRAILING.
+ * @throws IllegalArgumentException if newAlign is not one of the above.
+ */
+ public void setHorizontalAlignment(int newAlign)
+ {
+ //FIXME: should throw an IllegalArgumentException if newAlign is invalid
+ if (align == newAlign)
+ return;
+
+ int oldAlign = align;
+ align = newAlign;
+ firePropertyChange("horizontalAlignment", oldAlign, newAlign);
+ invalidate();
+ repaint();
+ }
+
+ /**
+ * Sets the current font and revalidates so the font will take effect.
+ */
+ public void setFont(Font newFont)
+ {
+ super.setFont(newFont);
+ revalidate();
+ }
+
+ /**
+ * Returns the preferred size. If there is a non-zero number of columns,
+ * this is the number of columns multiplied by the column width, otherwise
+ * it returns super.getPreferredSize().
+ */
+ public Dimension getPreferredSize()
+ {
+ Dimension size = super.getPreferredSize();
+
+ if (columns != 0)
+ {
+ Insets i = getInsets();
+ size.width = columns * getColumnWidth() + i.left + i.right;
+ }
+
+ return size;
+ }
+
+ /**
+ * Returns the scroll offset in pixels.
+ *
+ * @return the scroll offset
+ */
+ public int getScrollOffset()
+ {
+ return horizontalVisibility.getValue();
+ }
+
+ /**
+ * Sets the scroll offset in pixels.
+ *
+ * @param offset the scroll offset
+ */
+ public void setScrollOffset(int offset)
+ {
+ // Automatically sets to the highest possible value if
+ // offset is bigger than that.
+ horizontalVisibility.setValue(
+ Math.min(horizontalVisibility.getMaximum()
+ - horizontalVisibility.getExtent(),
+ offset));
+
+ }
+
+ /**
+ * Returns the set of Actions that are commands for the editor.
+ * This is the actions supported by this editor plus the actions
+ * of the UI (returned by JTextComponent.getActions()).
+ */
+ public Action[] getActions()
+ {
+ return TextAction.augmentList(super.getActions(), actions);
+ }
+
+ public void postActionEvent()
+ {
+ String command = actionCommand != null ? actionCommand : getText();
+ ActionEvent event = new ActionEvent(this, 0, command);
+ ActionListener[] listeners = getActionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].actionPerformed(event);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public Action getAction()
+ {
+ return action;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setAction(Action newAction)
+ {
+ if (action == newAction)
+ return;
+
+ if (action != null)
+ {
+ removeActionListener(action);
+ action.removePropertyChangeListener(actionPropertyChangeListener);
+ actionPropertyChangeListener = null;
+ }
+
+ Action oldAction = action;
+ action = newAction;
+
+ if (action != null)
+ {
+ addActionListener(action);
+ actionPropertyChangeListener = createActionPropertyChangeListener(action);
+ action.addPropertyChangeListener(actionPropertyChangeListener);
+ }
+
+ //FIXME: is this a hack? The horizontal alignment hasn't changed
+ firePropertyChange("horizontalAlignment", oldAction, newAction);
+ }
+
+ /**
+ * Sets the command string used in action events.
+ * @since 1.3
+ */
+ public void setActionCommand(String command)
+ {
+ actionCommand = command;
+ }
+
+ /**
+ * @since 1.3
+ */
+ protected PropertyChangeListener createActionPropertyChangeListener(Action action)
+ {
+ return new PropertyChangeListener()
+ {
+ public void propertyChange(PropertyChangeEvent event)
+ {
+ // Update properties "action" and "horizontalAlignment".
+ String name = event.getPropertyName();
+
+ if (name.equals("enabled"))
+ {
+ boolean enabled = ((Boolean) event.getNewValue()).booleanValue();
+ JTextField.this.setEnabled(enabled);
+ }
+ else if (name.equals(Action.SHORT_DESCRIPTION))
+ {
+ JTextField.this.setToolTipText((String) event.getNewValue());
+ }
+ }
+ };
+ }
+
+ /**
+ *
+ * @since 1.3
+ */
+ protected void configurePropertiesFromAction(Action action)
+ {
+ if (action != null)
+ {
+ setEnabled(action.isEnabled());
+ setToolTipText((String) action.getValue(Action.SHORT_DESCRIPTION));
+ }
+ else
+ {
+ setEnabled(true);
+ setToolTipText(null);
+ }
+ }
+
+ /**
+ * Returns the column width, which is the width of the character m
+ * for the font in use.
+ * @return the width of the character m for the font in use.
+ */
+ protected int getColumnWidth()
+ {
+ FontMetrics metrics = getToolkit().getFontMetrics(getFont());
+ return metrics.charWidth('m');
+ }
+
+ /**
+ * Returns the accessible context associated with the JTextField.
+ *
+ * @return the accessible context associated with the JTextField
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJTextField();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the bounded range model that describes the horizontal visibility
+ * of the text field in the case when the text does not fit into the
+ * available space. The actual values of this model are managed by the look
+ * and feel implementation.
+ *
+ * @return the bounded range model that describes the horizontal visibility
+ */
+ public BoundedRangeModel getHorizontalVisibility()
+ {
+ return horizontalVisibility;
+ }
+
+ /**
+ * Returns true, unless this is embedded in a
+ * JViewport in which case the viewport takes responsibility of
+ * validating.
+ *
+ * @return true, unless this is embedded in a
+ * JViewport in which case the viewport takes
+ * responsibility of validating
+ */
+ public boolean isValidateRoot()
+ {
+ return ! (getParent() instanceof JViewport);
+ }
+
+ public void scrollRectToVisible(Rectangle r)
+ {
+ int v = horizontalVisibility.getValue();
+
+ // The extent value is the inner width of the text field.
+ int e = horizontalVisibility.getExtent();
+ Insets i = getInsets();
+
+ // The x value in the rectangle (usually) denotes the new location
+ // of the caret. We check whether the location lies inside the left or
+ // right border and scroll into the appropriate direction.
+ // The calculation has to be shifted by the BoundedRangeModel's value
+ // because that value was already used to calculate r.x (this happens
+ // as part of a modelToView() call in FieldView).
+ if (r.x < i.left)
+ setScrollOffset(v + r.x - i.left);
+ else if (r.x > e + i.left)
+ setScrollOffset(r.x + v - e - i.left);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JTextPane.java b/libjava/classpath/javax/swing/JTextPane.java
new file mode 100644
index 000000000..4fef0020b
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTextPane.java
@@ -0,0 +1,424 @@
+/* JTextPane.java -- A powerful text widget supporting styled text
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Caret;
+import javax.swing.text.Document;
+import javax.swing.text.EditorKit;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
+import javax.swing.text.StyledEditorKit;
+
+/**
+ * A powerful text component that supports styled content as well as
+ * embedding images and components. It is entirely based on a
+ * {@link StyledDocument} content model and a {@link StyledEditorKit}.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ * @author Andrew Selkirk
+ */
+public class JTextPane
+ extends JEditorPane
+{
+ /**
+ * Creates a new JTextPane with a null document.
+ */
+ public JTextPane()
+ {
+ super();
+ }
+
+ /**
+ * Creates a new JTextPane and sets the specified
+ * document.
+ *
+ * @param document the content model to use
+ */
+ public JTextPane(StyledDocument document)
+ {
+ this();
+ setStyledDocument(document);
+ }
+
+ /**
+ * Returns the UI class ID. This is TextPaneUI.
+ *
+ * @return TextPaneUI
+ */
+ public String getUIClassID()
+ {
+ return "TextPaneUI";
+ }
+
+ /**
+ * Sets the content model for this JTextPane.
+ * JTextPane can only be used with {@link StyledDocument}s,
+ * if you try to set a different type of Document, an
+ * IllegalArgumentException is thrown.
+ *
+ * @param document the content model to set
+ *
+ * @throws IllegalArgumentException if document is not an
+ * instance of StyledDocument
+ *
+ * @see #setStyledDocument
+ */
+ public void setDocument(Document document)
+ {
+ if (document != null && !(document instanceof StyledDocument))
+ throw new IllegalArgumentException
+ ("JTextPane can only handle StyledDocuments");
+
+ setStyledDocument((StyledDocument) document);
+ }
+
+ /**
+ * Returns the {@link StyledDocument} that is the content model for
+ * this JTextPane. This is a typed wrapper for
+ * {@link #getDocument()}.
+ *
+ * @return the content model of this JTextPane
+ */
+ public StyledDocument getStyledDocument()
+ {
+ return (StyledDocument) super.getDocument();
+ }
+
+ /**
+ * Sets the content model for this JTextPane.
+ *
+ * @param document the content model to set
+ */
+ public void setStyledDocument(StyledDocument document)
+ {
+ super.setDocument(document);
+ }
+
+ /**
+ * Replaces the currently selected text with the specified
+ * content. If there is no selected text, this results
+ * in a simple insertion at the current caret position. If there is
+ * no content specified, this results in the selection
+ * beeing deleted.
+ *
+ * @param content the text with which the selection is replaced
+ */
+ public void replaceSelection(String content)
+ {
+ Caret caret = getCaret();
+ StyledDocument doc = getStyledDocument();
+ AttributeSet a = getInputAttributes().copyAttributes();
+ if (doc == null)
+ return;
+
+ int dot = caret.getDot();
+ int mark = caret.getMark();
+
+ int p0 = Math.min (dot, mark);
+ int p1 = Math.max (dot, mark);
+
+ try
+ {
+ if (doc instanceof AbstractDocument)
+ ((AbstractDocument)doc).replace(p0, p1 - p0, content, a);
+ else
+ {
+ // Remove selected text.
+ if (dot != mark)
+ doc.remove(p0, p1 - p0);
+ // Insert new text.
+ if (content != null && content.length() > 0)
+ doc.insertString(p0, content, a);
+ }
+ }
+ catch (BadLocationException e)
+ {
+ throw new AssertionError
+ ("No BadLocationException should be thrown here");
+ }
+ }
+
+ /**
+ * Inserts an AWT or Swing component into the text at the current caret
+ * position.
+ *
+ * @param component the component to be inserted
+ */
+ public void insertComponent(Component component)
+ {
+ SimpleAttributeSet atts = new SimpleAttributeSet();
+ atts.addAttribute(StyleConstants.ComponentAttribute, component);
+ atts.addAttribute(StyleConstants.NameAttribute,
+ StyleConstants.ComponentElementName);
+ try
+ {
+ getDocument().insertString(getCaret().getDot(), " ", atts);
+ }
+ catch (BadLocationException ex)
+ {
+ AssertionError err = new AssertionError("Unexpected bad location");
+ err.initCause(ex);
+ throw err;
+ }
+ }
+
+ /**
+ * Inserts an Icon into the text at the current caret position.
+ *
+ * @param icon the Icon to be inserted
+ */
+ public void insertIcon(Icon icon)
+ {
+ MutableAttributeSet inputAtts = getInputAttributes();
+ inputAtts.removeAttributes(inputAtts);
+ StyleConstants.setIcon(inputAtts, icon);
+ replaceSelection(" ");
+ inputAtts.removeAttributes(inputAtts);
+ }
+
+ /**
+ * Adds a style into the style hierarchy. Unspecified style attributes
+ * can be resolved in the parent style, if one is specified.
+ *
+ * While it is legal to add nameless styles (nm == nullnull
if the style should
+ * be unnamed
+ * @param parent the parent in which unspecified style attributes are
+ * resolved, or null if that is not necessary
+ *
+ * @return the newly created Style
+ */
+ public Style addStyle(String nm, Style parent)
+ {
+ return getStyledDocument().addStyle(nm, parent);
+ }
+
+ /**
+ * Removes a named Style from the style hierarchy.
+ *
+ * @param nm the name of the Style to be removed
+ */
+ public void removeStyle(String nm)
+ {
+ getStyledDocument().removeStyle(nm);
+ }
+
+ /**
+ * Looks up and returns a named Style.
+ *
+ * @param nm the name of the Style
+ *
+ * @return the found Style of null if no such
+ * Style exists
+ */
+ public Style getStyle(String nm)
+ {
+ return getStyledDocument().getStyle(nm);
+ }
+
+ /**
+ * Returns the logical style of the paragraph at the current caret position.
+ *
+ * @return the logical style of the paragraph at the current caret position
+ */
+ public Style getLogicalStyle()
+ {
+ return getStyledDocument().getLogicalStyle(getCaretPosition());
+ }
+
+ /**
+ * Sets the logical style for the paragraph at the current caret position.
+ *
+ * @param style the style to set for the current paragraph
+ */
+ public void setLogicalStyle(Style style)
+ {
+ getStyledDocument().setLogicalStyle(getCaretPosition(), style);
+ }
+
+ /**
+ * Returns the text attributes for the character at the current caret
+ * position.
+ *
+ * @return the text attributes for the character at the current caret
+ * position
+ */
+ public AttributeSet getCharacterAttributes()
+ {
+ StyledDocument doc = getStyledDocument();
+ Element el = doc.getCharacterElement(getCaretPosition());
+ return el.getAttributes();
+ }
+
+ /**
+ * Sets text attributes for the current selection. If there is no selection
+ * the text attributes are applied to newly inserted text
+ *
+ * @param attribute the text attributes to set
+ * @param replace if true, the attributes of the current
+ * selection are overridden, otherwise they are merged
+ *
+ * @see #getInputAttributes
+ */
+ public void setCharacterAttributes(AttributeSet attribute,
+ boolean replace)
+ {
+ int dot = getCaret().getDot();
+ int start = getSelectionStart();
+ int end = getSelectionEnd();
+ if (start == dot && end == dot)
+ // There is no selection, update insertAttributes instead
+ {
+ MutableAttributeSet inputAttributes =
+ getStyledEditorKit().getInputAttributes();
+ if (replace)
+ inputAttributes.removeAttributes(inputAttributes);
+ inputAttributes.addAttributes(attribute);
+ }
+ else
+ getStyledDocument().setCharacterAttributes(start, end - start, attribute,
+ replace);
+ }
+
+ /**
+ * Returns the text attributes of the paragraph at the current caret
+ * position.
+ *
+ * @return the attributes of the paragraph at the current caret position
+ */
+ public AttributeSet getParagraphAttributes()
+ {
+ StyledDocument doc = getStyledDocument();
+ Element el = doc.getParagraphElement(getCaretPosition());
+ return el.getAttributes();
+ }
+
+ /**
+ * Sets text attributes for the paragraph at the current selection.
+ * If there is no selection the text attributes are applied to
+ * the paragraph at the current caret position.
+ *
+ * @param attribute the text attributes to set
+ * @param replace if true, the attributes of the current
+ * selection are overridden, otherwise they are merged
+ */
+ public void setParagraphAttributes(AttributeSet attribute,
+ boolean replace)
+ {
+ // TODO
+ }
+
+ /**
+ * Returns the attributes that are applied to newly inserted text.
+ * This is a {@link MutableAttributeSet}, so you can easily modify these
+ * attributes.
+ *
+ * @return the attributes that are applied to newly inserted text
+ */
+ public MutableAttributeSet getInputAttributes()
+ {
+ return getStyledEditorKit().getInputAttributes();
+ }
+
+ /**
+ * Returns the {@link StyledEditorKit} that is currently used by this
+ * JTextPane.
+ *
+ * @return the current StyledEditorKit of this
+ * JTextPane
+ */
+ protected final StyledEditorKit getStyledEditorKit()
+ {
+ return (StyledEditorKit) getEditorKit();
+ }
+
+ /**
+ * Creates the default {@link EditorKit} that is used in
+ * JTextPanes. This is an instance of {@link StyledEditorKit}.
+ *
+ * @return the default {@link EditorKit} that is used in
+ * JTextPanes
+ */
+ protected EditorKit createDefaultEditorKit()
+ {
+ return new StyledEditorKit();
+ }
+
+ /**
+ * Sets the {@link EditorKit} to use for this JTextPane.
+ * JTextPanes can only handle {@link StyledEditorKit}s,
+ * if client programs try to set a different type of EditorKit
+ * then an IllegalArgumentException is thrown
+ *
+ * @param editor the EditorKit to set
+ *
+ * @throws IllegalArgumentException if editor is no
+ * StyledEditorKit
+ */
+ public final void setEditorKit(EditorKit editor)
+ {
+ if (!(editor instanceof StyledEditorKit))
+ throw new IllegalArgumentException
+ ("JTextPanes can only handle StyledEditorKits");
+ super.setEditorKit(editor);
+ }
+
+ /**
+ * Returns a param string that can be used for debugging.
+ *
+ * @return a param string that can be used for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString(); // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/JToggleButton.java b/libjava/classpath/javax/swing/JToggleButton.java
new file mode 100644
index 000000000..2ee4187c3
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToggleButton.java
@@ -0,0 +1,350 @@
+/* JToggleButton.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleState;
+import javax.swing.plaf.ButtonUI;
+
+/**
+ * The JToggleButton component provides a stateful button,
+ * which can be either selected or unselected. This provides the basis
+ * for the implementations of radio buttons (JRadioButton)
+ * and check boxes (JCheckBox).
+ *
+ * @author Michael Koch (konqueror@gmx.de)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see JRadioButton
+ * @see JCheckBox
+ * @since 1.2
+ */
+public class JToggleButton extends AbstractButton implements Accessible
+{
+ /**
+ * This class provides accessibility support for the toggle button.
+ */
+ protected class AccessibleJToggleButton
+ extends AccessibleAbstractButton
+ implements ItemListener
+ {
+ private static final long serialVersionUID = -8652952712161229225L;
+
+ /**
+ * Constructor for the accessible toggle button.
+ */
+ public AccessibleJToggleButton()
+ {
+ super();
+ /* Register the accessible toggle button as a listener for item events */
+ addItemListener(this);
+ }
+
+ /**
+ * Returns the accessible role for the toggle button.
+ *
+ * @return An instance of AccessibleRole, describing
+ * the role of the toggle button.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOGGLE_BUTTON;
+ }
+
+ /**
+ * Monitors the toggle button for state changes and fires accessible
+ * property change events when they occur.
+ *
+ * @param event the event that occurred.
+ */
+ public void itemStateChanged(ItemEvent event)
+ {
+ /* Fire a state property change event as the button's state has changed */
+ if (event.getStateChange() == ItemEvent.SELECTED)
+ {
+ /* State has changed from unselected (null) to selected */
+ firePropertyChange(ACCESSIBLE_STATE_PROPERTY, null, AccessibleState.SELECTED);
+ }
+ else
+ {
+ /* State has changed from selected to unselected (null) */
+ firePropertyChange(ACCESSIBLE_STATE_PROPERTY, AccessibleState.ENABLED, null);
+ }
+ }
+
+ }
+
+ /**
+ * The model handles the storage and maintenance of the state of
+ * the toggle button. This follows the same paradigm (the MVC
+ * or Model-View-Controller design pattern) employed by
+ * other Swing components, where the data associated with a component
+ * is stored separately from the display aspects.
+ */
+ public static class ToggleButtonModel extends DefaultButtonModel
+ {
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = -1589950750899943974L;
+
+ /**
+ * Sets the pressed state of the button. The selected state
+ * of the button also changes follwing the button being pressed.
+ *
+ * @param p true if the button is pressed down.
+ */
+ public void setPressed(boolean p)
+ {
+ // cannot change PRESSED state unless button is enabled
+ if (! isEnabled())
+ return;
+
+ // if this call does not represent a CHANGE in state, then return
+ if ((p && isPressed()) || (!p && !isPressed()))
+ return;
+
+ // The JDK first fires events in the following order:
+ // 1. ChangeEvent for selected
+ // 2. ChangeEvent for pressed
+ // 3. ActionEvent
+ // So do we.
+
+ // setPressed(false) == mouse release on us,
+ // if we were armed, we flip the selected state.
+ if (!p && isArmed())
+ {
+ setSelected(! isSelected());
+ }
+
+ // make the change
+ if (p)
+ stateMask = stateMask | PRESSED;
+ else
+ stateMask = stateMask & (~PRESSED);
+
+ // notify interested ChangeListeners
+ fireStateChanged();
+
+ if (!p && isArmed())
+ {
+ fireActionPerformed(new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ actionCommand));
+ }
+ }
+
+ /**
+ * Checks if the button is selected.
+ *
+ * @return true if the button is selected.
+ */
+ public boolean isSelected()
+ {
+ return super.isSelected();
+ }
+
+ /**
+ * Sets the selected state of the button.
+ *
+ * @param b true if button is selected
+ */
+ public void setSelected(boolean b)
+ {
+ super.setSelected(b);
+ }
+ }
+
+ /**
+ * Compatible with Sun's JDK.
+ */
+ private static final long serialVersionUID = -3128248873429850443L;
+
+ /**
+ * Constructs an unselected toggle button with no text or icon.
+ */
+ public JToggleButton()
+ {
+ this(null, null, false);
+ }
+
+ /**
+ * Constructs a toggle button using the labelling, state
+ * and icon specified by the supplied action.
+ *
+ * @param a the action to use to define the properties of the button.
+ */
+ public JToggleButton(Action a)
+ {
+ this();
+ setAction(a);
+ }
+
+ /**
+ * Constructs an unselected toggle button with the supplied icon
+ * and no text.
+ *
+ * @param icon the icon to use.
+ */
+ public JToggleButton(Icon icon)
+ {
+ this(null, icon, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied icon and state.
+ *
+ * @param icon the icon to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton(Icon icon, boolean selected)
+ {
+ this(null, icon, selected);
+ }
+
+ /**
+ * Constructs an unselected toggle button using the supplied text
+ * and no icon.
+ *
+ * @param text the text to use.
+ */
+ public JToggleButton(String text)
+ {
+ this(text, null, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied text and state.
+ *
+ * @param text the text to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton(String text, boolean selected)
+ {
+ this(text, null, selected);
+ }
+
+ /**
+ * Constructs an unselected toggle button with the supplied text
+ * and icon.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ */
+ public JToggleButton(String text, Icon icon)
+ {
+ this(text, icon, false);
+ }
+
+ /**
+ * Constructs a toggle button with the supplied text, icon and state.
+ *
+ * @param text the text to use.
+ * @param icon the icon to use.
+ * @param selected if true, the toggle button is initially in the
+ * selected state. Otherwise, the button is unselected.
+ */
+ public JToggleButton (String text, Icon icon, boolean selected)
+ {
+ super();
+ setModel(new ToggleButtonModel());
+ init(text, icon);
+ model.setSelected(selected);
+ setAlignmentX(LEFT_ALIGNMENT);
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this JToggleButton.
+ * The context is created, if necessary.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ /* Create the context if this is the first request */
+ if (accessibleContext == null)
+ {
+ /* Create the context */
+ accessibleContext = new AccessibleJToggleButton();
+ }
+ return accessibleContext;
+ }
+
+ /**
+ * Returns a string that specifies the name of the Look and Feel
+ * class that renders this component.
+ *
+ * @return The Look and Feel UI class in String form.
+ */
+ public String getUIClassID()
+ {
+ return "ToggleButtonUI";
+ }
+
+ /**
+ * Returns a textual representation of this component for debugging.
+ * Users should not depend on anything as regards the content or formatting
+ * of this string, except for the fact that the returned string may never be
+ * null (only empty).
+ *
+ * @return the component in String form for debugging.
+ */
+ protected String paramString()
+ {
+ return super.paramString();
+ }
+
+ /**
+ * This method resets the toggle button's UI delegate to the default UI for
+ * the current look and feel.
+ */
+ public void updateUI()
+ {
+ setUI((ButtonUI)UIManager.getUI(this));
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/JToolBar.java b/libjava/classpath/javax/swing/JToolBar.java
new file mode 100644
index 000000000..5f4816f04
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToolBar.java
@@ -0,0 +1,798 @@
+/* JToolBar.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.beans.PropertyChangeListener;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.ToolBarUI;
+
+/**
+ * JToolBar is a component that provides a toolbar to Swing programs. Users
+ * can add buttons (or actions that will be represented by JButtons) as well
+ * as other components to the JToolBar. JToolBars can be dragged in and out
+ * of their parent components. If the JToolBar is dragged out of the parent,
+ * then it will be displayed in its own RootPaneContainer. For dragging to
+ * work properly, JToolBars need to be placed in a Container that has a
+ * BorderLayout. That parent Container cannot have components in the NORTH,
+ * EAST, SOUTH, or WEST components (that is not the JToolBar).
+ */
+public class JToolBar extends JComponent implements SwingConstants, Accessible
+{
+ /**
+ * Provides the accessibility features for the JToolBar
+ * component.
+ */
+ protected class AccessibleJToolBar extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = -5516888265903814215L;
+
+ /**
+ * Creates a new AccessibleJToolBar instance.
+ */
+ protected AccessibleJToolBar()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a set containing the current state of the {@link JToolBar}
+ * component. The current implementation simply calls the superclass.
+ *
+ * @return The accessible state set.
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ // running tests against the reference implementation, I was unable
+ // to find any state information that is set specifically by the
+ // tool bar...
+ return super.getAccessibleStateSet();
+ }
+
+ /**
+ * Returns the accessible role for the JToolBar component.
+ *
+ * @return {@link AccessibleRole#TOOL_BAR}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOOL_BAR;
+ }
+ }
+
+ /**
+ * This is the private JToolBar layout manager.
+ */
+ private class DefaultToolBarLayout implements LayoutManager
+ {
+ /**
+ * This method is called when a new component is added to the container.
+ *
+ * @param name The name of the component added.
+ * @param comp The component that was added.
+ */
+ public void addLayoutComponent(String name, Component comp)
+ {
+ // Do nothing.
+ }
+
+ /**
+ * This method is called to lay out the given container to position and
+ * size the child components.
+ *
+ * @param c The container to lay out.
+ *
+ * @throws Error DOCUMENT ME!
+ */
+ public void layoutContainer(Container c)
+ {
+ if (! (c instanceof JToolBar))
+ throw new Error("DefaultToolBarLayout can only be used on JToolBars.");
+ Insets insets = getInsets();
+ Insets margin = getMargin();
+ int middle;
+ if (margin != null)
+ {
+ insets.left += margin.left;
+ insets.top += margin.top;
+ insets.bottom += margin.bottom;
+ insets.right += margin.right;
+ }
+ Component[] components = c.getComponents();
+ Dimension tdims = c.getSize();
+ int start = 0;
+ Dimension pref;
+
+ if (getOrientation() == SwingUtilities.HORIZONTAL)
+ {
+ start += insets.left;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null && components[i].isVisible())
+ {
+ pref = components[i].getPreferredSize();
+ if (pref != null)
+ {
+ middle = (tdims.height - pref.height) / 2;
+ components[i].setBounds(start, middle, pref.width,
+ pref.height);
+ start += pref.width;
+ }
+ }
+ }
+ }
+ else
+ {
+ start += insets.top;
+ for (int i = 0; i < components.length; i++)
+ {
+ if (components[i] != null && components[i].isVisible())
+ {
+ pref = components[i].getPreferredSize();
+ if (pref != null)
+ {
+ middle = (tdims.width - pref.width) / 2;
+ components[i].setBounds(middle, start, pref.width,
+ pref.height);
+ start += pref.height;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * This method returns the minimum size of the given container given the
+ * child components.
+ *
+ * @param parent The container to measure.
+ *
+ * @return The minimum size of the given container.
+ */
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ return preferredLayoutSize(parent);
+ }
+
+ /**
+ * This method returns the preferred size of the given container given the
+ * child components.
+ *
+ * @param parent The container to measure.
+ *
+ * @return The preferred size of the given container.
+ */
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ int orientation = getOrientation();
+ Component[] components = getComponents();
+
+ int limit = 0;
+ int total = 0;
+ Dimension dims;
+
+ int w = 0;
+ int h = 0;
+
+ if (orientation == SwingConstants.HORIZONTAL)
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ dims = components[i].getPreferredSize();
+ if (dims != null)
+ {
+ if (dims.height > limit)
+ limit = dims.height;
+ total += dims.width;
+ }
+ }
+ w = total;
+ h = limit;
+ }
+ else
+ {
+ for (int i = 0; i < components.length; i++)
+ {
+ dims = components[i].getPreferredSize();
+ if (dims != null)
+ {
+ if (dims.width > limit)
+ limit = dims.width;
+ total += dims.height;
+ }
+ }
+ w = limit;
+ h = total;
+ }
+
+ Insets insets = getInsets();
+ w += insets.left + insets.right;
+ h += insets.top + insets.bottom;
+
+ Insets margin = getMargin();
+ if (margin != null)
+ {
+ w += margin.left + margin.right;
+ h += margin.top + margin.bottom;
+ }
+
+ return new Dimension(w, h);
+ }
+
+ /**
+ * This method is called when the given component is removed from the
+ * container.
+ *
+ * @param comp The component removed.
+ */
+ public void removeLayoutComponent(Component comp)
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * This is an extension of JSeparator used in toolbars. Unlike JSeparator,
+ * nothing is painted for this Separator, it is only blank space that
+ * separates components.
+ */
+ public static class Separator extends JSeparator
+ {
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -1656745644823105219L;
+
+ /**
+ * Creates a new Separator object.
+ */
+ public Separator()
+ {
+ super();
+ } // Separator()
+
+ /**
+ * Creates a new Separator object with the given size.
+ *
+ * @param size The size of the separator.
+ */
+ public Separator(Dimension size)
+ {
+ setPreferredSize(size);
+ } // Separator()
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "ToolBarSeparatorUI";
+ } // getUIClassID()
+
+ /**
+ * This method returns the preferred size of the Separator.
+ *
+ * @return The preferred size of the Separator.
+ */
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ } // getPreferredSize()
+
+ /**
+ * This method returns the maximum size of the Separator.
+ *
+ * @return The maximum size of the Separator.
+ */
+ public Dimension getMaximumSize()
+ {
+ return super.getPreferredSize();
+ } // getMaximumSize()
+
+ /**
+ * This method returns the minimum size of the Separator.
+ *
+ * @return The minimum size of the Separator.
+ */
+ public Dimension getMinimumSize()
+ {
+ return super.getPreferredSize();
+ } // getMinimumSize()
+
+ /**
+ * This method returns the size of the Separator.
+ *
+ * @return The size of the Separator.
+ */
+ public Dimension getSeparatorSize()
+ {
+ return super.getPreferredSize();
+ } // getSeparatorSize()
+
+ /**
+ * This method sets the size of the Separator.
+ *
+ * @param size The new size of the Separator.
+ */
+ public void setSeparatorSize(Dimension size)
+ {
+ setPreferredSize(size);
+ } // setSeparatorSize()
+ } // Separator
+
+ /** DOCUMENT ME! */
+ private static final long serialVersionUID = -1269915519555129643L;
+
+ /** Whether the JToolBar paints its border. */
+ private transient boolean paintBorder = true;
+
+ /** The extra insets around the JToolBar. */
+ private transient Insets margin;
+
+ /** Whether the JToolBar can float (and be dragged around). */
+ private transient boolean floatable = true;
+
+ /** Whether the buttons will have rollover borders. */
+ private transient boolean rollover;
+
+ /** The orientation of the JToolBar. */
+ private int orientation = HORIZONTAL;
+
+ /**
+ * This method creates a new JToolBar object with horizontal orientation
+ * and no name.
+ */
+ public JToolBar()
+ {
+ this(null, HORIZONTAL);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar with the given orientation and no
+ * name.
+ *
+ * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
+ */
+ public JToolBar(int orientation)
+ {
+ this(null, orientation);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar object with the given name and
+ * horizontal orientation.
+ *
+ * @param name Name assigned to undocked tool bar.
+ */
+ public JToolBar(String name)
+ {
+ this(name, HORIZONTAL);
+ } // JToolBar()
+
+ /**
+ * This method creates a new JToolBar object with the given name and
+ * orientation.
+ *
+ * @param name Name assigned to undocked tool bar.
+ * @param orientation JToolBar orientation (HORIZONTAL or VERTICAL)
+ */
+ public JToolBar(String name, int orientation)
+ {
+ setName(name);
+ setOrientation(orientation);
+ setLayout(new DefaultToolBarLayout());
+ revalidate();
+ setOpaque(true);
+ updateUI();
+ }
+
+ /**
+ * This method adds a new JButton that performs the given Action to the
+ * JToolBar.
+ *
+ * @param action The Action to add to the JToolBar.
+ *
+ * @return The JButton that wraps the Action.
+ */
+ public JButton add(Action action)
+ {
+ JButton b = createActionComponent(action);
+ add(b);
+ return b;
+ } // add()
+
+ /**
+ * This method paints the border if the borderPainted property is true.
+ *
+ * @param graphics The graphics object to paint with.
+ */
+ protected void paintBorder(Graphics graphics)
+ {
+ if (paintBorder && isFloatable())
+ super.paintBorder(graphics);
+ } // paintBorder()
+
+ /**
+ * This method returns the UI class used to paint this JToolBar.
+ *
+ * @return The UI class for this JToolBar.
+ */
+ public ToolBarUI getUI()
+ {
+ return (ToolBarUI) ui;
+ } // getUI()
+
+ /**
+ * This method sets the UI used with the JToolBar.
+ *
+ * @param ui The UI used with the JToolBar.
+ */
+ public void setUI(ToolBarUI ui)
+ {
+ super.setUI(ui);
+ } // setUI()
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults.
+ */
+ public void updateUI()
+ {
+ setUI((ToolBarUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String identifier for the UI class to the used
+ * with the JToolBar.
+ *
+ * @return The String identifier for the UI class.
+ */
+ public String getUIClassID()
+ {
+ return "ToolBarUI";
+ } // getUIClassID()
+
+ /**
+ * This method sets the rollover property for the JToolBar. In rollover
+ * mode, JButtons inside the JToolBar will only display their borders when
+ * the mouse is moving over them.
+ *
+ * @param b The new rollover property.
+ */
+ public void setRollover(boolean b)
+ {
+ if (b != rollover)
+ {
+ rollover = b;
+ firePropertyChange("rollover", ! rollover, rollover);
+ revalidate();
+ repaint();
+ }
+ }
+
+ /**
+ * This method returns the rollover property.
+ *
+ * @return The rollover property.
+ */
+ public boolean isRollover()
+ {
+ return rollover;
+ }
+
+ /**
+ * This method returns the index of the given component.
+ *
+ * @param component The component to find.
+ *
+ * @return The index of the given component.
+ */
+ public int getComponentIndex(Component component)
+ {
+ Component[] components = getComponents();
+ if (components == null)
+ return -1;
+
+ for (int i = 0; i < components.length; i++)
+ if (components[i] == component)
+ return i;
+
+ return -1;
+ } // getComponentIndex()
+
+ /**
+ * This method returns the component at the given index.
+ *
+ * @param index The index of the component.
+ *
+ * @return The component at the given index.
+ */
+ public Component getComponentAtIndex(int index)
+ {
+ return getComponent(index);
+ } // getComponentAtIndex()
+
+ /**
+ * This method returns the margin property.
+ *
+ * @return The margin property.
+ */
+ public Insets getMargin()
+ {
+ return margin;
+ } // getMargin()
+
+ /**
+ * This method sets the margin property. The margin property determines the
+ * extra space between the children components of the JToolBar and the
+ * border.
+ *
+ * @param margin The margin property.
+ */
+ public void setMargin(Insets margin)
+ {
+ if ((this.margin != null && margin == null)
+ || (this.margin == null && margin != null)
+ || (margin != null && this.margin != null
+ && (margin.left != this.margin.left
+ || margin.right != this.margin.right || margin.top != this.margin.top
+ || margin.bottom != this.margin.bottom)))
+ {
+ Insets oldMargin = this.margin;
+ this.margin = margin;
+ firePropertyChange("margin", oldMargin, this.margin);
+ revalidate();
+ repaint();
+ }
+ } // setMargin()
+
+ /**
+ * This method returns the borderPainted property.
+ *
+ * @return The borderPainted property.
+ */
+ public boolean isBorderPainted()
+ {
+ return paintBorder;
+ } // isBorderPainted()
+
+ /**
+ * This method sets the borderPainted property. If set to false, the border
+ * will not be painted.
+ *
+ * @param painted Whether the border will be painted.
+ */
+ public void setBorderPainted(boolean painted)
+ {
+ if (painted != paintBorder)
+ {
+ paintBorder = painted;
+ firePropertyChange("borderPainted", ! paintBorder,
+ paintBorder);
+ repaint();
+ }
+ } // setBorderPainted()
+
+ /**
+ * This method returns the floatable property.
+ *
+ * @return The floatable property.
+ */
+ public boolean isFloatable()
+ {
+ return floatable;
+ } // isFloatable()
+
+ /**
+ * This method sets the floatable property. If set to false, the JToolBar
+ * cannot be dragged.
+ *
+ * @param floatable Whether the JToolBar can be dragged.
+ */
+ public void setFloatable(boolean floatable)
+ {
+ if (floatable != this.floatable)
+ {
+ this.floatable = floatable;
+ firePropertyChange("floatable", ! floatable, floatable);
+ }
+ } // setFloatable()
+
+ /**
+ * This method returns the orientation of the JToolBar.
+ *
+ * @return The orientation of the JToolBar.
+ */
+ public int getOrientation()
+ {
+ return orientation;
+ } // getOrientation()
+
+ /**
+ * This method sets the layout manager to be used with the JToolBar.
+ *
+ * @param mgr The Layout Manager used with the JToolBar.
+ */
+ public void setLayout(LayoutManager mgr)
+ {
+ super.setLayout(mgr);
+ revalidate();
+ repaint();
+ } // setLayout()
+
+ /**
+ * This method sets the orientation property for JToolBar.
+ *
+ * @param orientation The new orientation for JToolBar.
+ *
+ * @throws IllegalArgumentException If the orientation is not HORIZONTAL or
+ * VERTICAL.
+ */
+ public void setOrientation(int orientation)
+ {
+ if (orientation != HORIZONTAL && orientation != VERTICAL)
+ throw new IllegalArgumentException(orientation
+ + " is not a legal orientation");
+ if (orientation != this.orientation)
+ {
+ int oldOrientation = this.orientation;
+ this.orientation = orientation;
+ firePropertyChange("orientation", oldOrientation, this.orientation);
+ revalidate();
+ repaint();
+ }
+ } // setOrientation()
+
+ /**
+ * This method adds a Separator of default size to the JToolBar.
+ */
+ public void addSeparator()
+ {
+ add(new Separator());
+ } // addSeparator()
+
+ /**
+ * This method adds a Separator with the given size to the JToolBar.
+ *
+ * @param size The size of the Separator.
+ */
+ public void addSeparator(Dimension size)
+ {
+ add(new Separator(size));
+ } // addSeparator()
+
+ /**
+ * This method is used to create JButtons which can be added to the JToolBar
+ * for the given action.
+ *
+ * @param action The action to create a JButton for.
+ *
+ * @return The JButton created from the action.
+ */
+ protected JButton createActionComponent(Action action)
+ {
+ return new JButton(action);
+ } // createActionComponent()
+
+ /**
+ * This method creates a pre-configured PropertyChangeListener which updates
+ * the control as changes are made to the Action. However, this is no
+ * longer the recommended way of adding Actions to Containers. As such,
+ * this method returns null.
+ *
+ * @param button The JButton to configure a PropertyChangeListener for.
+ *
+ * @return null.
+ */
+ protected PropertyChangeListener createActionChangeListener(JButton button)
+ {
+ // XXX: As specified, this returns null. But seems kind of strange, usually deprecated methods don't just return null, verify!
+ return null;
+ } // createActionChangeListener()
+
+ /**
+ * This method overrides Container's addImpl method. If a JButton is added,
+ * it is disabled.
+ *
+ * @param component The Component to add.
+ * @param constraints The Constraints placed on the component.
+ * @param index The index to place the Component at.
+ */
+ protected void addImpl(Component component, Object constraints, int index)
+ {
+ // XXX: Sun says disable button but test cases show otherwise.
+ super.addImpl(component, constraints, index);
+
+ // if we added a Swing Button then adjust this a little
+ if (component instanceof AbstractButton)
+ {
+ AbstractButton b = (AbstractButton) component;
+ b.setRolloverEnabled(rollover);
+ }
+
+ } // addImpl()
+
+ /**
+ * Returns a string describing the attributes for the JToolBar
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JToolBar.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",floatable=").append(floatable);
+ sb.append(",margin=");
+ if (margin != null)
+ sb.append(margin);
+ sb.append(",orientation=");
+ if (orientation == HORIZONTAL)
+ sb.append("HORIZONTAL");
+ else
+ sb.append(VERTICAL);
+ sb.append(",paintBorder=").append(paintBorder);
+ return sb.toString();
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JToolBar component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJToolBar}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJToolBar();
+
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JToolTip.java b/libjava/classpath/javax/swing/JToolTip.java
new file mode 100644
index 000000000..6f226e780
--- /dev/null
+++ b/libjava/classpath/javax/swing/JToolTip.java
@@ -0,0 +1,244 @@
+/* JToolTip.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.java.lang.CPStringBuilder;
+
+import java.awt.AWTEvent;
+import java.beans.PropertyChangeEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.plaf.ToolTipUI;
+
+/**
+ * This class is used to display ToolTips. ToolTips are small floating windows
+ * that display text when the mouse comes to rest over a Component. ToolTips
+ * are set for JComponents using JComponent.setToolTipText(String).
+ */
+public class JToolTip extends JComponent implements Accessible
+{
+
+ private static final long serialVersionUID = -1138929898906751643L;
+
+ /**
+ * Provides the accessibility features for the JToolTip
+ * component.
+ */
+ protected class AccessibleJToolTip extends AccessibleJComponent
+ {
+ private static final long serialVersionUID = -6222548177795408476L;
+
+ /**
+ * Creates a new AccessibleJToolTip object.
+ */
+ protected AccessibleJToolTip()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns a description for the accessible component.
+ *
+ * @return A description for the accessible component.
+ */
+ public String getAccessibleDescription()
+ {
+ String result = super.getAccessibleDescription();
+ if (result == null)
+ result = text;
+ return result;
+ }
+
+ /**
+ * Returns the accessible role for the JToolTip component.
+ *
+ * @return {@link AccessibleRole#TOOL_TIP}.
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TOOL_TIP;
+ }
+ }
+
+ /** The text to display in the JToolTip. */
+ String text;
+
+ /** The component that the tool tip is associated with. */
+ JComponent component;
+
+ /**
+ * Creates a new JToolTip instance.
+ */
+ public JToolTip()
+ {
+ disableEvents(AWTEvent.MOUSE_EVENT_MASK);
+ updateUI();
+ }
+
+ /**
+ * Returns the text displayed by the tool tip.
+ *
+ * @return The text (possibly null).
+ *
+ * @see #setTipText(String)
+ */
+ public String getTipText()
+ {
+ return text;
+ }
+
+ /**
+ * Returns the object that provides accessibility features for this
+ * JToolTip component.
+ *
+ * @return The accessible context (an instance of {@link AccessibleJToolTip}).
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJToolTip();
+ return accessibleContext;
+ }
+
+ /**
+ * Returns the component that the tool tip is associated with.
+ *
+ * @return The component (possibly null).
+ *
+ * @see #setComponent(JComponent)
+ */
+ public JComponent getComponent()
+ {
+ return component;
+ }
+
+ /**
+ * Returns the current UI delegate for this component.
+ *
+ * @return The UI delegate.
+ */
+ public ToolTipUI getUI()
+ {
+ return (ToolTipUI) ui;
+ }
+
+ /**
+ * Returns the string suffix used to identify the UI class, in this case
+ * "ToolTipUI".
+ *
+ * @return "ToolTipUI".
+ */
+ public String getUIClassID()
+ {
+ return "ToolTipUI";
+ }
+
+ /**
+ * Returns a string describing the attributes for the JToolTip
+ * component, for use in debugging. The return value is guaranteed to be
+ * non-null, but the format of the string may vary between
+ * implementations.
+ *
+ * @return A string describing the attributes of the JToolTip.
+ */
+ protected String paramString()
+ {
+ CPStringBuilder sb = new CPStringBuilder(super.paramString());
+ sb.append(",tiptext=");
+ if (text != null)
+ sb.append(text);
+ return sb.toString();
+ }
+
+ /**
+ * Sets the component that the tool tip is associated with and sends a
+ * {@link PropertyChangeEvent} (with the property name 'component') to all
+ * registered listeners.
+ *
+ * @param c the component (null permitted).
+ *
+ * @see #getComponent()
+ */
+ public void setComponent(JComponent c)
+ {
+ JComponent oldValue = component;
+ component = c;
+ firePropertyChange("component", oldValue, c);
+ }
+
+ /**
+ * Sets the text to be displayed by the tool tip and sends a
+ * {@link PropertyChangeEvent} (with the property name 'tiptext') to all
+ * registered listeners.
+ *
+ * @param tipText the text (null permitted).
+ *
+ * @see #getTipText()
+ */
+ public void setTipText(String tipText)
+ {
+ String oldValue = text;
+ text = tipText;
+ firePropertyChange("tiptext", oldValue, tipText);
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel default.
+ */
+ public void updateUI()
+ {
+ setUI((ToolTipUI) UIManager.getUI(this));
+ }
+
+ /**
+ * Returns true if the component is guaranteed to be painted
+ * on top of others. This returns false by default and is overridden by
+ * components like JMenuItem, JPopupMenu and JToolTip to return true for
+ * added efficiency.
+ *
+ * @return true if the component is guaranteed to be painted
+ * on top of others
+ */
+ boolean onTop()
+ {
+ return true;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JTree.java b/libjava/classpath/javax/swing/JTree.java
new file mode 100644
index 000000000..d89adad5e
--- /dev/null
+++ b/libjava/classpath/javax/swing/JTree.java
@@ -0,0 +1,3186 @@
+/* JTree.java
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.FocusListener;
+import java.beans.PropertyChangeListener;
+import java.io.Serializable;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleAction;
+import javax.accessibility.AccessibleComponent;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.accessibility.AccessibleSelection;
+import javax.accessibility.AccessibleState;
+import javax.accessibility.AccessibleStateSet;
+import javax.accessibility.AccessibleText;
+import javax.accessibility.AccessibleValue;
+import javax.swing.event.TreeExpansionEvent;
+import javax.swing.event.TreeExpansionListener;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.event.TreeWillExpandListener;
+import javax.swing.plaf.TreeUI;
+import javax.swing.text.Position;
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.DefaultTreeSelectionModel;
+import javax.swing.tree.ExpandVetoException;
+import javax.swing.tree.TreeCellEditor;
+import javax.swing.tree.TreeCellRenderer;
+import javax.swing.tree.TreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+import javax.swing.tree.TreeSelectionModel;
+
+public class JTree extends JComponent implements Scrollable, Accessible
+{
+
+ /**
+ * This class implements accessibility support for the JTree class. It
+ * provides an implementation of the Java Accessibility API appropriate
+ * to tree user-interface elements.
+ */
+ protected class AccessibleJTree extends JComponent.AccessibleJComponent
+ implements AccessibleSelection, TreeSelectionListener, TreeModelListener,
+ TreeExpansionListener
+ {
+
+ /**
+ * This class implements accessibility support for the JTree child. It provides
+ * an implementation of the Java Accessibility API appropriate to tree nodes.
+ */
+ protected class AccessibleJTreeNode extends AccessibleContext
+ implements Accessible, AccessibleComponent, AccessibleSelection,
+ AccessibleAction
+ {
+
+ private JTree tree;
+ private TreePath tp;
+ private Accessible acc;
+ private AccessibleStateSet states;
+ private Vector selectionList;
+ private Vector actionList;
+ private TreeModel mod;
+ private Cursor cursor;
+
+ /**
+ * Constructs an AccessibleJTreeNode
+ *
+ * @param t - the current tree
+ * @param p - the current path to be dealt with
+ * @param ap - the accessible object to use
+ */
+ public AccessibleJTreeNode(JTree t, TreePath p, Accessible ap)
+ {
+ states = new AccessibleStateSet();
+ selectionList = new Vector();
+ actionList = new Vector();
+ mod = tree.getModel();
+ cursor = JTree.this.getCursor();
+
+ tree = t;
+ tp = p;
+ acc = ap;
+
+ // Add all the children of this path that may already be
+ // selected to the selection list.
+ TreePath[] selected = tree.getSelectionPaths();
+ for (int i = 0; i < selected.length; i++)
+ {
+ TreePath sel = selected[i];
+ if ((sel.getParentPath()).equals(tp))
+ selectionList.add(sel);
+ }
+
+ // Add all the actions available for a node to
+ // the action list.
+ actionList.add("EXPAND");
+ actionList.add("COLLAPSE");
+ actionList.add("EDIT");
+ actionList.add("SELECT");
+ actionList.add("DESELECT");
+ }
+
+ /**
+ * Adds the specified selected item in the object to the object's
+ * selection.
+ *
+ * @param i - the i-th child of this node.
+ */
+ public void addAccessibleSelection(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ selectionList.add(child);
+ tree.addSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+
+ /**
+ * Adds the specified focus listener to receive focus events
+ * from this component.
+ *
+ * @param l - the new focus listener
+ */
+ public void addFocusListener(FocusListener l)
+ {
+ tree.addFocusListener(l);
+ }
+
+ /**
+ * Add a PropertyChangeListener to the listener list.
+ *
+ * @param l - the new property change listener
+ */
+ public void addPropertyChangeListener(PropertyChangeListener l)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Clears the selection in the object, so that nothing in the
+ * object is selected.
+ */
+ public void clearAccessibleSelection()
+ {
+ selectionList.clear();
+ }
+
+ /**
+ * Checks whether the specified point is within this object's
+ * bounds, where the point's x and y coordinates are defined to be
+ * relative to the coordinate system of the object.
+ *
+ * @param p - the point to check
+ * @return true if p is in the bounds
+ */
+ public boolean contains(Point p)
+ {
+ return getBounds().contains(p);
+ }
+
+ /**
+ * Perform the specified Action on the tree node.
+ *
+ * @param i - the i-th action to perform
+ * @return true if the the action was performed; else false.
+ */
+ public boolean doAccessibleAction(int i)
+ {
+ if (i >= actionList.size() || i < 0)
+ return false;
+
+ if (actionList.get(i).equals("EXPAND"))
+ tree.expandPath(tp);
+ else if (actionList.get(i).equals("COLLAPSE"))
+ tree.collapsePath(tp);
+ else if (actionList.get(i).equals("SELECT"))
+ tree.addSelectionPath(tp);
+ else if (actionList.get(i).equals("DESELECT"))
+ tree.removeSelectionPath(tp);
+ else if (actionList.get(i).equals("EDIT"))
+ tree.startEditingAtPath(tp);
+ else
+ return false;
+ return true;
+ }
+
+ /**
+ * Get the AccessibleAction associated with this object.
+ *
+ * @return the action
+ */
+ public AccessibleAction getAccessibleAction()
+ {
+ return this;
+ }
+
+ /**
+ * Returns the number of accessible actions available in this tree node.
+ *
+ * @return the number of actions
+ */
+ public int getAccessibleActionCount()
+ {
+ return actionList.size();
+ }
+
+ /**
+ * Return a description of the specified action of the tree node.
+ *
+ * @param i - the i-th action's description
+ * @return a description of the action
+ */
+ public String getAccessibleActionDescription(int i)
+ {
+ if (i < 0 || i >= actionList.size())
+ return (actionList.get(i)).toString();
+ return super.getAccessibleDescription();
+ }
+
+ /**
+ * Returns the Accessible child, if one exists, contained at the
+ * local coordinate Point.
+ *
+ * @param p - the point of the accessible
+ * @return the accessible at point p if it exists
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ TreePath acc = tree.getClosestPathForLocation(p.x, p.y);
+ if (acc != null)
+ return new AccessibleJTreeNode(tree, acc, this);
+ return null;
+ }
+
+ /**
+ * Return the specified Accessible child of the object.
+ *
+ * @param i - the i-th child of the current path
+ * @return the child if it exists
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ return new AccessibleJTreeNode(tree, tp.pathByAddingChild(child),
+ acc);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the number of accessible children in the object.
+ *
+ * @return the number of children the current node has
+ */
+ public int getAccessibleChildrenCount()
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return mod.getChildCount(tp.getLastPathComponent());
+ return 0;
+ }
+
+ /**
+ * Get the AccessibleComponent associated with this object.
+ *
+ * @return the accessible component if it is supported.
+ */
+ public AccessibleComponent getAccessibleComponent()
+ {
+ return this;
+ }
+
+ /**
+ * Get the AccessibleContext associated with this tree node.
+ *
+ * @return an instance of this class
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return this;
+ }
+
+ /**
+ * Get the accessible description of this object.
+ *
+ * @return the accessible description
+ */
+ public String getAccessibleDescription()
+ {
+ return super.getAccessibleDescription();
+ }
+
+ /**
+ * Get the index of this object in its accessible parent.
+ *
+ * @return the index of this in the parent.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ AccessibleContext parent = getAccessibleParent().getAccessibleContext();
+ if (parent != null)
+ for (int i = 0; i < parent.getAccessibleChildrenCount(); i++)
+ {
+ if ((parent.getAccessibleChild(i)).equals(this))
+ return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Get the accessible name of this object.
+ *
+ * @return the accessible name
+ */
+ public String getAccessibleName()
+ {
+ return super.getAccessibleName();
+ }
+
+ /**
+ * Get the Accessible parent of this object.
+ *
+ * @return the accessible parent if it exists.
+ */
+ public Accessible getAccessibleParent()
+ {
+ return super.getAccessibleParent();
+ }
+
+ /**
+ * Get the role of this object.
+ *
+ * @return the accessible role
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleJTree.this.getAccessibleRole();
+ }
+
+ /**
+ * Get the AccessibleSelection associated with this object if one exists.
+ *
+ * @return the accessible selection for this.
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ return this;
+ }
+
+ /**
+ * Returns an Accessible representing the specified selected item
+ * in the object.
+ *
+ * @return the accessible representing a certain selected item.
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ if (i > 0 && i < getAccessibleSelectionCount())
+ return new AccessibleJTreeNode(tree,
+ tp.pathByAddingChild(selectionList.get(i)), acc);
+ return null;
+ }
+
+ /**
+ * Returns the number of items currently selected.
+ *
+ * @return the number of items selected.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return selectionList.size();
+ }
+
+ /**
+ * Get the state set of this object.
+ *
+ * @return the state set for this object
+ */
+ public AccessibleStateSet getAccessibleStateSet()
+ {
+ if (isVisible())
+ states.add(AccessibleState.VISIBLE);
+ if (tree.isCollapsed(tp))
+ states.add(AccessibleState.COLLAPSED);
+ if (tree.isEditable())
+ states.add(AccessibleState.EDITABLE);
+ if (mod != null &&
+ !mod.isLeaf(tp.getLastPathComponent()))
+ states.add(AccessibleState.EXPANDABLE);
+ if (tree.isExpanded(tp))
+ states.add(AccessibleState.EXPANDED);
+ if (isFocusable())
+ states.add(AccessibleState.FOCUSABLE);
+ if (hasFocus())
+ states.add(AccessibleState.FOCUSED);
+ if (tree.getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION)
+ states.add(AccessibleState.MULTISELECTABLE);
+ if (tree.isOpaque())
+ states.add(AccessibleState.OPAQUE);
+ if (tree.isPathSelected(tp))
+ states.add(AccessibleState.SELECTED);
+ if (isShowing())
+ states.add(AccessibleState.SHOWING);
+
+ states.add(AccessibleState.SELECTABLE);
+ return states;
+ }
+
+ /**
+ * Get the AccessibleText associated with this object if one exists.
+ *
+ * @return the accessible text
+ */
+ public AccessibleText getAccessibleText()
+ {
+ return super.getAccessibleText();
+ }
+
+ /**
+ * Get the AccessibleValue associated with this object if one exists.
+ *
+ * @return the accessible value if it exists
+ */
+ public AccessibleValue getAccessibleValue()
+ {
+ return super.getAccessibleValue();
+ }
+
+ /**
+ * Get the background color of this object.
+ *
+ * @return the color of the background.
+ */
+ public Color getBackground()
+ {
+ return tree.getBackground();
+ }
+
+ /**
+ * Gets the bounds of this object in the form of a Rectangle object.
+ *
+ * @return the bounds of the current node.
+ */
+ public Rectangle getBounds()
+ {
+ return tree.getPathBounds(tp);
+ }
+
+ /**
+ * Gets the Cursor of this object.
+ *
+ * @return the cursor for the current node
+ */
+ public Cursor getCursor()
+ {
+ return cursor;
+ }
+
+ /**
+ * Gets the Font of this object.
+ *
+ * @return the font for the current node
+ */
+ public Font getFont()
+ {
+ return tree.getFont();
+ }
+
+ /**
+ * Gets the FontMetrics of this object.
+ *
+ * @param f - the current font.
+ * @return the font metrics for the given font.
+ */
+ public FontMetrics getFontMetrics(Font f)
+ {
+ return tree.getFontMetrics(f);
+ }
+
+ /**
+ * Get the foreground color of this object.
+ *
+ * @return the foreground for this object.
+ */
+ public Color getForeground()
+ {
+ return tree.getForeground();
+ }
+
+ /**
+ * Gets the locale of the component.
+ *
+ * @return the locale of the component.
+ */
+ public Locale getLocale()
+ {
+ return tree.getLocale();
+ }
+
+ /**
+ * Gets the location of the object relative to the
+ * parent in the form of a point specifying the object's
+ * top-left corner in the screen's coordinate space.
+ *
+ * @return the location of the current node.
+ */
+ public Point getLocation()
+ {
+ return getLocationInJTree();
+ }
+
+ /**
+ * Returns the location in the tree.
+ *
+ * @return the location in the JTree.
+ */
+ protected Point getLocationInJTree()
+ {
+ Rectangle bounds = tree.getPathBounds(tp);
+ return new Point(bounds.x, bounds.y);
+ }
+
+ /**
+ * Returns the location of the object on the screen.
+ *
+ * @return the location of the object on the screen.
+ */
+ public Point getLocationOnScreen()
+ {
+ Point loc = getLocation();
+ SwingUtilities.convertPointToScreen(loc, tree);
+ return loc;
+ }
+
+ /**
+ * Returns the size of this object in the form of a Dimension object.
+ *
+ * @return the size of the object
+ */
+ public Dimension getSize()
+ {
+ Rectangle b = getBounds();
+ return b.getSize();
+ }
+
+ /**
+ * Returns true if the current child of this object is selected.
+ *
+ * @param i - the child of the current node
+ * @return true if the child is selected.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ return tree.isPathSelected(tp.pathByAddingChild(child));
+ return false;
+ }
+
+ /**
+ * Determines if the object is enabled.
+ *
+ * @return true if the tree is enabled
+ */
+ public boolean isEnabled()
+ {
+ return tree.isEnabled();
+ }
+
+ /**
+ * Returns whether this object can accept focus or not.
+ *
+ * @return true, it is always focus traversable
+ */
+ public boolean isFocusTraversable()
+ {
+ return true;
+ }
+
+ /**
+ * Determines if the object is showing.
+ *
+ * @return true if the object is visible and the
+ * parent is visible.
+ */
+ public boolean isShowing()
+ {
+ return isVisible() && tree.isShowing();
+ }
+
+ /**
+ * Determines if the object is visible.
+ *
+ * @return true if the object is visible.
+ */
+ public boolean isVisible()
+ {
+ return tree.isVisible(tp);
+ }
+
+ /**
+ * Removes the specified selected item in the object from the
+ * object's selection.
+ *
+ * @param i - the specified item to remove
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ if (mod != null)
+ {
+ Object child = mod.getChild(tp.getLastPathComponent(), i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ if (selectionList.contains(child))
+ {
+ selectionList.remove(child);
+ tree.removeSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes the specified focus listener so it no longer receives focus
+ * events from this component.
+ *
+ * @param l - the focus listener to remove
+ */
+ public void removeFocusListener(FocusListener l)
+ {
+ tree.removeFocusListener(l);
+ }
+
+ /**
+ * Remove a PropertyChangeListener from the listener list.
+ *
+ * @param l - the property change listener to remove.
+ */
+ public void removePropertyChangeListener(PropertyChangeListener l)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Requests focus for this object.
+ */
+ public void requestFocus()
+ {
+ tree.requestFocus();
+ }
+
+ /**
+ * Causes every selected item in the object to be selected if the object
+ * supports multiple selections.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ Object parent = tp.getLastPathComponent();
+ if (mod != null)
+ {
+ for (int i = 0; i < mod.getChildCount(parent); i++)
+ {
+ Object child = mod.getChild(parent, i);
+ if (child != null)
+ {
+ if (!states.contains(AccessibleState.MULTISELECTABLE))
+ clearAccessibleSelection();
+ if (selectionList.contains(child))
+ {
+ selectionList.add(child);
+ tree.addSelectionPath(tp.pathByAddingChild(child));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Set the accessible description of this object.
+ *
+ * @param s - the string to set the accessible description to.
+ */
+ public void setAccessibleDescription(String s)
+ {
+ super.setAccessibleDescription(s);
+ }
+
+ /**
+ * Set the localized accessible name of this object.
+ *
+ * @param s - the string to set the accessible name to.
+ */
+ public void setAccessibleName(String s)
+ {
+ super.setAccessibleName(s);
+ }
+
+ /**
+ * Set the background color of this object.
+ *
+ * @param c - the color to set the background to.
+ */
+ public void setBackground(Color c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the bounds of this object in the form of a Rectangle object.
+ *
+ * @param r - the bounds to set the object o
+ */
+ public void setBounds(Rectangle r)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the Cursor of this object.
+ *
+ * @param c - the new cursor
+ */
+ public void setCursor(Cursor c)
+ {
+ cursor = c;
+ }
+
+ /**
+ * Sets the enabled state of the object.
+ *
+ * @param b - boolean to enable or disable object
+ */
+ public void setEnabled(boolean b)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the Font of this object.
+ *
+ * @param f - the new font.
+ */
+ public void setFont(Font f)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the foreground color of this object.
+ *
+ * @param c - the new foreground color.
+ */
+ public void setForeground(Color c)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the location of the object relative to the parent.
+ *
+ * @param p - the new location for the object.
+ */
+ public void setLocation(Point p)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Resizes this object so that it has width and height.
+ *
+ * @param d - the new size for the object.
+ */
+ public void setSize(Dimension d)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Sets the visible state of the object.
+ *
+ * @param b - sets the objects visibility.
+ */
+ public void setVisible(boolean b)
+ {
+ // Nothing to do here.
+ }
+ }
+
+ /**
+ * Constructor
+ */
+ public AccessibleJTree()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Adds the specified selected item in the object to the object's selection.
+ *
+ * @param i - the row to add to the tree's selection
+ */
+ public void addAccessibleSelection(int i)
+ {
+ addSelectionInterval(i, i);
+ }
+
+ /**
+ * Clears the selection in the object, so that nothing in the object is selected.
+ */
+ public void clearAccessibleSelection()
+ {
+ clearSelection();
+ }
+
+ /**
+ * Fire a visible data property change notification.
+ */
+ public void fireVisibleDataPropertyChange()
+ {
+ treeDidChange();
+ }
+
+ /**
+ * Returns the Accessible child, if one exists, contained at the local
+ * coordinate Point.
+ *
+ * @param p - the point of the accessible to get.
+ * @return the accessible at point p.
+ */
+ public Accessible getAccessibleAt(Point p)
+ {
+ TreePath tp = getClosestPathForLocation(p.x, p.y);
+ if (tp != null)
+ return new AccessibleJTreeNode(JTree.this, tp, null);
+ return null;
+ }
+
+ /**
+ * Return the nth Accessible child of the object.
+ *
+ * @param i - the accessible child to get
+ * @return the i-th child
+ */
+ public Accessible getAccessibleChild(int i)
+ {
+ return null;
+ }
+
+ /**
+ * Returns the number of top-level children nodes of this JTree.
+ *
+ * @return the number of top-level children
+ */
+ public int getAccessibleChildrenCount()
+ {
+ TreeModel model = getModel();
+ if (model != null)
+ return model.getChildCount(model.getRoot());
+ return 0;
+ }
+
+ /**
+ * Get the index of this object in its accessible parent.
+ *
+ * @return the index of this object.
+ */
+ public int getAccessibleIndexInParent()
+ {
+ return 0;
+ }
+
+ /**
+ * Get the role of this object.
+ *
+ * @return the role of this object
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.TREE;
+ }
+
+ /**
+ * Get the AccessibleSelection associated with this object.
+ *
+ * @return the accessible selection of the tree
+ */
+ public AccessibleSelection getAccessibleSelection()
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return (new AccessibleJTreeNode(JTree.this,
+ new TreePath(mod.getRoot()), null)).getAccessibleSelection();
+ return null;
+ }
+
+ /**
+ * Returns an Accessible representing the specified selected item in the object.
+ *
+ * @return the i-th accessible in the selection
+ */
+ public Accessible getAccessibleSelection(int i)
+ {
+ TreeModel mod = getModel();
+ if (mod != null)
+ return (new AccessibleJTreeNode(JTree.this,
+ new TreePath(mod.getRoot()), null)).getAccessibleSelection(i);
+ return null;
+ }
+
+ /**
+ * Returns the number of items currently selected.
+ *
+ * @return the number of selected accessibles.
+ */
+ public int getAccessibleSelectionCount()
+ {
+ return getSelectionCount();
+ }
+
+ /**
+ * Returns true if the current child of this object is selected.
+ *
+ * @param i - the child of this object
+ * @return true if the i-th child is selected.
+ */
+ public boolean isAccessibleChildSelected(int i)
+ {
+ // Nothing to do here.
+ return false;
+ }
+
+ /**
+ * Removes the specified selected item in the object from the object's
+ * selection.
+ *
+ * @param i - the i-th selected item to remove
+ */
+ public void removeAccessibleSelection(int i)
+ {
+ removeSelectionInterval(i, i);
+ }
+
+ /**
+ * Causes every selected item in the object to be selected if the object
+ * supports multiple selections.
+ */
+ public void selectAllAccessibleSelection()
+ {
+ if (getSelectionModel().getSelectionMode() !=
+ TreeSelectionModel.SINGLE_TREE_SELECTION)
+ addSelectionInterval(0, getVisibleRowCount());
+ }
+
+ /**
+ * Tree Collapsed notification
+ *
+ * @param e - the event
+ */
+ public void treeCollapsed(TreeExpansionEvent e)
+ {
+ fireTreeCollapsed(e.getPath());
+ }
+
+ /**
+ * Tree Model Expansion notification.
+ *
+ * @param e - the event
+ */
+ public void treeExpanded(TreeExpansionEvent e)
+ {
+ fireTreeExpanded(e.getPath());
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesChanged(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesInserted(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model Node change notification.
+ *
+ * @param e - the event
+ */
+ public void treeNodesRemoved(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Model structure change change notification.
+ *
+ * @param e - the event
+ */
+ public void treeStructureChanged(TreeModelEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Tree Selection Listener value change method.
+ *
+ * @param e - the event
+ */
+ public void valueChanged(TreeSelectionEvent e)
+ {
+ fireValueChanged(e);
+ }
+ }
+
+ public static class DynamicUtilTreeNode extends DefaultMutableTreeNode
+ {
+ protected Object childValue;
+
+ protected boolean loadedChildren;
+
+ /**
+ * Currently not set or used by this class. It might be set and used in
+ * later versions of this class.
+ */
+ protected boolean hasChildren;
+
+ public DynamicUtilTreeNode(Object value, Object children)
+ {
+ super(value);
+ childValue = children;
+ loadedChildren = false;
+ }
+
+ public int getChildCount()
+ {
+ loadChildren();
+ return super.getChildCount();
+ }
+
+ protected void loadChildren()
+ {
+ if (!loadedChildren)
+ {
+ createChildren(this, childValue);
+ loadedChildren = true;
+ }
+ }
+
+ public Enumeration children()
+ {
+ loadChildren();
+ return super.children();
+ }
+
+ /**
+ * Returns the child node at position pos. Subclassed
+ * here to load the children if necessary.
+ *
+ * @param pos the position of the child node to fetch
+ *
+ * @return the childnode at the specified position
+ */
+ public TreeNode getChildAt(int pos)
+ {
+ loadChildren();
+ return super.getChildAt(pos);
+ }
+
+ public boolean isLeaf()
+ {
+ return childValue == null || !(childValue instanceof Hashtable
+ || childValue instanceof Vector
+ || childValue.getClass().isArray());
+ }
+
+ public static void createChildren(DefaultMutableTreeNode parent,
+ Object children)
+ {
+ if (children instanceof Hashtable)
+ {
+ Hashtable tab = (Hashtable) children;
+ Enumeration e = tab.keys();
+ while (e.hasMoreElements())
+ {
+ Object key = e.nextElement();
+ Object val = tab.get(key);
+ parent.add(new DynamicUtilTreeNode(key, val));
+ }
+ }
+ else if (children instanceof Vector)
+ {
+ Iterator i = ((Vector) children).iterator();
+ while (i.hasNext())
+ {
+ Object n = i.next();
+ parent.add(new DynamicUtilTreeNode(n, n));
+ }
+ }
+ else if (children != null && children.getClass().isArray())
+ {
+ Object[] arr = (Object[]) children;
+ for (int i = 0; i < arr.length; ++i)
+ parent.add(new DynamicUtilTreeNode(arr[i], arr[i]));
+ }
+ }
+ }
+
+ /**
+ * Listens to the model of the JTree and updates the property
+ * expandedState if nodes are removed or changed.
+ */
+ protected class TreeModelHandler implements TreeModelListener
+ {
+
+ /**
+ * Creates a new instance of TreeModelHandler.
+ */
+ protected TreeModelHandler()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when a node has changed in some ways. This does not include
+ * that a node has changed its location or changed it's children. It
+ * only means that some attributes of the node have changed that might
+ * affect its presentation.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesChanged(TreeModelEvent ev)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when a node is inserted into the tree.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesInserted(TreeModelEvent ev)
+ {
+ // nothing to do here
+ }
+
+ /**
+ * Notifies when a node is removed from the tree.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeNodesRemoved(TreeModelEvent ev)
+ {
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ Object[] children = ev.getChildren();
+ TreeSelectionModel sm = getSelectionModel();
+ if (children != null)
+ {
+ TreePath path;
+ Vector toRemove = new Vector();
+ // Collect items that we must remove.
+ for (int i = children.length - 1; i >= 0; i--)
+ {
+ path = parent.pathByAddingChild(children[i]);
+ if (nodeStates.containsKey(path))
+ toRemove.add(path);
+ // Clear selection while we are at it.
+ if (sm != null)
+ removeDescendantSelectedPaths(path, true);
+ }
+ if (toRemove.size() > 0)
+ removeDescendantToggledPaths(toRemove.elements());
+ TreeModel model = getModel();
+ if (model == null || model.isLeaf(parent.getLastPathComponent()))
+ nodeStates.remove(parent);
+ }
+ }
+ }
+
+ /**
+ * Notifies when the structure of the tree is changed.
+ *
+ * This method is called after the actual change occured.
+ *
+ * @param ev the TreeModelEvent describing the change
+ */
+ public void treeStructureChanged(TreeModelEvent ev)
+ {
+ if (ev != null)
+ {
+ TreePath parent = ev.getTreePath();
+ if (parent != null)
+ {
+ if (parent.getPathCount() == 1)
+ {
+ // We have a new root, clear everything.
+ clearToggledPaths();
+ Object root = treeModel.getRoot();
+ if (root != null && treeModel.isLeaf(root))
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ else if (nodeStates.containsKey(parent))
+ {
+ Vector toRemove = new Vector();
+ boolean expanded = isExpanded(parent);
+ toRemove.add(parent);
+ removeDescendantToggledPaths(toRemove.elements());
+ if (expanded)
+ {
+ TreeModel model = getModel();
+ if (model != null
+ || model.isLeaf(parent.getLastPathComponent()))
+ collapsePath(parent);
+ else
+ nodeStates.put(parent, Boolean.TRUE);
+ }
+ }
+ removeDescendantSelectedPaths(parent, false);
+ }
+ }
+ }
+ }
+
+ /**
+ * This redirects TreeSelectionEvents and rewrites the source of it to be
+ * this JTree. This is typically done when the tree model generates an
+ * event, but the JTree object associated with that model should be listed
+ * as the actual source of the event.
+ */
+ protected class TreeSelectionRedirector implements TreeSelectionListener,
+ Serializable
+ {
+ /** The serial version UID. */
+ private static final long serialVersionUID = -3505069663646241664L;
+
+ /**
+ * Creates a new instance of TreeSelectionRedirector
+ */
+ protected TreeSelectionRedirector()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Notifies when the tree selection changes.
+ *
+ * @param ev the TreeSelectionEvent that describes the change
+ */
+ public void valueChanged(TreeSelectionEvent ev)
+ {
+ TreeSelectionEvent rewritten =
+ (TreeSelectionEvent) ev.cloneWithSource(JTree.this);
+ fireValueChanged(rewritten);
+ }
+ }
+
+ /**
+ * A TreeModel that does not allow anything to be selected.
+ */
+ protected static class EmptySelectionModel extends DefaultTreeSelectionModel
+ {
+ /** The serial version UID. */
+ private static final long serialVersionUID = -5815023306225701477L;
+
+ /**
+ * The shared instance of this model.
+ */
+ protected static final EmptySelectionModel sharedInstance =
+ new EmptySelectionModel();
+
+ /**
+ * Creates a new instance of EmptySelectionModel.
+ */
+ protected EmptySelectionModel()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the shared instance of EmptySelectionModel.
+ *
+ * @return the shared instance of EmptySelectionModel
+ */
+ public static EmptySelectionModel sharedInstance()
+ {
+ return sharedInstance;
+ }
+
+ /**
+ * This catches attempts to set a selection and sets nothing instead.
+ *
+ * @param paths not used here
+ */
+ public void setSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+
+ /**
+ * This catches attempts to add something to the selection.
+ *
+ * @param paths not used here
+ */
+ public void addSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+
+ /**
+ * This catches attempts to remove something from the selection.
+ *
+ * @param paths not used here
+ */
+ public void removeSelectionPaths(TreePath[] paths)
+ {
+ // We don't allow selections in this class.
+ }
+ }
+
+ private static final long serialVersionUID = 7559816092864483649L;
+
+ public static final String CELL_EDITOR_PROPERTY = "cellEditor";
+
+ public static final String CELL_RENDERER_PROPERTY = "cellRenderer";
+
+ public static final String EDITABLE_PROPERTY = "editable";
+
+ public static final String INVOKES_STOP_CELL_EDITING_PROPERTY =
+ "invokesStopCellEditing";
+
+ public static final String LARGE_MODEL_PROPERTY = "largeModel";
+
+ public static final String ROOT_VISIBLE_PROPERTY = "rootVisible";
+
+ public static final String ROW_HEIGHT_PROPERTY = "rowHeight";
+
+ public static final String SCROLLS_ON_EXPAND_PROPERTY = "scrollsOnExpand";
+
+ public static final String SELECTION_MODEL_PROPERTY = "selectionModel";
+
+ public static final String SHOWS_ROOT_HANDLES_PROPERTY = "showsRootHandles";
+
+ public static final String TOGGLE_CLICK_COUNT_PROPERTY = "toggleClickCount";
+
+ public static final String TREE_MODEL_PROPERTY = "model";
+
+ public static final String VISIBLE_ROW_COUNT_PROPERTY = "visibleRowCount";
+
+ /** @since 1.3 */
+ public static final String ANCHOR_SELECTION_PATH_PROPERTY =
+ "anchorSelectionPath";
+
+ /** @since 1.3 */
+ public static final String LEAD_SELECTION_PATH_PROPERTY = "leadSelectionPath";
+
+ /** @since 1.3 */
+ public static final String EXPANDS_SELECTED_PATHS_PROPERTY =
+ "expandsSelectedPaths";
+
+ private static final Object EXPANDED = Boolean.TRUE;
+
+ private static final Object COLLAPSED = Boolean.FALSE;
+
+ private boolean dragEnabled;
+
+ private boolean expandsSelectedPaths;
+
+ private TreePath anchorSelectionPath;
+
+ /**
+ * This contains the state of all nodes in the tree. Al/ entries map the
+ * TreePath of a note to to its state. Valid states are EXPANDED and
+ * COLLAPSED. Nodes not in this Hashtable are assumed state COLLAPSED.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Hashtable nodeStates = new Hashtable();
+
+ protected transient TreeCellEditor cellEditor;
+
+ protected transient TreeCellRenderer cellRenderer;
+
+ protected boolean editable;
+
+ protected boolean invokesStopCellEditing;
+
+ protected boolean largeModel;
+
+ protected boolean rootVisible;
+
+ protected int rowHeight;
+
+ protected boolean scrollsOnExpand;
+
+ protected transient TreeSelectionModel selectionModel;
+
+ protected boolean showsRootHandles;
+
+ protected int toggleClickCount;
+
+ protected transient TreeModel treeModel;
+
+ protected int visibleRowCount;
+
+ /**
+ * Handles TreeModelEvents to update the expandedState.
+ */
+ protected transient TreeModelListener treeModelListener;
+
+ /**
+ * Redirects TreeSelectionEvents so that the source is this JTree.
+ */
+ protected TreeSelectionRedirector selectionRedirector =
+ new TreeSelectionRedirector();
+
+ /**
+ * Indicates if the rowHeight property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientRowHeightSet = false;
+
+ /**
+ * Indicates if the scrollsOnExpand property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientScrollsOnExpandSet = false;
+
+ /**
+ * Indicates if the showsRootHandles property has been set by a client
+ * program or by the UI.
+ *
+ * @see #setUIProperty(String, Object)
+ * @see LookAndFeel#installProperty(JComponent, String, Object)
+ */
+ private boolean clientShowsRootHandlesSet = false;
+
+ /**
+ * Creates a new JTree object.
+ */
+ public JTree()
+ {
+ this(getDefaultTreeModel());
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Hashtable, ?> value)
+ {
+ this(createTreeModel(value));
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Object[] value)
+ {
+ this(createTreeModel(value));
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param model the model to use
+ */
+ public JTree(TreeModel model)
+ {
+ setRootVisible(true);
+ setSelectionModel( new DefaultTreeSelectionModel() );
+
+ // The root node appears expanded by default.
+ nodeStates = new Hashtable();
+
+ // The cell renderer gets set by the UI.
+ cellRenderer = null;
+
+ // Install the UI before installing the model. This way we avoid double
+ // initialization of lots of UI and model stuff inside the UI and related
+ // classes. The necessary UI updates are performed via property change
+ // events to the UI.
+ updateUI();
+ setModel(model);
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param root the root node
+ */
+ public JTree(TreeNode root)
+ {
+ this(root, false);
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param root the root node
+ * @param asksAllowChildren if false, all nodes without children are leaf
+ * nodes. If true, only nodes that do not allow children are leaf
+ * nodes.
+ */
+ public JTree(TreeNode root, boolean asksAllowChildren)
+ {
+ this(new DefaultTreeModel(root, asksAllowChildren));
+ }
+
+ /**
+ * Creates a new JTree object.
+ *
+ * @param value the initial nodes in the tree
+ */
+ public JTree(Vector> value)
+ {
+ this(createTreeModel(value));
+ }
+
+ public int getRowForPath(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getRowForPath(this, path);
+
+ return -1;
+ }
+
+ public TreePath getPathForRow(int row)
+ {
+ TreeUI ui = getUI();
+ return ui != null ? ui.getPathForRow(this, row) : null;
+ }
+
+ /**
+ * Get the pathes that are displayes between the two given rows.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ *
+ * @return the array of the tree pathes
+ */
+ protected TreePath[] getPathBetweenRows(int index0, int index1)
+ {
+ TreeUI ui = getUI();
+
+ if (ui == null)
+ return null;
+
+ int minIndex = Math.min(index0, index1);
+ int maxIndex = Math.max(index0, index1);
+ TreePath[] paths = new TreePath[maxIndex - minIndex + 1];
+
+ for (int i = minIndex; i <= maxIndex; ++i)
+ paths[i - minIndex] = ui.getPathForRow(this, i);
+
+ return paths;
+ }
+
+ /**
+ * Creates a new TreeModel object.
+ *
+ * @param value the values stored in the model
+ */
+ protected static TreeModel createTreeModel(Object value)
+ {
+ return new DefaultTreeModel(new DynamicUtilTreeNode(value, value));
+ }
+
+ /**
+ * Return the UI associated with this JTree object.
+ *
+ * @return the associated TreeUI object
+ */
+ public TreeUI getUI()
+ {
+ return (TreeUI) ui;
+ }
+
+ /**
+ * Sets the UI associated with this JTree object.
+ *
+ * @param ui the TreeUI to associate
+ */
+ public void setUI(TreeUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults..
+ */
+ public void updateUI()
+ {
+ setUI((TreeUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "TreeUI";
+ }
+
+ /**
+ * Gets the AccessibleContext associated with this
+ * JTree.
+ *
+ * @return the associated context
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return new AccessibleJTree();
+ }
+
+ /**
+ * Returns the preferred viewport size.
+ *
+ * @return the preferred size
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+ return getPreferredSize();
+ }
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given scrolling
+ * direction and orientation. This method handles a partially exposed row by
+ * returning the distance required to completely expose the item.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction)
+ {
+ int delta = 0;
+
+ // Round so that the top would start from the row boundary
+ if (orientation == SwingConstants.VERTICAL)
+ {
+ int row = getClosestRowForLocation(0, visibleRect.y);
+ if (row != -1)
+ {
+ Rectangle b = getRowBounds(row);
+ if (b.y != visibleRect.y)
+ {
+ if (direction < 0)
+ delta = Math.max(0, visibleRect.y - b.y);
+ else
+ delta = b.y + b.height - visibleRect.y;
+ }
+ else
+ {
+ if (direction < 0)
+ {
+ if (row != 0)
+ {
+ b = getRowBounds(row - 1);
+ delta = b.height;
+ }
+ }
+ else
+ delta = b.height;
+ }
+ }
+ }
+ else
+ // The RI always returns 4 for HORIZONTAL scrolling.
+ delta = 4;
+ return delta;
+ }
+
+ public int getScrollableBlockIncrement(Rectangle visibleRect,
+ int orientation, int direction)
+ {
+ int block;
+ if (orientation == SwingConstants.VERTICAL)
+ block = visibleRect.height;
+ else
+ block = visibleRect.width;
+ return block;
+ }
+
+ public boolean getScrollableTracksViewportHeight()
+ {
+ if (getParent() instanceof JViewport)
+ return ((JViewport) getParent()).getHeight() > getPreferredSize().height;
+ return false;
+ }
+
+ public boolean getScrollableTracksViewportWidth()
+ {
+ if (getParent() instanceof JViewport)
+ return ((JViewport) getParent()).getWidth() > getPreferredSize().width;
+ return false;
+ }
+
+ /**
+ * Adds a TreeExpansionListener object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeExpansionListener(TreeExpansionListener listener)
+ {
+ listenerList.add(TreeExpansionListener.class, listener);
+ }
+
+ /**
+ * Removes a TreeExpansionListener object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeExpansionListener(TreeExpansionListener listener)
+ {
+ listenerList.remove(TreeExpansionListener.class, listener);
+ }
+
+ /**
+ * Returns all added TreeExpansionListener objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeExpansionListener[] getTreeExpansionListeners()
+ {
+ return (TreeExpansionListener[]) getListeners(TreeExpansionListener.class);
+ }
+
+ /**
+ * Notifies all listeners that the tree was collapsed.
+ *
+ * @param path the path to the node that was collapsed
+ */
+ public void fireTreeCollapsed(TreePath path)
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeExpansionListener[] listeners = getTreeExpansionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeCollapsed(event);
+ }
+
+ /**
+ * Notifies all listeners that the tree was expanded.
+ *
+ * @param path the path to the node that was expanded
+ */
+ public void fireTreeExpanded(TreePath path)
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeExpansionListener[] listeners = getTreeExpansionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeExpanded(event);
+ }
+
+ /**
+ * Adds a TreeSelctionListener object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.add(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Removes a TreeSelectionListener object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeSelectionListener(TreeSelectionListener listener)
+ {
+ listenerList.remove(TreeSelectionListener.class, listener);
+ }
+
+ /**
+ * Returns all added TreeSelectionListener objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeSelectionListener[] getTreeSelectionListeners()
+ {
+ return (TreeSelectionListener[])
+ getListeners(TreeSelectionListener.class);
+ }
+
+ /**
+ * Notifies all listeners when the selection of the tree changed.
+ *
+ * @param event the event to send
+ */
+ protected void fireValueChanged(TreeSelectionEvent event)
+ {
+ TreeSelectionListener[] listeners = getTreeSelectionListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].valueChanged(event);
+ }
+
+ /**
+ * Adds a TreeWillExpandListener object to the tree.
+ *
+ * @param listener the listener to add
+ */
+ public void addTreeWillExpandListener(TreeWillExpandListener listener)
+ {
+ listenerList.add(TreeWillExpandListener.class, listener);
+ }
+
+ /**
+ * Removes a TreeWillExpandListener object from the tree.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeTreeWillExpandListener(TreeWillExpandListener listener)
+ {
+ listenerList.remove(TreeWillExpandListener.class, listener);
+ }
+
+ /**
+ * Returns all added TreeWillExpandListener objects.
+ *
+ * @return an array of listeners
+ */
+ public TreeWillExpandListener[] getTreeWillExpandListeners()
+ {
+ return (TreeWillExpandListener[])
+ getListeners(TreeWillExpandListener.class);
+ }
+
+ /**
+ * Notifies all listeners that the tree will collapse.
+ *
+ * @param path the path to the node that will collapse
+ */
+ public void fireTreeWillCollapse(TreePath path) throws ExpandVetoException
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeWillCollapse(event);
+ }
+
+ /**
+ * Notifies all listeners that the tree will expand.
+ *
+ * @param path the path to the node that will expand
+ */
+ public void fireTreeWillExpand(TreePath path) throws ExpandVetoException
+ {
+ TreeExpansionEvent event = new TreeExpansionEvent(this, path);
+ TreeWillExpandListener[] listeners = getTreeWillExpandListeners();
+
+ for (int index = 0; index < listeners.length; ++index)
+ listeners[index].treeWillExpand(event);
+ }
+
+ /**
+ * Returns the model of this JTree object.
+ *
+ * @return the associated TreeModel
+ */
+ public TreeModel getModel()
+ {
+ return treeModel;
+ }
+
+ /**
+ * Sets the model to use in JTree.
+ *
+ * @param model the TreeModel to use
+ */
+ public void setModel(TreeModel model)
+ {
+ if (treeModel == model)
+ return;
+
+ // Remove listeners from old model.
+ if (treeModel != null && treeModelListener != null)
+ treeModel.removeTreeModelListener(treeModelListener);
+
+ // add treeModelListener to the new model
+ if (treeModelListener == null)
+ treeModelListener = createTreeModelListener();
+ if (model != null) // as setModel(null) is allowed
+ model.addTreeModelListener(treeModelListener);
+
+ TreeModel oldValue = treeModel;
+ treeModel = model;
+ clearToggledPaths();
+
+ if (treeModel != null)
+ {
+ if (treeModelListener == null)
+ treeModelListener = createTreeModelListener();
+ if (treeModelListener != null)
+ treeModel.addTreeModelListener(treeModelListener);
+ Object root = treeModel.getRoot();
+ if (root != null && !treeModel.isLeaf(root))
+ {
+ nodeStates.put(new TreePath(root), Boolean.TRUE);
+ }
+ }
+
+ firePropertyChange(TREE_MODEL_PROPERTY, oldValue, model);
+ }
+
+ /**
+ * Checks if this JTree object is editable.
+ *
+ * @return true if this tree object is editable,
+ * false otherwise
+ */
+ public boolean isEditable()
+ {
+ return editable;
+ }
+
+ /**
+ * Sets the editable property.
+ *
+ * @param flag true to make this tree object editable,
+ * false otherwise
+ */
+ public void setEditable(boolean flag)
+ {
+ if (editable == flag)
+ return;
+
+ boolean oldValue = editable;
+ editable = flag;
+ firePropertyChange(EDITABLE_PROPERTY, oldValue, editable);
+ }
+
+ /**
+ * Checks if the root element is visible.
+ *
+ * @return true if the root element is visible,
+ * false otherwise
+ */
+ public boolean isRootVisible()
+ {
+ return rootVisible;
+ }
+
+ public void setRootVisible(boolean flag)
+ {
+ if (rootVisible == flag)
+ return;
+
+ // If the root is currently selected, unselect it
+ if (rootVisible && !flag)
+ {
+ TreeSelectionModel model = getSelectionModel();
+ // The root is always shown in the first row
+ TreePath rootPath = getPathForRow(0);
+ model.removeSelectionPath(rootPath);
+ }
+
+ boolean oldValue = rootVisible;
+ rootVisible = flag;
+ firePropertyChange(ROOT_VISIBLE_PROPERTY, oldValue, flag);
+
+ }
+
+ public boolean getShowsRootHandles()
+ {
+ return showsRootHandles;
+ }
+
+ public void setShowsRootHandles(boolean flag)
+ {
+ clientShowsRootHandlesSet = true;
+
+ if (showsRootHandles == flag)
+ return;
+
+ boolean oldValue = showsRootHandles;
+ showsRootHandles = flag;
+ firePropertyChange(SHOWS_ROOT_HANDLES_PROPERTY, oldValue, flag);
+ }
+
+ public TreeCellEditor getCellEditor()
+ {
+ return cellEditor;
+ }
+
+ public void setCellEditor(TreeCellEditor editor)
+ {
+ if (cellEditor == editor)
+ return;
+
+ TreeCellEditor oldValue = cellEditor;
+ cellEditor = editor;
+ firePropertyChange(CELL_EDITOR_PROPERTY, oldValue, editor);
+ }
+
+ public TreeCellRenderer getCellRenderer()
+ {
+ return cellRenderer;
+ }
+
+ public void setCellRenderer(TreeCellRenderer newRenderer)
+ {
+ if (cellRenderer == newRenderer)
+ return;
+
+ TreeCellRenderer oldValue = cellRenderer;
+ cellRenderer = newRenderer;
+ firePropertyChange(CELL_RENDERER_PROPERTY, oldValue, newRenderer);
+ }
+
+ public TreeSelectionModel getSelectionModel()
+ {
+ return selectionModel;
+ }
+
+ public void setSelectionModel(TreeSelectionModel model)
+ {
+ if (selectionModel == model)
+ return;
+
+ if( model == null )
+ model = EmptySelectionModel.sharedInstance();
+
+ if (selectionModel != null)
+ selectionModel.removeTreeSelectionListener(selectionRedirector);
+
+ TreeSelectionModel oldValue = selectionModel;
+ selectionModel = model;
+
+ selectionModel.addTreeSelectionListener(selectionRedirector);
+
+ firePropertyChange(SELECTION_MODEL_PROPERTY, oldValue, model);
+ revalidate();
+ repaint();
+ }
+
+ public int getVisibleRowCount()
+ {
+ return visibleRowCount;
+ }
+
+ public void setVisibleRowCount(int rows)
+ {
+ if (visibleRowCount == rows)
+ return;
+
+ int oldValue = visibleRowCount;
+ visibleRowCount = rows;
+ firePropertyChange(VISIBLE_ROW_COUNT_PROPERTY, oldValue, rows);
+ }
+
+ public boolean isLargeModel()
+ {
+ return largeModel;
+ }
+
+ public void setLargeModel(boolean large)
+ {
+ if (largeModel == large)
+ return;
+
+ boolean oldValue = largeModel;
+ largeModel = large;
+ firePropertyChange(LARGE_MODEL_PROPERTY, oldValue, large);
+ }
+
+ public int getRowHeight()
+ {
+ return rowHeight;
+ }
+
+ public void setRowHeight(int height)
+ {
+ clientRowHeightSet = true;
+
+ if (rowHeight == height)
+ return;
+
+ int oldValue = rowHeight;
+ rowHeight = height;
+ firePropertyChange(ROW_HEIGHT_PROPERTY, oldValue, height);
+ }
+
+ public boolean isFixedRowHeight()
+ {
+ return rowHeight > 0;
+ }
+
+ public boolean getInvokesStopCellEditing()
+ {
+ return invokesStopCellEditing;
+ }
+
+ public void setInvokesStopCellEditing(boolean invoke)
+ {
+ if (invokesStopCellEditing == invoke)
+ return;
+
+ boolean oldValue = invokesStopCellEditing;
+ invokesStopCellEditing = invoke;
+ firePropertyChange(INVOKES_STOP_CELL_EDITING_PROPERTY,
+ oldValue, invoke);
+ }
+
+ /**
+ * @since 1.3
+ */
+ public int getToggleClickCount()
+ {
+ return toggleClickCount;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setToggleClickCount(int count)
+ {
+ if (toggleClickCount == count)
+ return;
+
+ int oldValue = toggleClickCount;
+ toggleClickCount = count;
+ firePropertyChange(TOGGLE_CLICK_COUNT_PROPERTY, oldValue, count);
+ }
+
+ public void scrollPathToVisible(TreePath path)
+ {
+ if (path == null)
+ return;
+ Rectangle rect = getPathBounds(path);
+ scrollRectToVisible(rect);
+ }
+
+ public void scrollRowToVisible(int row)
+ {
+ scrollPathToVisible(getPathForRow(row));
+ }
+
+ public boolean getScrollsOnExpand()
+ {
+ return scrollsOnExpand;
+ }
+
+ public void setScrollsOnExpand(boolean scroll)
+ {
+ clientScrollsOnExpandSet = true;
+ if (scrollsOnExpand == scroll)
+ return;
+
+ boolean oldValue = scrollsOnExpand;
+ scrollsOnExpand = scroll;
+ firePropertyChange(SCROLLS_ON_EXPAND_PROPERTY, oldValue, scroll);
+ }
+
+ public void setSelectionPath(TreePath path)
+ {
+ clearSelectionPathStates();
+ selectionModel.setSelectionPath(path);
+ }
+
+ public void setSelectionPaths(TreePath[] paths)
+ {
+ clearSelectionPathStates();
+ selectionModel.setSelectionPaths(paths);
+ }
+
+ /**
+ * This method, and all calls to it, should be removed once the
+ * DefaultTreeModel fires events properly. Maintenance of the nodeStates
+ * table should really be done in the TreeModelHandler.
+ */
+ private void clearSelectionPathStates()
+ {
+ TreePath[] oldPaths = selectionModel.getSelectionPaths();
+ if (oldPaths != null)
+ for (int i = 0; i < oldPaths.length; i++)
+ nodeStates.remove(oldPaths[i]);
+ }
+
+ public void setSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ setSelectionPath(path);
+ }
+
+ public void setSelectionRows(int[] rows)
+ {
+ // Make sure we have an UI so getPathForRow() does not return null.
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ setSelectionPaths(paths);
+ }
+
+ public void setSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ setSelectionPaths(paths);
+ }
+
+ public void addSelectionPath(TreePath path)
+ {
+ selectionModel.addSelectionPath(path);
+ }
+
+ public void addSelectionPaths(TreePath[] paths)
+ {
+ selectionModel.addSelectionPaths(paths);
+ }
+
+ public void addSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ selectionModel.addSelectionPath(path);
+ }
+
+ public void addSelectionRows(int[] rows)
+ {
+ // Make sure we have an UI so getPathForRow() does not return null.
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ addSelectionPaths(paths);
+ }
+
+ /**
+ * Select all rows between the two given indexes, inclusive. The method
+ * will not select the inner leaves and braches of the currently collapsed
+ * nodes in this interval.
+ *
+ * @param index0 the starting row, inclusive
+ * @param index1 the ending row, inclusive
+ */
+ public void addSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ addSelectionPaths(paths);
+ }
+
+ public void removeSelectionPath(TreePath path)
+ {
+ clearSelectionPathStates();
+ selectionModel.removeSelectionPath(path);
+ }
+
+ public void removeSelectionPaths(TreePath[] paths)
+ {
+ clearSelectionPathStates();
+ selectionModel.removeSelectionPaths(paths);
+ }
+
+ public void removeSelectionRow(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ removeSelectionPath(path);
+ }
+
+ public void removeSelectionRows(int[] rows)
+ {
+ if (rows == null || getUI() == null)
+ return;
+
+ TreePath[] paths = new TreePath[rows.length];
+
+ for (int i = rows.length - 1; i >= 0; --i)
+ paths[i] = getPathForRow(rows[i]);
+
+ removeSelectionPaths(paths);
+ }
+
+ public void removeSelectionInterval(int index0, int index1)
+ {
+ TreePath[] paths = getPathBetweenRows(index0, index1);
+
+ if (paths != null)
+ removeSelectionPaths(paths);
+ }
+
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ setLeadSelectionPath(null);
+ }
+
+ public TreePath getLeadSelectionPath()
+ {
+ if (selectionModel == null)
+ return null;
+ else
+ return selectionModel.getLeadSelectionPath();
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setLeadSelectionPath(TreePath path)
+ {
+ if (selectionModel != null)
+ {
+ TreePath oldValue = selectionModel.getLeadSelectionPath();
+ if (path == oldValue || path != null && path.equals(oldValue))
+ return;
+
+ // Repaint the previous and current rows with the lead selection path.
+ if (path != null)
+ {
+ repaint(getPathBounds(path));
+ selectionModel.addSelectionPath(path);
+ }
+
+ if (oldValue != null)
+ repaint(getPathBounds(oldValue));
+
+ firePropertyChange(LEAD_SELECTION_PATH_PROPERTY, oldValue, path);
+ }
+ }
+
+ /**
+ * @since 1.3
+ */
+ public TreePath getAnchorSelectionPath()
+ {
+ return anchorSelectionPath;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setAnchorSelectionPath(TreePath path)
+ {
+ if (anchorSelectionPath == path)
+ return;
+
+ TreePath oldValue = anchorSelectionPath;
+ anchorSelectionPath = path;
+ firePropertyChange(ANCHOR_SELECTION_PATH_PROPERTY, oldValue, path);
+ }
+
+ public int getLeadSelectionRow()
+ {
+ return selectionModel.getLeadSelectionRow();
+ }
+
+ public int getMaxSelectionRow()
+ {
+ return selectionModel.getMaxSelectionRow();
+ }
+
+ public int getMinSelectionRow()
+ {
+ return selectionModel.getMinSelectionRow();
+ }
+
+ public int getSelectionCount()
+ {
+ return selectionModel.getSelectionCount();
+ }
+
+ public TreePath getSelectionPath()
+ {
+ return selectionModel.getSelectionPath();
+ }
+
+ public TreePath[] getSelectionPaths()
+ {
+ return selectionModel.getSelectionPaths();
+ }
+
+ public int[] getSelectionRows()
+ {
+ return selectionModel.getSelectionRows();
+ }
+
+ public boolean isPathSelected(TreePath path)
+ {
+ return selectionModel.isPathSelected(path);
+ }
+
+ /**
+ * Returns true when the specified row is selected,
+ * false otherwise. This call is delegated to the
+ * {@link TreeSelectionModel#isRowSelected(int)} method.
+ *
+ * @param row the row to check
+ *
+ * @return true when the specified row is selected,
+ * false otherwise
+ */
+ public boolean isRowSelected(int row)
+ {
+ return selectionModel.isRowSelected(row);
+ }
+
+ public boolean isSelectionEmpty()
+ {
+ return selectionModel.isSelectionEmpty();
+ }
+
+ /**
+ * Return the value of the dragEnabled property.
+ *
+ * @return the value
+ *
+ * @since 1.4
+ */
+ public boolean getDragEnabled()
+ {
+ return dragEnabled;
+ }
+
+ /**
+ * Set the dragEnabled property.
+ *
+ * @param enabled new value
+ *
+ * @since 1.4
+ */
+ public void setDragEnabled(boolean enabled)
+ {
+ dragEnabled = enabled;
+ }
+
+ public int getRowCount()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getRowCount(this);
+
+ return 0;
+ }
+
+ public void collapsePath(TreePath path)
+ {
+ try
+ {
+ fireTreeWillCollapse(path);
+ }
+ catch (ExpandVetoException ev)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+ setExpandedState(path, false);
+ fireTreeCollapsed(path);
+ }
+
+ public void collapseRow(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ collapsePath(path);
+ }
+
+ public void expandPath(TreePath path)
+ {
+ // Don't expand if path is null
+ // or is already expanded.
+ if (path == null || isExpanded(path))
+ return;
+
+ try
+ {
+ fireTreeWillExpand(path);
+ }
+ catch (ExpandVetoException ev)
+ {
+ // We do nothing if attempt has been vetoed.
+ }
+
+ setExpandedState(path, true);
+ fireTreeExpanded(path);
+ }
+
+ public void expandRow(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ expandPath(path);
+ }
+
+ public boolean isCollapsed(TreePath path)
+ {
+ return !isExpanded(path);
+ }
+
+ public boolean isCollapsed(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return false;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return isCollapsed(path);
+
+ return false;
+ }
+
+ public boolean isExpanded(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ Object state = nodeStates.get(path);
+
+ if ((state == null) || (state != EXPANDED))
+ return false;
+
+ TreePath parent = path.getParentPath();
+
+ if (parent != null)
+ return isExpanded(parent);
+
+ return true;
+ }
+
+ public boolean isExpanded(int row)
+ {
+ if (row < 0 || row >= getRowCount())
+ return false;
+
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return isExpanded(path);
+
+ return false;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public boolean getExpandsSelectedPaths()
+ {
+ return expandsSelectedPaths;
+ }
+
+ /**
+ * @since 1.3
+ */
+ public void setExpandsSelectedPaths(boolean flag)
+ {
+ if (expandsSelectedPaths == flag)
+ return;
+
+ boolean oldValue = expandsSelectedPaths;
+ expandsSelectedPaths = flag;
+ firePropertyChange(EXPANDS_SELECTED_PATHS_PROPERTY, oldValue, flag);
+ }
+
+ public Rectangle getPathBounds(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui == null)
+ return null;
+
+ return ui.getPathBounds(this, path);
+ }
+
+ public Rectangle getRowBounds(int row)
+ {
+ TreePath path = getPathForRow(row);
+
+ if (path != null)
+ return getPathBounds(path);
+
+ return null;
+ }
+
+ public boolean isEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.isEditing(this);
+
+ return false;
+ }
+
+ public boolean stopEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (isEditing())
+ if (ui != null)
+ return ui.stopEditing(this);
+
+ return false;
+ }
+
+ public void cancelEditing()
+ {
+ TreeUI ui = getUI();
+
+ if (isEditing())
+ if (ui != null)
+ ui.cancelEditing(this);
+ }
+
+ public void startEditingAtPath(TreePath path)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ ui.startEditingAtPath(this, path);
+ }
+
+ public TreePath getEditingPath()
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getEditingPath(this);
+
+ return null;
+ }
+
+ public TreePath getPathForLocation(int x, int y)
+ {
+ TreePath path = getClosestPathForLocation(x, y);
+
+ if (path != null)
+ {
+ Rectangle rect = getPathBounds(path);
+
+ if ((rect != null) && rect.contains(x, y))
+ return path;
+ }
+
+ return null;
+ }
+
+ public int getRowForLocation(int x, int y)
+ {
+ TreePath path = getPathForLocation(x, y);
+
+ if (path != null)
+ return getRowForPath(path);
+
+ return -1;
+ }
+
+ public TreePath getClosestPathForLocation(int x, int y)
+ {
+ TreeUI ui = getUI();
+
+ if (ui != null)
+ return ui.getClosestPathForLocation(this, x, y);
+
+ return null;
+ }
+
+ public int getClosestRowForLocation(int x, int y)
+ {
+ TreePath path = getClosestPathForLocation(x, y);
+
+ if (path != null)
+ return getRowForPath(path);
+
+ return -1;
+ }
+
+ public Object getLastSelectedPathComponent()
+ {
+ TreePath path = getSelectionPath();
+
+ if (path != null)
+ return path.getLastPathComponent();
+
+ return null;
+ }
+
+ private void doExpandParents(TreePath path, boolean state)
+ {
+ TreePath parent = path.getParentPath();
+
+ if (!isExpanded(parent) && parent != null)
+ doExpandParents(parent, false);
+
+ nodeStates.put(path, state ? EXPANDED : COLLAPSED);
+ }
+
+ protected void setExpandedState(TreePath path, boolean state)
+ {
+ if (path == null)
+ return;
+
+ doExpandParents(path, state);
+ }
+
+ protected void clearToggledPaths()
+ {
+ nodeStates.clear();
+ }
+
+ protected Enumeration getDescendantToggledPaths(TreePath parent)
+ {
+ if (parent == null)
+ return null;
+
+ Enumeration nodes = nodeStates.keys();
+ Vector result = new Vector();
+
+ while (nodes.hasMoreElements())
+ {
+ TreePath path = (TreePath) nodes.nextElement();
+
+ if (path.isDescendant(parent))
+ result.addElement(path);
+ }
+
+ return result.elements();
+ }
+
+ public boolean hasBeenExpanded(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ return nodeStates.get(path) != null;
+ }
+
+ public boolean isVisible(TreePath path)
+ {
+ if (path == null)
+ return false;
+
+ TreePath parent = path.getParentPath();
+
+ if (parent == null)
+ return true; // Is root node.
+
+ return isExpanded(parent);
+ }
+
+ public void makeVisible(TreePath path)
+ {
+ if (path == null)
+ return;
+
+ expandPath(path.getParentPath());
+ }
+
+ public boolean isPathEditable(TreePath path)
+ {
+ return isEditable();
+ }
+
+ /**
+ * Creates and returns an instance of {@link TreeModelHandler}.
+ *
+ * @return an instance of {@link TreeModelHandler}
+ */
+ protected TreeModelListener createTreeModelListener()
+ {
+ return new TreeModelHandler();
+ }
+
+ /**
+ * Returns a sample TreeModel that can be used in a JTree. This can be used
+ * in Bean- or GUI-Builders to show something interesting.
+ *
+ * @return a sample TreeModel that can be used in a JTree
+ */
+ protected static TreeModel getDefaultTreeModel()
+ {
+ DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node");
+ DefaultMutableTreeNode child1 = new DefaultMutableTreeNode("Child node 1");
+ DefaultMutableTreeNode child11 =
+ new DefaultMutableTreeNode("Child node 1.1");
+ DefaultMutableTreeNode child12 =
+ new DefaultMutableTreeNode("Child node 1.2");
+ DefaultMutableTreeNode child13 =
+ new DefaultMutableTreeNode("Child node 1.3");
+ DefaultMutableTreeNode child2 = new DefaultMutableTreeNode("Child node 2");
+ DefaultMutableTreeNode child21 =
+ new DefaultMutableTreeNode("Child node 2.1");
+ DefaultMutableTreeNode child22 =
+ new DefaultMutableTreeNode("Child node 2.2");
+ DefaultMutableTreeNode child23 =
+ new DefaultMutableTreeNode("Child node 2.3");
+ DefaultMutableTreeNode child24 =
+ new DefaultMutableTreeNode("Child node 2.4");
+
+ DefaultMutableTreeNode child3 = new DefaultMutableTreeNode("Child node 3");
+ root.add(child1);
+ root.add(child2);
+ root.add(child3);
+ child1.add(child11);
+ child1.add(child12);
+ child1.add(child13);
+ child2.add(child21);
+ child2.add(child22);
+ child2.add(child23);
+ child2.add(child24);
+ return new DefaultTreeModel(root);
+ }
+
+ /**
+ * Converts the specified value to a String. This is used by the renderers
+ * of this JTree and its nodes.
+ *
+ * This implementation simply returns value.toString() and
+ * ignores all other parameters. Subclass this method to control the
+ * conversion.
+ *
+ * @param value the value that is converted to a String
+ * @param selected indicates if that value is selected or not
+ * @param expanded indicates if that value is expanded or not
+ * @param leaf indicates if that value is a leaf node or not
+ * @param row the row of the node
+ * @param hasFocus indicates if that node has focus or not
+ */
+ public String convertValueToText(Object value, boolean selected,
+ boolean expanded, boolean leaf, int row, boolean hasFocus)
+ {
+ return value.toString();
+ }
+
+ /**
+ * A String representation of this JTree. This is intended to be used for
+ * debugging. The returned string may be empty but may not be
+ * null.
+ *
+ * @return a String representation of this JTree
+ */
+ protected String paramString()
+ {
+ // TODO: this is completely legal, but it would possibly be nice
+ // to return some more content, like the tree structure, some properties
+ // etc ...
+ return "";
+ }
+
+ /**
+ * Returns all TreePath objects which are a descendants of the given path
+ * and are exapanded at the moment of the execution of this method. If the
+ * state of any node is beeing toggled while this method is executing this
+ * change may be left unaccounted.
+ *
+ * @param path The parent of this request
+ *
+ * @return An Enumeration containing TreePath objects
+ */
+ public Enumeration getExpandedDescendants(TreePath path)
+ {
+ Enumeration paths = nodeStates.keys();
+ Vector relevantPaths = new Vector();
+ while (paths.hasMoreElements())
+ {
+ TreePath nextPath = (TreePath) paths.nextElement();
+ if (nodeStates.get(nextPath) == EXPANDED
+ && path.isDescendant(nextPath))
+ {
+ relevantPaths.add(nextPath);
+ }
+ }
+ return relevantPaths.elements();
+ }
+
+ /**
+ * Returns the next table element (beginning from the row
+ * startingRow that starts with prefix.
+ * Searching is done in the direction specified by bias.
+ *
+ * @param prefix the prefix to search for in the cell values
+ * @param startingRow the index of the row where to start searching from
+ * @param bias the search direction, either {@link Position.Bias#Forward} or
+ * {@link Position.Bias#Backward}
+ *
+ * @return the path to the found element or -1 if no such element has been
+ * found
+ *
+ * @throws IllegalArgumentException if prefix is null or
+ * startingRow is not valid
+ *
+ * @since 1.4
+ */
+ public TreePath getNextMatch(String prefix, int startingRow,
+ Position.Bias bias)
+ {
+ if (prefix == null)
+ throw new IllegalArgumentException("The argument 'prefix' must not be"
+ + " null.");
+ if (startingRow < 0)
+ throw new IllegalArgumentException("The argument 'startingRow' must not"
+ + " be less than zero.");
+
+ int size = getRowCount();
+ if (startingRow > size)
+ throw new IllegalArgumentException("The argument 'startingRow' must not"
+ + " be greater than the number of"
+ + " elements in the TreeModel.");
+
+ TreePath foundPath = null;
+ if (bias == Position.Bias.Forward)
+ {
+ for (int i = startingRow; i < size; i++)
+ {
+ TreePath path = getPathForRow(i);
+ Object o = path.getLastPathComponent();
+ // FIXME: in the following call to convertValueToText the
+ // last argument (hasFocus) should be done right.
+ String item = convertValueToText(o, isRowSelected(i),
+ isExpanded(i), treeModel.isLeaf(o),
+ i, false);
+ if (item.startsWith(prefix))
+ {
+ foundPath = path;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (int i = startingRow; i >= 0; i--)
+ {
+ TreePath path = getPathForRow(i);
+ Object o = path.getLastPathComponent();
+ // FIXME: in the following call to convertValueToText the
+ // last argument (hasFocus) should be done right.
+ String item = convertValueToText(o, isRowSelected(i),
+ isExpanded(i), treeModel.isLeaf(o), i, false);
+ if (item.startsWith(prefix))
+ {
+ foundPath = path;
+ break;
+ }
+ }
+ }
+ return foundPath;
+ }
+
+ /**
+ * Removes any paths in the current set of selected paths that are
+ * descendants of path. If includePath is set
+ * to true and path itself is selected, then
+ * it will be removed too.
+ *
+ * @param path the path from which selected descendants are to be removed
+ * @param includeSelected if true then path itself
+ * will also be remove if it's selected
+ *
+ * @return true if something has been removed,
+ * false otherwise
+ *
+ * @since 1.3
+ */
+ protected boolean removeDescendantSelectedPaths(TreePath path,
+ boolean includeSelected)
+ {
+ boolean removedSomething = false;
+ TreePath[] selected = getSelectionPaths();
+ for (int index = 0; index < selected.length; index++)
+ {
+ if ((selected[index] == path && includeSelected)
+ || (selected[index].isDescendant(path)))
+ {
+ removeSelectionPath(selected[index]);
+ removedSomething = true;
+ }
+ }
+ return removedSomething;
+ }
+
+ /**
+ * Removes any descendants of the TreePaths in toRemove that have been
+ * expanded.
+ *
+ * @param toRemove - Enumeration of TreePaths that need to be removed from
+ * cache of toggled tree paths.
+ */
+ protected void removeDescendantToggledPaths(Enumeration toRemove)
+ {
+ while (toRemove.hasMoreElements())
+ {
+ TreePath current = toRemove.nextElement();
+ Enumeration descendants = getDescendantToggledPaths(current);
+
+ while (descendants.hasMoreElements())
+ {
+ TreePath currentDes = (TreePath) descendants.nextElement();
+ if (isExpanded(currentDes))
+ nodeStates.remove(currentDes);
+ }
+ }
+ }
+
+ /**
+ *
+ * Sent when the tree has changed enough that we need to resize the bounds,
+ * but not enough that we need to remove the expanded node set (e.g nodes were
+ * expanded or collapsed, or nodes were inserted into the tree). You should
+ * never have to invoke this, the UI will invoke this as it needs to.
+ *
+ *
+ * If the tree uses {@link DefaultTreeModel}, you must call
+ * {@link DefaultTreeModel#reload(TreeNode)} or
+ * {@link DefaultTreeModel#reload()} after adding or removing nodes. Following
+ * the official Java 1.5 API standard, just calling treeDidChange, repaint()
+ * or revalidate() does not update the tree appearance properly.
+ *
+ * @see DefaultTreeModel#reload()
+ */
+ public void treeDidChange()
+ {
+ repaint();
+ }
+
+ /**
+ * Helper method for
+ * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
+ *
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ */
+ void setUIProperty(String propertyName, Object value)
+ {
+ if (propertyName.equals("rowHeight"))
+ {
+ if (! clientRowHeightSet)
+ {
+ setRowHeight(((Integer) value).intValue());
+ clientRowHeightSet = false;
+ }
+ }
+ else if (propertyName.equals("scrollsOnExpand"))
+ {
+ if (! clientScrollsOnExpandSet)
+ {
+ setScrollsOnExpand(((Boolean) value).booleanValue());
+ clientScrollsOnExpandSet = false;
+ }
+ }
+ else if (propertyName.equals("showsRootHandles"))
+ {
+ if (! clientShowsRootHandlesSet)
+ {
+ setShowsRootHandles(((Boolean) value).booleanValue());
+ clientShowsRootHandlesSet = false;
+ }
+ }
+ else
+ {
+ super.setUIProperty(propertyName, value);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/JViewport.java b/libjava/classpath/javax/swing/JViewport.java
new file mode 100644
index 000000000..729fac671
--- /dev/null
+++ b/libjava/classpath/javax/swing/JViewport.java
@@ -0,0 +1,948 @@
+/* JViewport.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.classpath.SystemProperties;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.io.Serializable;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+import javax.accessibility.AccessibleRole;
+import javax.swing.border.Border;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.plaf.ViewportUI;
+
+/**
+ *
+ *
A viewport is, like all swing components, located at some position in
+ * the swing component tree; that location is exactly the same as any other
+ * components: the viewport's "bounds".
+ *
+ *
But in terms of drawing its child, the viewport thinks of itself as
+ * covering a particular position of the view's coordinate space.
+ * For example, the {@link #getViewPosition} method returns
+ * the position (VX,VY) shown above, which is an position in
+ * "view space", even though this is implemented by positioning
+ * the underlying child at position (-VX,-VY)
+ *
+ */
+public class JViewport extends JComponent implements Accessible
+{
+ /**
+ * Provides accessibility support for JViewport.
+ *
+ * @author Roman Kennke (roman@kennke.org)
+ */
+ protected class AccessibleJViewport extends AccessibleJComponent
+ {
+ /**
+ * Creates a new instance of AccessibleJViewport.
+ */
+ protected AccessibleJViewport()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the accessible role of JViewport, which is
+ * {@link AccessibleRole#VIEWPORT}.
+ *
+ * @return the accessible role of JViewport
+ */
+ public AccessibleRole getAccessibleRole()
+ {
+ return AccessibleRole.VIEWPORT;
+ }
+ }
+
+ /**
+ * A {@link java.awt.event.ComponentListener} that listens for
+ * changes of the view's size. This triggers a revalidate() call on the
+ * viewport.
+ */
+ protected class ViewListener extends ComponentAdapter implements Serializable
+ {
+ private static final long serialVersionUID = -2812489404285958070L;
+
+ /**
+ * Creates a new instance of ViewListener.
+ */
+ protected ViewListener()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Receives notification when a component (in this case: the view
+ * component) changes it's size. This simply triggers a revalidate() on the
+ * viewport.
+ *
+ * @param ev the ComponentEvent describing the change
+ */
+ public void componentResized(ComponentEvent ev)
+ {
+ // Fire state change, because resizing the view means changing the
+ // extentSize.
+ fireStateChanged();
+ revalidate();
+ }
+ }
+
+ public static final int SIMPLE_SCROLL_MODE = 0;
+ public static final int BLIT_SCROLL_MODE = 1;
+ public static final int BACKINGSTORE_SCROLL_MODE = 2;
+
+ private static final long serialVersionUID = -6925142919680527970L;
+
+ /**
+ * The default scrollmode to be used by all JViewports as determined by
+ * the system property gnu.javax.swing.JViewport.scrollMode.
+ */
+ private static final int defaultScrollMode;
+
+ protected boolean scrollUnderway;
+ protected boolean isViewSizeSet;
+
+ /**
+ * This flag indicates whether we use a backing store for drawing.
+ *
+ * @deprecated since JDK 1.3
+ */
+ protected boolean backingStore;
+
+ /**
+ * The backingstore image used for the backingstore and blit scroll methods.
+ */
+ protected Image backingStoreImage;
+
+ /**
+ * The position at which the view has been drawn the last time. This is used
+ * to determine the bittable area.
+ */
+ protected Point lastPaintPosition;
+
+ ChangeEvent changeEvent = new ChangeEvent(this);
+
+ int scrollMode;
+
+ /**
+ * The ViewListener instance.
+ */
+ ViewListener viewListener;
+
+ /**
+ * Stores the location from where to blit. This is a cached Point object used
+ * in blitting calculations.
+ */
+ Point cachedBlitFrom;
+
+ /**
+ * Stores the location where to blit to. This is a cached Point object used
+ * in blitting calculations.
+ */
+ Point cachedBlitTo;
+
+ /**
+ * Stores the width of the blitted area. This is a cached Dimension object
+ * used in blitting calculations.
+ */
+ Dimension cachedBlitSize;
+
+ /**
+ * Stores the bounds of the area that needs to be repainted. This is a cached
+ * Rectangle object used in blitting calculations.
+ */
+ Rectangle cachedBlitPaint;
+
+ boolean damaged = true;
+
+ /**
+ * A flag indicating if the size of the viewport has changed since the
+ * last repaint. This is used in double buffered painting to check if we
+ * need a new double buffer, or can reuse the old one.
+ */
+ boolean sizeChanged = true;
+
+ /**
+ * Indicates if this JViewport is the paint root or not. If it is not, then
+ * we may not assume that the offscreen buffer still has the right content
+ * because parent components may have cleared the background already.
+ */
+ private boolean isPaintRoot = false;
+
+ /**
+ * Initializes the default setting for the scrollMode property.
+ */
+ static
+ {
+ String scrollModeProp =
+ SystemProperties.getProperty("gnu.swing.scrollmode", "BACKINGSTORE");
+ if (scrollModeProp.equalsIgnoreCase("simple"))
+ defaultScrollMode = SIMPLE_SCROLL_MODE;
+ else if (scrollModeProp.equalsIgnoreCase("backingstore"))
+ defaultScrollMode = BACKINGSTORE_SCROLL_MODE;
+ else
+ defaultScrollMode = BLIT_SCROLL_MODE;
+ }
+
+ public JViewport()
+ {
+ setOpaque(true);
+ setScrollMode(defaultScrollMode);
+ updateUI();
+ setLayout(createLayoutManager());
+ lastPaintPosition = new Point();
+ cachedBlitFrom = new Point();
+ cachedBlitTo = new Point();
+ cachedBlitSize = new Dimension();
+ cachedBlitPaint = new Rectangle();
+ }
+
+ public Dimension getExtentSize()
+ {
+ return getSize();
+ }
+
+ public Dimension toViewCoordinates(Dimension size)
+ {
+ return size;
+ }
+
+ public Point toViewCoordinates(Point p)
+ {
+ Point pos = getViewPosition();
+ return new Point(p.x + pos.x,
+ p.y + pos.y);
+ }
+
+ public void setExtentSize(Dimension newSize)
+ {
+ Dimension oldExtent = getExtentSize();
+ if (! newSize.equals(oldExtent))
+ {
+ setSize(newSize);
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the viewSize when set, or the preferred size of the set
+ * Component view. If no viewSize and no Component view is set an
+ * empty Dimension is returned.
+ */
+ public Dimension getViewSize()
+ {
+ Dimension size;
+ Component view = getView();
+ if (view != null)
+ {
+ if (isViewSizeSet)
+ size = view.getSize();
+ else
+ size = view.getPreferredSize();
+ }
+ else
+ size = new Dimension(0, 0);
+ return size;
+ }
+
+
+ public void setViewSize(Dimension newSize)
+ {
+ Component view = getView();
+ if (view != null)
+ {
+ if (! newSize.equals(view.getSize()))
+ {
+ scrollUnderway = false;
+ view.setSize(newSize);
+ isViewSizeSet = true;
+ fireStateChanged();
+ }
+ }
+ }
+
+ /**
+ * Get the viewport's position in view space. Despite confusing name,
+ * this really does return the viewport's (0,0) position in view space,
+ * not the view's position.
+ */
+
+ public Point getViewPosition()
+ {
+ Component view = getView();
+ if (view == null)
+ return new Point(0,0);
+ else
+ {
+ Point p = view.getLocation();
+ p.x = -p.x;
+ p.y = -p.y;
+ return p;
+ }
+ }
+
+ public void setViewPosition(Point p)
+ {
+ Component view = getView();
+ if (view != null && ! p.equals(getViewPosition()))
+ {
+ scrollUnderway = true;
+ view.setLocation(-p.x, -p.y);
+ fireStateChanged();
+ }
+ }
+
+ public Rectangle getViewRect()
+ {
+ return new Rectangle(getViewPosition(), getExtentSize());
+ }
+
+ /**
+ * @deprecated 1.4
+ */
+ public boolean isBackingStoreEnabled()
+ {
+ return scrollMode == BACKINGSTORE_SCROLL_MODE;
+ }
+
+ /**
+ * @deprecated 1.4
+ */
+ public void setBackingStoreEnabled(boolean b)
+ {
+ if (b && scrollMode != BACKINGSTORE_SCROLL_MODE)
+ {
+ scrollMode = BACKINGSTORE_SCROLL_MODE;
+ fireStateChanged();
+ }
+ }
+
+ public void setScrollMode(int mode)
+ {
+ scrollMode = mode;
+ fireStateChanged();
+ }
+
+ public int getScrollMode()
+ {
+ return scrollMode;
+ }
+
+ public Component getView()
+ {
+ if (getComponentCount() == 0)
+ return null;
+
+ return getComponents()[0];
+ }
+
+ public void setView(Component v)
+ {
+ Component currView = getView();
+ if (viewListener != null && currView != null)
+ currView.removeComponentListener(viewListener);
+
+ if (v != null)
+ {
+ if (viewListener == null)
+ viewListener = createViewListener();
+ v.addComponentListener(viewListener);
+ add(v);
+ fireStateChanged();
+ }
+ revalidate();
+ repaint();
+ }
+
+ public void reshape(int x, int y, int w, int h)
+ {
+ if (w != getWidth() || h != getHeight())
+ sizeChanged = true;
+ super.reshape(x, y, w, h);
+ if (sizeChanged)
+ {
+ damaged = true;
+ fireStateChanged();
+ }
+ }
+
+ public final Insets getInsets()
+ {
+ return new Insets(0, 0, 0, 0);
+ }
+
+ public final Insets getInsets(Insets insets)
+ {
+ if (insets == null)
+ return getInsets();
+ insets.top = 0;
+ insets.bottom = 0;
+ insets.left = 0;
+ insets.right = 0;
+ return insets;
+ }
+
+
+ /**
+ * Overridden to return false, so the JViewport's paint method
+ * gets called instead of directly calling the children. This is necessary
+ * in order to get a useful clipping and translation on the children.
+ *
+ * @return false
+ */
+ public boolean isOptimizedDrawingEnabled()
+ {
+ return false;
+ }
+
+ public void paint(Graphics g)
+ {
+ Component view = getView();
+
+ if (view == null)
+ return;
+
+ Rectangle viewBounds = view.getBounds();
+ Rectangle portBounds = getBounds();
+
+ if (viewBounds.width == 0
+ || viewBounds.height == 0
+ || portBounds.width == 0
+ || portBounds.height == 0)
+ return;
+
+ switch (getScrollMode())
+ {
+
+ case JViewport.BACKINGSTORE_SCROLL_MODE:
+ paintBackingStore(g);
+ break;
+ case JViewport.BLIT_SCROLL_MODE:
+ paintBlit(g);
+ break;
+ case JViewport.SIMPLE_SCROLL_MODE:
+ default:
+ paintSimple(g);
+ break;
+ }
+ damaged = false;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) getListeners(ChangeListener.class);
+ }
+
+ /**
+ * This method returns the String ID of the UI class of Separator.
+ *
+ * @return The UI class' String ID.
+ */
+ public String getUIClassID()
+ {
+ return "ViewportUI";
+ }
+
+ /**
+ * This method resets the UI used to the Look and Feel defaults..
+ */
+ public void updateUI()
+ {
+ setUI((ViewportUI) UIManager.getUI(this));
+ }
+
+ /**
+ * This method returns the viewport's UI delegate.
+ *
+ * @return The viewport's UI delegate.
+ */
+ public ViewportUI getUI()
+ {
+ return (ViewportUI) ui;
+ }
+
+ /**
+ * This method sets the viewport's UI delegate.
+ *
+ * @param ui The viewport's UI delegate.
+ */
+ public void setUI(ViewportUI ui)
+ {
+ super.setUI(ui);
+ }
+
+ public final void setBorder(Border border)
+ {
+ if (border != null)
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Scrolls the view so that contentRect becomes visible.
+ *
+ * @param contentRect the rectangle to make visible within the view
+ */
+ public void scrollRectToVisible(Rectangle contentRect)
+ {
+ Component view = getView();
+ if (view == null)
+ return;
+
+ Point pos = getViewPosition();
+ // We get the contentRect in the viewport coordinates. But we want to
+ // calculate with view coordinates.
+ int contentX = contentRect.x + pos.x;
+ int contentY = contentRect.y + pos.y;
+ Rectangle viewBounds = getView().getBounds();
+ Rectangle portBounds = getBounds();
+
+ if (isShowing())
+ getView().validate();
+
+ // If the bottom boundary of contentRect is below the port
+ // boundaries, scroll up as necessary.
+ if (contentY + contentRect.height + viewBounds.y > portBounds.height)
+ pos.y = contentY + contentRect.height - portBounds.height;
+ // If contentY is above the port boundaries, scroll down to
+ // contentY.
+ if (contentY + viewBounds.y < 0)
+ pos.y = contentY;
+ // If the right boundary of contentRect is right from the port
+ // boundaries, scroll left as necessary.
+ if (contentX + contentRect.width + viewBounds.x > portBounds.width)
+ pos.x = contentX + contentRect.width - portBounds.width;
+ // If contentX is left from the port boundaries, scroll right to
+ // contentRect.x.
+ if (contentX + viewBounds.x < 0)
+ pos.x = contentX;
+ setViewPosition(pos);
+ }
+
+ /**
+ * Returns the accessible context for this JViewport. This
+ * will be an instance of {@link AccessibleJViewport}.
+ *
+ * @return the accessible context for this JViewport
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJViewport();
+ return accessibleContext;
+ }
+
+ /**
+ * Forward repaint to parent to make sure only one paint is performed by the
+ * RepaintManager.
+ *
+ * @param tm number of milliseconds to defer the repaint request
+ * @param x the X coordinate of the upper left corner of the dirty area
+ * @param y the Y coordinate of the upper left corner of the dirty area
+ * @param w the width of the dirty area
+ * @param h the height of the dirty area
+ */
+ public void repaint(long tm, int x, int y, int w, int h)
+ {
+ Component parent = getParent();
+ if (parent != null)
+ parent.repaint(tm, x + getX(), y + getY(), w, h);
+ else
+ super.repaint(tm, x, y, w, h);
+ }
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ if (getComponentCount() > 0)
+ remove(getComponents()[0]);
+
+ super.addImpl(comp, constraints, index);
+ }
+
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+ for (int i = 0; i < listeners.length; ++i)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Creates a {@link ViewListener} that is supposed to listen for
+ * size changes on the view component.
+ *
+ * @return a ViewListener instance
+ */
+ protected ViewListener createViewListener()
+ {
+ return new ViewListener();
+ }
+
+ /**
+ * Creates the LayoutManager that is used for this viewport. Override
+ * this method if you want to use a custom LayoutManager.
+ *
+ * @return a LayoutManager to use for this viewport
+ */
+ protected LayoutManager createLayoutManager()
+ {
+ return new ViewportLayout();
+ }
+
+ /**
+ * Computes the parameters for the blitting scroll method. dx
+ * and dy specifiy the X and Y offset by which the viewport
+ * is scrolled. All other arguments are output parameters and are filled by
+ * this method.
+ *
+ * blitFrom holds the position of the blit rectangle in the
+ * viewport rectangle before scrolling, blitTo where the blitArea
+ * is copied to.
+ *
+ * blitSize holds the size of the blit area and
+ * blitPaint is the area of the view that needs to be painted.
+ *
+ * This method returns true if blitting is possible and
+ * false if the viewport has to be repainted completetly without
+ * blitting.
+ *
+ * @param dx the horizontal delta
+ * @param dy the vertical delta
+ * @param blitFrom the position from where to blit; set by this method
+ * @param blitTo the position where to blit area is copied to; set by this
+ * method
+ * @param blitSize the size of the blitted area; set by this method
+ * @param blitPaint the area that needs repainting; set by this method
+ *
+ * @return true if blitting is possible,
+ * false otherwise
+ */
+ protected boolean computeBlit(int dx, int dy, Point blitFrom, Point blitTo,
+ Dimension blitSize, Rectangle blitPaint)
+ {
+ if ((dx != 0 && dy != 0) || (dy == 0 && dy == 0) || damaged)
+ // We cannot blit if the viewport is scrolled in both directions at
+ // once. Also, we do not want to blit if the viewport is not scrolled at
+ // all, because that probably means the view component repaints itself
+ // and the buffer needs updating.
+ return false;
+
+ Rectangle portBounds = SwingUtilities.calculateInnerArea(this, getBounds());
+
+ // Compute the blitFrom and blitTo parameters.
+ blitFrom.x = portBounds.x;
+ blitFrom.y = portBounds.y;
+ blitTo.x = portBounds.x;
+ blitTo.y = portBounds.y;
+
+ if (dy > 0)
+ {
+ blitFrom.y = portBounds.y + dy;
+ }
+ else if (dy < 0)
+ {
+ blitTo.y = portBounds.y - dy;
+ }
+ else if (dx > 0)
+ {
+ blitFrom.x = portBounds.x + dx;
+ }
+ else if (dx < 0)
+ {
+ blitTo.x = portBounds.x - dx;
+ }
+
+ // Compute size of the blit area.
+ if (dx != 0)
+ {
+ blitSize.width = portBounds.width - Math.abs(dx);
+ blitSize.height = portBounds.height;
+ }
+ else if (dy != 0)
+ {
+ blitSize.width = portBounds.width;
+ blitSize.height = portBounds.height - Math.abs(dy);
+ }
+
+ // Compute the blitPaint parameter.
+ blitPaint.setBounds(portBounds);
+ if (dy > 0)
+ {
+ blitPaint.y = portBounds.y + portBounds.height - dy;
+ blitPaint.height = dy;
+ }
+ else if (dy < 0)
+ {
+ blitPaint.height = -dy;
+ }
+ if (dx > 0)
+ {
+ blitPaint.x = portBounds.x + portBounds.width - dx;
+ blitPaint.width = dx;
+ }
+ else if (dx < 0)
+ {
+ blitPaint.width = -dx;
+ }
+
+ return true;
+ }
+
+ /**
+ * Paints the viewport in case we have a scrollmode of
+ * {@link #SIMPLE_SCROLL_MODE}.
+ *
+ * This simply paints the view directly on the surface of the viewport.
+ *
+ * @param g the graphics context to use
+ */
+ void paintSimple(Graphics g)
+ {
+ // We need to call this to properly clear the background.
+ paintComponent(g);
+
+ Point pos = getViewPosition();
+ Component view = getView();
+ Shape oldClip = g.getClip();
+ g.clipRect(0, 0, getWidth(), getHeight());
+ boolean translated = false;
+ try
+ {
+ g.translate(-pos.x, -pos.y);
+ translated = true;
+ view.paint(g);
+ }
+ finally
+ {
+ if (translated)
+ g.translate (pos.x, pos.y);
+ g.setClip(oldClip);
+ }
+ }
+
+ /**
+ * Paints the viewport in case we have a scroll mode of
+ * {@link #BACKINGSTORE_SCROLL_MODE}.
+ *
+ * This method uses a backing store image to paint the view to, which is then
+ * subsequently painted on the screen. This should make scrolling more
+ * smooth.
+ *
+ * @param g the graphics context to use
+ */
+ void paintBackingStore(Graphics g)
+ {
+ // If we have no backing store image yet or the size of the component has
+ // changed, we need to rebuild the backing store.
+ if (backingStoreImage == null || sizeChanged)
+ {
+ backingStoreImage = createImage(getWidth(), getHeight());
+ sizeChanged = false;
+ Graphics g2 = backingStoreImage.getGraphics();
+ paintSimple(g2);
+ g2.dispose();
+ }
+ // Otherwise we can perform the blitting on the backing store image:
+ // First we move the part that remains visible after scrolling, then
+ // we only need to paint the bit that becomes newly visible.
+ else
+ {
+ Graphics g2 = backingStoreImage.getGraphics();
+ Point viewPosition = getViewPosition();
+ int dx = viewPosition.x - lastPaintPosition.x;
+ int dy = viewPosition.y - lastPaintPosition.y;
+ boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
+ cachedBlitSize, cachedBlitPaint);
+ if (canBlit && isPaintRoot)
+ {
+ // Copy the part that remains visible during scrolling.
+ if (cachedBlitSize.width > 0 && cachedBlitSize.height > 0)
+ {
+ g2.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
+ cachedBlitSize.width, cachedBlitSize.height,
+ cachedBlitTo.x - cachedBlitFrom.x,
+ cachedBlitTo.y - cachedBlitFrom.y);
+ }
+ // Now paint the part that becomes newly visible.
+ g2.setClip(cachedBlitPaint.x, cachedBlitPaint.y,
+ cachedBlitPaint.width, cachedBlitPaint.height);
+ paintSimple(g2);
+ }
+ // If blitting is not possible for some reason, fall back to repainting
+ // everything.
+ else
+ {
+ // If the image has not been scrolled at all, only the changed
+ // clip must be updated in the buffer.
+ if (dx == 0 && dy == 0)
+ g2.setClip(g.getClip());
+
+ paintSimple(g2);
+ }
+ g2.dispose();
+ }
+ // Actually draw the backingstore image to the graphics context.
+ g.drawImage(backingStoreImage, 0, 0, this);
+ // Update the lastPaintPosition so that we know what is already drawn when
+ // we paint the next time.
+ lastPaintPosition.setLocation(getViewPosition());
+ }
+
+ /**
+ * Paints the viewport in case we have a scrollmode of
+ * {@link #BLIT_SCROLL_MODE}.
+ *
+ * This paints the viewport using a backingstore and a blitting algorithm.
+ * Only the newly exposed area of the view is painted from the view painting
+ * methods, the remainder is copied from the backing store.
+ *
+ * @param g the graphics context to use
+ */
+ void paintBlit(Graphics g)
+ {
+ // First we move the part that remains visible after scrolling, then
+ // we only need to paint the bit that becomes newly visible.
+ Point viewPosition = getViewPosition();
+ int dx = viewPosition.x - lastPaintPosition.x;
+ int dy = viewPosition.y - lastPaintPosition.y;
+ boolean canBlit = computeBlit(dx, dy, cachedBlitFrom, cachedBlitTo,
+ cachedBlitSize, cachedBlitPaint);
+ if (canBlit && isPaintRoot)
+ {
+ // Copy the part that remains visible during scrolling.
+ if (cachedBlitSize.width > 0 && cachedBlitSize.width > 0)
+ {
+ g.copyArea(cachedBlitFrom.x, cachedBlitFrom.y,
+ cachedBlitSize.width, cachedBlitSize.height,
+ cachedBlitTo.x - cachedBlitFrom.x,
+ cachedBlitTo.y - cachedBlitFrom.y);
+ }
+ // Now paint the part that becomes newly visible.
+ Shape oldClip = g.getClip();
+ g.clipRect(cachedBlitPaint.x, cachedBlitPaint.y,
+ cachedBlitPaint.width, cachedBlitPaint.height);
+ try
+ {
+ paintSimple(g);
+ }
+ finally
+ {
+ g.setClip(oldClip);
+ }
+ }
+ // If blitting is not possible for some reason, fall back to repainting
+ // everything.
+ else
+ paintSimple(g);
+ lastPaintPosition.setLocation(getViewPosition());
+ }
+
+ /**
+ * Overridden from JComponent to set the {@link #isPaintRoot} flag.
+ *
+ * @param x the rectangle to paint, X coordinate
+ * @param y the rectangle to paint, Y coordinate
+ * @param w the rectangle to paint, width
+ * @param h the rectangle to paint, height
+ */
+ void paintImmediately2(int x, int y, int w, int h)
+ {
+ isPaintRoot = true;
+ super.paintImmediately2(x, y, w, h);
+ isPaintRoot = false;
+ }
+
+ /**
+ * Returns true when the JViewport is using a backbuffer, so that we
+ * can update our backbuffer correctly.
+ */
+ boolean isPaintRoot()
+ {
+ return scrollMode == BACKINGSTORE_SCROLL_MODE;
+ }
+}
diff --git a/libjava/classpath/javax/swing/JWindow.java b/libjava/classpath/javax/swing/JWindow.java
new file mode 100644
index 000000000..91e0b5069
--- /dev/null
+++ b/libjava/classpath/javax/swing/JWindow.java
@@ -0,0 +1,290 @@
+/* JWindow.java --
+ Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTEvent;
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.GraphicsConfiguration;
+import java.awt.LayoutManager;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+
+/**
+ * Unlike JComponent derivatives, JWindow inherits from
+ * java.awt.Window. But also lets a look-and-feel component to its work.
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class JWindow extends Window implements Accessible, RootPaneContainer
+{
+ /**
+ * Provides accessibility support for JWindow.
+ */
+ protected class AccessibleJWindow extends Window.AccessibleAWTWindow
+ {
+ /**
+ * Creates a new instance of AccessibleJWindow.
+ */
+ protected AccessibleJWindow()
+ {
+ super();
+ // Nothing to do here.
+ }
+ }
+
+ private static final long serialVersionUID = 5420698392125238833L;
+
+ protected JRootPane rootPane;
+
+ /**
+ * @specnote rootPaneCheckingEnabled is false to comply with J2SE 5.0
+ */
+ protected boolean rootPaneCheckingEnabled = false;
+
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * Creates a new JWindow that has a shared invisible owner frame
+ * as its parent.
+ */
+ public JWindow()
+ {
+ super(SwingUtilities.getOwnerFrame(null));
+ windowInit();
+ }
+
+ /**
+ * Creates a new JWindow that uses the specified graphics
+ * environment. This can be used to open a window on a different screen for
+ * example.
+ *
+ * @param gc the graphics environment to use
+ */
+ public JWindow(GraphicsConfiguration gc)
+ {
+ super(SwingUtilities.getOwnerFrame(null), gc);
+ windowInit();
+ }
+
+ /**
+ * Creates a new JWindow that has the specified
+ * owner frame. If owner is null, then
+ * an invisible shared owner frame is installed as owner frame.
+ *
+ * @param owner the owner frame of this window; if null a shared
+ * invisible owner frame is used
+ */
+ public JWindow(Frame owner)
+ {
+ super(SwingUtilities.getOwnerFrame(owner));
+ windowInit();
+ }
+
+ /**
+ * Creates a new JWindow that has the specified
+ * owner window. If owner is null,
+ * then an invisible shared owner frame is installed as owner frame.
+ *
+ * @param owner the owner window of this window; if null a
+ * shared invisible owner frame is used
+ */
+ public JWindow(Window owner)
+ {
+ super(SwingUtilities.getOwnerFrame(owner));
+ windowInit();
+ }
+
+ /**
+ * Creates a new JWindow for the given graphics configuration
+ * and that has the specified owner window. If
+ * owner is null, then an invisible shared owner
+ * frame is installed as owner frame.
+ *
+ * The gc parameter can be used to open the window on a
+ * different screen for example.
+ *
+ * @param owner the owner window of this window; if null a
+ * shared invisible owner frame is used
+ * @param gc the graphics configuration to use
+ */
+ public JWindow(Window owner, GraphicsConfiguration gc)
+ {
+ super(SwingUtilities.getOwnerFrame(owner), gc);
+ windowInit();
+ }
+
+ protected void windowInit()
+ {
+ // We need to explicitly enable events here so that our processKeyEvent()
+ // and processWindowEvent() gets called.
+ enableEvents(AWTEvent.KEY_EVENT_MASK);
+
+ super.setLayout(new BorderLayout(1, 1));
+ getRootPane(); // will do set/create
+ // Now we're done init stage, adds and layouts go to content pane.
+ setRootPaneCheckingEnabled(true);
+ }
+
+ public Dimension getPreferredSize()
+ {
+ return super.getPreferredSize();
+ }
+
+ public void setLayout(LayoutManager manager)
+ {
+ // Check if we're in initialization stage. If so, call super.setLayout
+ // otherwise, valid calls go to the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().setLayout(manager);
+ else
+ super.setLayout(manager);
+ }
+
+ public void setLayeredPane(JLayeredPane layeredPane)
+ {
+ getRootPane().setLayeredPane(layeredPane);
+ }
+
+ public JLayeredPane getLayeredPane()
+ {
+ return getRootPane().getLayeredPane();
+ }
+
+ public JRootPane getRootPane()
+ {
+ if (rootPane == null)
+ setRootPane(createRootPane());
+ return rootPane;
+ }
+
+ protected void setRootPane(JRootPane root)
+ {
+ if (rootPane != null)
+ remove(rootPane);
+
+ rootPane = root;
+ add(rootPane, BorderLayout.CENTER);
+ }
+
+ protected JRootPane createRootPane()
+ {
+ return new JRootPane();
+ }
+
+ public Container getContentPane()
+ {
+ return getRootPane().getContentPane();
+ }
+
+ public void setContentPane(Container contentPane)
+ {
+ getRootPane().setContentPane(contentPane);
+ }
+
+ public Component getGlassPane()
+ {
+ return getRootPane().getGlassPane();
+ }
+
+ public void setGlassPane(Component glassPane)
+ {
+ getRootPane().setGlassPane(glassPane);
+ }
+
+
+ protected void addImpl(Component comp, Object constraints, int index)
+ {
+ // If we're adding in the initialization stage use super.add.
+ // otherwise pass the add onto the content pane.
+ if (isRootPaneCheckingEnabled())
+ getContentPane().add(comp, constraints, index);
+ else
+ super.addImpl(comp, constraints, index);
+ }
+
+ public void remove(Component comp)
+ {
+ // If we're removing the root pane, use super.remove. Otherwise
+ // pass it on to the content pane instead.
+ if (comp == rootPane)
+ super.remove(rootPane);
+ else
+ getContentPane().remove(comp);
+ }
+
+ protected boolean isRootPaneCheckingEnabled()
+ {
+ return rootPaneCheckingEnabled;
+ }
+
+ protected void setRootPaneCheckingEnabled(boolean enabled)
+ {
+ rootPaneCheckingEnabled = enabled;
+ }
+
+ public void update(Graphics g)
+ {
+ paint(g);
+ }
+
+ protected void processKeyEvent(KeyEvent e)
+ {
+ super.processKeyEvent(e);
+ }
+
+ public AccessibleContext getAccessibleContext()
+ {
+ if (accessibleContext == null)
+ accessibleContext = new AccessibleJWindow();
+ return accessibleContext;
+ }
+
+ protected String paramString()
+ {
+ return "JWindow";
+ }
+}
diff --git a/libjava/classpath/javax/swing/KeyStroke.java b/libjava/classpath/javax/swing/KeyStroke.java
new file mode 100644
index 000000000..94bc334b0
--- /dev/null
+++ b/libjava/classpath/javax/swing/KeyStroke.java
@@ -0,0 +1,123 @@
+/* KeyStroke.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTKeyStroke;
+import java.awt.event.KeyEvent;
+import java.io.Serializable;
+
+public class KeyStroke
+ extends AWTKeyStroke
+ implements Serializable
+{
+ private static final long serialVersionUID = -9060180771037902530L;
+
+ // Called by java.awt.AWTKeyStroke.registerSubclass via reflection.
+ private KeyStroke()
+ {
+ // Nothing to do here.
+ }
+
+ private KeyStroke(char keyChar, int keyCode, int modifiers,
+ boolean onKeyRelease)
+ {
+ super(keyChar, keyCode, modifiers, onKeyRelease);
+ }
+
+ static
+ {
+ AWTKeyStroke.registerSubclass(KeyStroke.class);
+ }
+
+ public static KeyStroke getKeyStroke(char keyChar)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyChar);
+ }
+
+ /**
+ * @deprecated Use {@link #getKeyStroke(char)}
+ *
+ * This method, unlike all the other factory methods on this object,
+ * returns a non-cached, non-shared object. New code should not use it.
+ */
+ public static KeyStroke getKeyStroke(char keyChar, boolean onKeyRelease)
+ {
+ return new KeyStroke(keyChar, KeyEvent.VK_UNDEFINED, 0, onKeyRelease);
+ }
+
+ public static KeyStroke getKeyStroke(Character keyChar, int modifiers)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyChar, modifiers);
+ }
+
+ public static KeyStroke getKeyStroke(int keyCode, int modifiers,
+ boolean onKeyRelease)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyCode, modifiers, onKeyRelease);
+ }
+
+ public static KeyStroke getKeyStroke(int keyCode, int modifiers)
+ {
+ return (KeyStroke) getAWTKeyStroke(keyCode, modifiers);
+ }
+
+ /**
+ * Returns the KeyStroke according to getAWTKeyStroke().
+ * But it returns null instead of throwing
+ * IllegalArugmentException when
+ * the keystoke sequence cannot be parsed from the given string.
+ */
+ public static KeyStroke getKeyStroke(String str)
+ {
+ try
+ {
+ return (KeyStroke) getAWTKeyStroke(str);
+ }
+ catch (IllegalArgumentException iae)
+ {
+ return null;
+ }
+ }
+
+ public static KeyStroke getKeyStrokeForEvent(KeyEvent event)
+ {
+ return (KeyStroke) getAWTKeyStrokeForEvent(event);
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/KeyboardManager.java b/libjava/classpath/javax/swing/KeyboardManager.java
new file mode 100644
index 000000000..5c1c09eab
--- /dev/null
+++ b/libjava/classpath/javax/swing/KeyboardManager.java
@@ -0,0 +1,281 @@
+/* KeyboardManager.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.applet.Applet;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Window;
+import java.awt.event.KeyEvent;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.WeakHashMap;
+
+/**
+ * This class maintains a mapping from top-level containers to a
+ * Hashtable. The Hashtable maps KeyStrokes to Components to be used when
+ * Components register keyboard actions with the condition
+ * JComponent.WHEN_IN_FOCUSED_WINDOW.
+ *
+ * @author Anthony Balkissoon abalkiss at redhat dot com
+ *
+ */
+class KeyboardManager
+{
+ /** Shared instance of KeyboardManager **/
+ static KeyboardManager manager = new KeyboardManager();
+
+ /**
+ * A mapping between top level containers and Hashtables that
+ * map KeyStrokes to Components.
+ */
+ WeakHashMap topLevelLookup = new WeakHashMap();
+
+ /**
+ * A mapping between top level containers and Vectors of JMenuBars
+ * used to allow all the JMenuBars within a top level container
+ * a chance to consume key events.
+ */
+ Hashtable menuBarLookup = new Hashtable();
+ /**
+ * Returns the shared instance of KeyboardManager.
+ * @return the shared instance of KeybaordManager.
+ */
+ public static KeyboardManager getManager()
+ {
+ return manager;
+ }
+ /**
+ * Returns the top-level ancestor for the given JComponent.
+ * @param c the JComponent whose top-level ancestor we want
+ * @return the top-level ancestor for the given JComponent.
+ */
+ static Container findTopLevel (Component c)
+ {
+ Container topLevel = (c instanceof Container) ? (Container) c
+ : c.getParent();
+ while (topLevel != null &&
+ !(topLevel instanceof Window) &&
+ !(topLevel instanceof Applet) &&
+ !(topLevel instanceof JInternalFrame))
+ topLevel = topLevel.getParent();
+ return topLevel;
+ }
+
+ /**
+ * Returns the Hashtable that maps KeyStrokes to Components, for
+ * the specified top-level container c. If no Hashtable exists
+ * we create and register it here and return the newly created
+ * Hashtable.
+ *
+ * @param c the top-level container whose Hashtable we want
+ * @return the Hashtable mapping KeyStrokes to Components for the
+ * specified top-level container
+ */
+ Hashtable getHashtableForTopLevel (Container c)
+ {
+ Hashtable keyToComponent = (Hashtable)topLevelLookup.get(c);
+ if (keyToComponent == null)
+ {
+ keyToComponent = new Hashtable();
+ topLevelLookup.put(c, keyToComponent);
+ }
+ return keyToComponent;
+ }
+
+ /**
+ * Registers a KeyStroke with a Component. This does not register
+ * the KeyStroke to a specific Action. When searching for a
+ * WHEN_IN_FOCUSED_WINDOW binding we will first go up to the focused
+ * top-level Container, then get the Hashtable that maps KeyStrokes
+ * to components for that particular top-level Container, then
+ * call processKeyBindings on that component with the condition
+ * JComponent.WHEN_IN_FOCUSED_WINDOW.
+ * @param comp the JComponent associated with the KeyStroke
+ * @param key the KeyStroke
+ */
+ public void registerBinding(JComponent comp, KeyStroke key)
+ {
+ // This method associates a KeyStroke with a particular JComponent
+ // When the KeyStroke occurs, if this component's top-level ancestor
+ // has focus (one of its children is the focused Component) then
+ // comp.processKeyBindings will be called with condition
+ // JComponent.WHEN_IN_FOCUSED_WINDOW.
+
+ // Look for the JComponent's top-level parent and return if it is null
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+
+ // Now get the Hashtable for this top-level container
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+
+ // And add the new binding to this Hashtable
+ // FIXME: should allow more than one JComponent to be associated
+ // with a KeyStroke, in case one of them is disabled
+ keyToComponent.put(key, comp);
+ }
+
+ public void clearBindingsForComp(JComponent comp)
+ {
+ // This method clears all the WHEN_IN_FOCUSED_WINDOW bindings associated
+ // with comp. This is used for a terribly ineffcient
+ // strategy in which JComponent.updateComponentInputMap simply clears
+ // all bindings associated with its component and then reloads all the
+ // bindings from the updated ComponentInputMap. This is only a preliminary
+ // strategy and should be improved upon once the WHEN_IN_FOCUSED_WINDOW
+ // bindings work.
+
+ // Find the top-level ancestor
+
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+ // And now get its Hashtable
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+
+ Enumeration keys = keyToComponent.keys();
+ Object temp;
+
+ // Iterate through the keys and remove any key whose value is comp
+ while (keys.hasMoreElements())
+ {
+ temp = keys.nextElement();
+ if (comp == (JComponent)keyToComponent.get(temp))
+ keyToComponent.remove(temp);
+ }
+ }
+
+ /**
+ * This method registers all the bindings in the given ComponentInputMap.
+ * Rather than call registerBinding on all the keys, we do the work here
+ * so that we don't duplicate finding the top-level container and
+ * getting its Hashtable.
+ *
+ * @param map the ComponentInputMap whose bindings we want to register
+ */
+ public void registerEntireMap (ComponentInputMap map)
+ {
+ if (map == null)
+ return;
+ JComponent comp = map.getComponent();
+ KeyStroke[] keys = map.allKeys();
+ if (keys == null)
+ return;
+ // Find the top-level container associated with this ComponentInputMap
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return;
+
+ // Register the KeyStrokes in the top-level container's Hashtable
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+ for (int i = 0; i < keys.length; i++)
+ keyToComponent.put(keys[i], comp);
+ }
+
+ public boolean processKeyStroke (Component comp, KeyStroke key, KeyEvent e)
+ {
+ boolean pressed = e.getID() == KeyEvent.KEY_PRESSED;
+
+ // Look for the top-level ancestor
+ Container topLevel = findTopLevel(comp);
+ if (topLevel == null)
+ return false;
+ // Now get the Hashtable for that top-level container
+ Hashtable keyToComponent = getHashtableForTopLevel(topLevel);
+ Enumeration keys = keyToComponent.keys();
+ JComponent target = (JComponent)keyToComponent.get(key);
+ if (target != null && target.processKeyBinding
+ (key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
+ return true;
+
+ // Have to give all the JMenuBars a chance to consume the event
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ for (int i = 0; i < menuBars.size(); i++)
+ if (((JMenuBar)menuBars.elementAt(i)).processKeyBinding(key, e, JComponent.WHEN_IN_FOCUSED_WINDOW, pressed))
+ return true;
+ return false;
+ }
+
+ /**
+ * Returns the Vector of JMenuBars associated with the top-level
+ * @param c the top-level container whose JMenuBar Vector we want
+ * @return the Vector of JMenuBars for this top level container
+ */
+ Vector getVectorForTopLevel(Container c)
+ {
+ Vector result = (Vector) menuBarLookup.get(c);
+ if (result == null)
+ {
+ result = new Vector();
+ menuBarLookup.put (c, result);
+ }
+ return result;
+ }
+
+ /**
+ * In processKeyStroke, KeyManager must give all JMenuBars in the
+ * focused top-level container a chance to process the event. So,
+ * JMenuBars must be registered in KeyManager and associated with a
+ * top-level container. That's what this method is for.
+ * @param menuBar the JMenuBar to register
+ */
+ public void registerJMenuBar (JMenuBar menuBar)
+ {
+ Container topLevel = findTopLevel(menuBar);
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ if (!menuBars.contains(menuBar))
+ menuBars.add(menuBar);
+ }
+
+ /**
+ * Unregisters a JMenuBar from its top-level container. This is
+ * called before the JMenuBar is actually removed from the container
+ * so findTopLevel will still find us the correct top-level container.
+ * @param menuBar the JMenuBar to unregister.
+ */
+ public void unregisterJMenuBar (JMenuBar menuBar)
+ {
+ Container topLevel = findTopLevel(menuBar);
+ Vector menuBars = getVectorForTopLevel(topLevel);
+ if (menuBars.contains(menuBar))
+ menuBars.remove(menuBar);
+ }
+}
diff --git a/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java b/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java
new file mode 100644
index 000000000..335bc265d
--- /dev/null
+++ b/libjava/classpath/javax/swing/LayoutFocusTraversalPolicy.java
@@ -0,0 +1,89 @@
+/* LayoutFocusTraversalPolicy.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.io.Serializable;
+import java.util.Comparator;
+
+/**
+ * @author Graydon Hoare
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public class LayoutFocusTraversalPolicy
+ extends SortingFocusTraversalPolicy
+ implements Serializable
+{
+ private static class LayoutComparator
+ implements Comparator
+ {
+ public LayoutComparator()
+ {
+ // Do nothing here.
+ }
+
+ public int compare(Object o1, Object o2)
+ {
+ Component comp1 = (Component) o1;
+ Component comp2 = (Component) o2;
+
+ int x1 = comp1.getX();
+ int y1 = comp1.getY();
+ int x2 = comp2.getX();
+ int y2 = comp2.getY();
+
+ if (x1 == x2 && y1 == y2)
+ return 0;
+
+ if ((y1 < y2) || ((y1 == y2) && (x1 < x2)))
+ return -1;
+
+ return 1;
+ }
+ }
+
+ private static final long serialVersionUID = 4312146927238881442L;
+
+ public LayoutFocusTraversalPolicy()
+ {
+ super(new LayoutComparator());
+ }
+}
diff --git a/libjava/classpath/javax/swing/ListCellRenderer.java b/libjava/classpath/javax/swing/ListCellRenderer.java
new file mode 100644
index 000000000..0e56d5016
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListCellRenderer.java
@@ -0,0 +1,50 @@
+/* ListCellRenderer.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+
+/**
+ * Renders the cells of a {@link JList}.
+ */
+public interface ListCellRenderer
+{
+ Component getListCellRendererComponent(JList list, Object value, int index,
+ boolean isSelected,
+ boolean cellHasFocus);
+}
diff --git a/libjava/classpath/javax/swing/ListModel.java b/libjava/classpath/javax/swing/ListModel.java
new file mode 100644
index 000000000..736627e85
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListModel.java
@@ -0,0 +1,80 @@
+/* ListModel.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ListDataListener;
+
+/**
+ * The data model that is typically used in {@link JList}.
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ */
+public interface ListModel
+{
+ /**
+ * Return the number of data elements in the list.
+ *
+ * @return The number of data elements in the list
+ */
+ int getSize();
+
+ /**
+ * Retrieves a data element at a specified index.
+ *
+ * @param index The index of the element to retrieve
+ *
+ * @return The data element at the specified index
+ */
+ Object getElementAt(int index);
+
+ /**
+ * Add a listener object to this model. The listener will be called
+ * any time the set of elements in the model is changed.
+ *
+ * @param l The listener to add
+ */
+ void addListDataListener(ListDataListener l);
+
+ /**
+ * Add a listener object to this model. The listener will no longer be
+ * called when the set of elements in the model is changed.
+ *
+ * @param l The listener to remove
+ */
+ void removeListDataListener(ListDataListener l);
+}
diff --git a/libjava/classpath/javax/swing/ListSelectionModel.java b/libjava/classpath/javax/swing/ListSelectionModel.java
new file mode 100644
index 000000000..1cd67e948
--- /dev/null
+++ b/libjava/classpath/javax/swing/ListSelectionModel.java
@@ -0,0 +1,332 @@
+/* ListSelectionModel.java --
+ Copyright (C) 2002, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+/**
+ * A model that tracks the selection status of a list of items. Each item in
+ * the list is identified by a zero-based index only, so the model can be used
+ * to track the selection status of any type of list. The model
+ * supports three modes:
+ *
+ *
SINGLE_SELECTION - only one item in the list may be
+ * selected;
+ *
SINGLE_INTERVAL_SELECTION - only one interval in the list
+ * may be selected;
+ *
MULTIPLE_INTERVAL_SELECTION - any combination of items in
+ * the list may be selected.
+ *
+ * The model uses an event notification mechanism to notify listeners (see
+ * {@link ListSelectionListener}) about updates to the selection model.
+ *
+ * This model is used to track row selections in the {@link JList} component,
+ * and row and column selections in the {@link JTable} component.
+ */
+public interface ListSelectionModel
+{
+
+ /**
+ * A selection mode in which only one item can be selected.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int SINGLE_SELECTION = 0;
+
+ /**
+ * A selection mode in which a single interval can be selected (an interval
+ * is a range containing one or more contiguous items).
+ *
+ * @see #setSelectionMode(int)
+ */
+ int SINGLE_INTERVAL_SELECTION = 1;
+
+ /**
+ * A selection mode in which any combination of items can be selected.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int MULTIPLE_INTERVAL_SELECTION = 2;
+
+ /**
+ * Sets the selection mode.
+ *
+ * FIXME: The spec is silent about what happens to existing selections, for
+ * example when changing from an interval selection to single selection.
+ *
+ * @param mode one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @see #getSelectionMode()
+ *
+ * @throws IllegalArgumentException if mode is not one of the
+ * specified values.
+ */
+ void setSelectionMode(int mode);
+
+ /**
+ * Returns the selection mode, which is one of {@link #SINGLE_SELECTION},
+ * {@link #SINGLE_INTERVAL_SELECTION} and
+ * {@link #MULTIPLE_INTERVAL_SELECTION}.
+ *
+ * @return The selection mode.
+ *
+ * @see #setSelectionMode(int)
+ */
+ int getSelectionMode();
+
+ /**
+ * Clears the current selection from the model. If the selection state
+ * changes (that is, the existing selection is non-empty) a
+ * {@link ListSelectionEvent} should be sent to all registered listeners.
+ *
+ * FIXME: what happens to the anchor and lead selection indices (the spec
+ * is silent about this)? See:
+ *
+ * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4334792
+ */
+ void clearSelection();
+
+ /**
+ * Returns the lowest selected index, or -1 if there is no
+ * selection.
+ *
+ * @return The lowest selected index.
+ *
+ * @see #getMaxSelectionIndex()
+ */
+ int getMinSelectionIndex();
+
+ /**
+ * Returns the highest selected index, or -1 if there is no
+ * selection.
+ *
+ * @return The highest selected index.
+ *
+ * @see #getMinSelectionIndex()
+ */
+ int getMaxSelectionIndex();
+
+ /**
+ * Returns true if the specified item is selected, and
+ * false otherwise. Special note: if index is
+ * negative, this method should return false (no exception
+ * should be thrown).
+ *
+ * @param index the item index (zero-based).
+ *
+ * @return true if the specified item is selected, and
+ * false otherwise.
+ */
+ boolean isSelectedIndex(int index);
+
+ /**
+ * Returns true if there is no selection, and false
+ * otherwise.
+ *
+ * @return true if there is no selection, and
+ * false otherwise.
+ */
+ boolean isSelectionEmpty();
+
+ /**
+ * Sets the selection interval to the specified range (note that
+ * anchor can be less than, equal to, or greater than
+ * lead). If this results in the selection being changed,
+ * a {@link ListSelectionEvent} is sent to all registered listeners.
+ *
+ * If the selection mode is {@link #SINGLE_SELECTION}, only the
+ * lead item is selected.
+ *
+ * @param anchor the anchor index.
+ * @param lead the lead index.
+ */
+ void setSelectionInterval(int anchor, int lead);
+
+ /**
+ * Marks the items in the specified interval as selected. The behaviour of
+ * this method depends on the selection mode:
+ *
+ *
SINGLE_SELECTION - only the lead item is
+ * selected;
+ *
SINGLE_INTERVAL_SELECTION - the existing selection
+ * interval is replaced by the specified interval;
+ *
MULTIPLE_INTERVAL_SELECTION - the specified interval is
+ * merged into the currently selected intervals.
+ *
+ * Note that anchor can be less than, equal to, or greater than
+ * lead.
+ *
+ * @param anchor the index of the anchor item
+ * @param lead the index of the lead item.
+ */
+ void addSelectionInterval(int anchor, int lead);
+
+ /**
+ * Marks the items in the specified interval as not selected. The behaviour
+ * of this method depends on the selection mode:
+ *
+ *
SINGLE_SELECTION - XXX;
+ *
SINGLE_INTERVAL_SELECTION - XXX;
+ *
MULTIPLE_INTERVAL_SELECTION - XXX.
+ *
+ * Note that anchor can be less than, equal to, or greater than
+ * lead.
+ *
+ * @param anchor the index of the anchor item
+ * @param lead the index of the lead item.
+ */
+ void removeSelectionInterval(int anchor, int lead);
+
+ /**
+ * Inserts a new interval containing length items at the
+ * specified index (the before flag indicates
+ * whether the range is inserted before or after the existing item at
+ * index).
+ *
+ * FIXME: What is the selection status of the new items? Bug 4870694.
+ * FIXME: What event is generated?
+ *
+ * @param index the index of the item.
+ * @param length the number of items in the interval to be inserted.
+ * @param before if true, the interval should be inserted
+ * before index, otherwise it is inserted after.
+ *
+ * @see #removeIndexInterval(int, int)
+ */
+ void insertIndexInterval(int index, int length, boolean before);
+
+ /**
+ * Removes the items in the specified range (inclusive) from the selection
+ * model. This method should be called when an interval is deleted from
+ * the underlying list.
+ *
+ * FIXME: what happens to the lead and anchor indices if they are part of
+ * the range that is removed?
+ * FIXME: what event is generated
+ *
+ * @param index0 XXX
+ * @param index1 XXX
+ *
+ * @see #insertIndexInterval(int, int, boolean)
+ */
+ void removeIndexInterval(int index0, int index1);
+
+ /**
+ * Returns the index of the anchor item.
+ *
+ * @return The index of the anchor item.
+ *
+ * @see #setAnchorSelectionIndex(int)
+ */
+ int getAnchorSelectionIndex();
+
+ /**
+ * Sets the index of the anchor item.
+ *
+ * @param index the item index.
+ *
+ * @see #getAnchorSelectionIndex()
+ */
+ void setAnchorSelectionIndex(int index);
+
+ /**
+ * Returns the index of the lead item.
+ *
+ * @return The index of the lead item.
+ *
+ * @see #setLeadSelectionIndex(int)
+ */
+ int getLeadSelectionIndex();
+
+ /**
+ * Sets the index of the lead item.
+ *
+ * @param index the item index.
+ *
+ * @see #getLeadSelectionIndex()
+ */
+ void setLeadSelectionIndex(int index);
+
+ /**
+ * Sets the flag that is passed to listeners for each change notification.
+ * If a sequence of changes is made to the selection model, this flag should
+ * be set to true at the start of the sequence, and
+ * false for the last change - this gives listeners the option
+ * to ignore interim changes if that is more efficient.
+ *
+ * @param valueIsAdjusting the flag value.
+ *
+ * @see #getValueIsAdjusting()
+ */
+ void setValueIsAdjusting(boolean valueIsAdjusting);
+
+ /**
+ * Returns a flag that is passed to registered listeners when changes are
+ * made to the model. See the description for
+ * {@link #setValueIsAdjusting(boolean)} for more information.
+ *
+ * @return The flag.
+ */
+ boolean getValueIsAdjusting();
+
+ /**
+ * Registers a listener with the model so that it receives notification
+ * of changes to the model.
+ *
+ * @param listener the listener (null ignored).
+ *
+ * @see #removeListSelectionListener(ListSelectionListener)
+ */
+ void addListSelectionListener(ListSelectionListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives notification of
+ * changes to the model. If the specified listener is not registered with
+ * the model, or is null, this method does nothing.
+ *
+ * @param listener the listener (null ignored).
+ *
+ * @see #addListSelectionListener(ListSelectionListener)
+ */
+ void removeListSelectionListener(ListSelectionListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/LookAndFeel.java b/libjava/classpath/javax/swing/LookAndFeel.java
new file mode 100644
index 000000000..aec6ebb7f
--- /dev/null
+++ b/libjava/classpath/javax/swing/LookAndFeel.java
@@ -0,0 +1,433 @@
+/* LookAndFeel.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.net.URL;
+
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentInputMapUIResource;
+import javax.swing.plaf.IconUIResource;
+import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.UIResource;
+import javax.swing.plaf.metal.MetalLookAndFeel;
+import javax.swing.text.JTextComponent;
+
+/**
+ * A look-and-feel controls most aspects of the appearance and
+ * operation of user interface components in javax.swing. A
+ * cross-platform look-and-feel (the {@link MetalLookAndFeel}) is provided.
+ *
+ * @see UIManager#getInstalledLookAndFeels()
+ * @see UIManager#setLookAndFeel(LookAndFeel)
+ */
+public abstract class LookAndFeel
+{
+ /**
+ * Creates and returns a look-and-feel specific defaults table. This method
+ * is called once by {@link UIManager#setLookAndFeel(LookAndFeel)} and
+ * shouldn't be called again (as it creates a large table of defaults).
+ *
+ * @return The UI defaults.
+ */
+ public UIDefaults getDefaults()
+ {
+ return null;
+ }
+
+ /**
+ * Returns a description of the look and feel.
+ *
+ * @return A description of the look and feel.
+ */
+ public abstract String getDescription();
+
+ /**
+ * Returns the value of Toolkit.getDefaultToolkit()
+ * .getDesktopProperty(systemPropertyName), or
+ * fallbackValue if no such property is defined.
+ *
+ * @param systemPropertyName the system property name.
+ * @param fallbackValue the fallback value.
+ *
+ * @return The property value or fallbackValue.
+ */
+ public static Object getDesktopPropertyValue(String systemPropertyName,
+ Object fallbackValue)
+ {
+ Object value = Toolkit.getDefaultToolkit().getDesktopProperty(
+ systemPropertyName);
+ return value != null ? value : fallbackValue;
+ }
+
+ /**
+ * Returns an identifier for the look and feel.
+ *
+ * @return An identifier for the look and feel.
+ */
+ public abstract String getID();
+
+ /**
+ * Returns the name for the look and feel.
+ *
+ * @return The name for the look and feel.
+ */
+ public abstract String getName();
+
+ /**
+ * Returns true when the look-and-feel supports window
+ * decorations, and false otherwise. This default implementation
+ * always returns false and needs to be overridden when the
+ * derived look-and-feel supports this.
+ *
+ * @return false.
+ *
+ * @since 1.4
+ */
+ public boolean getSupportsWindowDecorations()
+ {
+ return false;
+ }
+
+ /**
+ * Initializes the look-and-feel. The
+ * {@link UIManager#setLookAndFeel(LookAndFeel)} method calls this method
+ * before the first call (and typically the only call) to
+ * {@link #getDefaults()}. This default implementation does nothing, but
+ * subclasses can override this behaviour.
+ */
+ public void initialize()
+ {
+ // We do nothing here. This method is meant to be overridden by
+ // LookAndFeel implementations.
+ }
+
+ /**
+ * Convenience method for installing a component's default {@link Border}
+ * object on the specified component if either the border is currently
+ * null or already an instance of {@link UIResource}.
+ *
+ * @param c the component (null not permitted).
+ * @param defaultBorderName the border name (for lookup in the UIDefaults
+ * table).
+ */
+ public static void installBorder(JComponent c, String defaultBorderName)
+ {
+ Border b = c.getBorder();
+ if (b == null || b instanceof UIResource)
+ c.setBorder(UIManager.getBorder(defaultBorderName));
+ }
+
+ /**
+ * Convenience method for initializing a component's foreground and
+ * background color properties with values from the current defaults table.
+ *
+ * @param c the component (null not permitted).
+ * @param defaultBgName the key for the background color in the UIDefaults
+ * table.
+ * @param defaultFgName the key for the foreground color in the UIDefaults
+ * table.
+ */
+ public static void installColors(JComponent c, String defaultBgName,
+ String defaultFgName)
+ {
+ // Install background.
+ Color bg = c.getBackground();
+ if (bg == null || bg instanceof UIResource)
+ c.setBackground(UIManager.getColor(defaultBgName));
+
+ // Install foreground.
+ Color fg = c.getForeground();
+ if (fg == null || fg instanceof UIResource)
+ c.setForeground(UIManager.getColor(defaultFgName));
+ }
+
+ /**
+ * Convenience method for initializing a component's foreground, background
+ * and font properties with values from the current defaults table.
+ *
+ * @param component the component (null not permitted).
+ * @param defaultBgName the key for the background color in the UIDefaults
+ * table.
+ * @param defaultFgName the key for the foreground color in the UIDefaults
+ * table.
+ * @param defaultFontName the key for the font in the UIDefaults table.
+ */
+ public static void installColorsAndFont(JComponent component,
+ String defaultBgName,
+ String defaultFgName,
+ String defaultFontName)
+ {
+ // Install colors.
+ installColors(component, defaultBgName, defaultFgName);
+ // Install font.
+ Font f = component.getFont();
+ if (f == null || f instanceof UIResource)
+ component.setFont(UIManager.getFont(defaultFontName));
+ }
+
+ /**
+ * Returns true if the look-and-feel is the "native"
+ * look-and-feel for the current platform, and false otherwise.
+ * A native look-and-feel emulates the appearance and behaviour of the
+ * default windowing system on the host operating system.
+ *
+ * @return A flag indicating whether or not this is the native look and feel
+ * for the current platform.
+ */
+ public abstract boolean isNativeLookAndFeel();
+
+ /**
+ * Returns true if the look-and-feel is supported on the
+ * current operating system, and false otherwise. This
+ * mechanism is provided so that it is possible to prevent a look-and-feel
+ * from being used on some operating systems (usually for legal, not
+ * technical, reasons).
+ *
+ * @return A flag indicating whether or not the look-and-feel is supported
+ * on the current platform.
+ */
+ public abstract boolean isSupportedLookAndFeel();
+
+ /**
+ * Loads the bindings in keys into retMap. Does not remove existing entries
+ * from retMap. keys describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param retMap the InputMap into which we load bindings
+ * @param keys the Object array describing the InputMap as above
+ */
+ public static void loadKeyBindings(InputMap retMap, Object[] keys)
+ {
+ if (keys == null)
+ return;
+ for (int i = 0; i < keys.length - 1; i += 2)
+ {
+ Object key = keys[i];
+ KeyStroke keyStroke;
+ if (key instanceof KeyStroke)
+ keyStroke = (KeyStroke) key;
+ else
+ keyStroke = KeyStroke.getKeyStroke((String) key);
+ retMap.put(keyStroke, keys[i + 1]);
+ }
+ }
+
+ /**
+ * Creates a ComponentInputMap from keys.
+ * keys describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param c the JComponent associated with the ComponentInputMap
+ * @param keys the Object array describing the InputMap as above
+ *
+ * @return A new input map.
+ */
+ public static ComponentInputMap makeComponentInputMap(JComponent c,
+ Object[] keys)
+ {
+ ComponentInputMap retMap = new ComponentInputMapUIResource(c);
+ loadKeyBindings(retMap, keys);
+ return retMap;
+ }
+
+ /**
+ * Utility method that creates a UIDefaults.LazyValue that creates an
+ * ImageIcon UIResource for the specified gifFile filename.
+ *
+ * @param baseClass the base class for accessing the icon resource.
+ * @param gifFile the file name.
+ *
+ * @return A {@link UIDefaults.LazyValue} that serves up an
+ * {@link IconUIResource}.
+ */
+ public static Object makeIcon(Class> baseClass, String gifFile)
+ {
+ final URL file = baseClass.getResource(gifFile);
+ return new UIDefaults.LazyValue()
+ {
+ public Object createValue(UIDefaults table)
+ {
+ return new IconUIResource(new ImageIcon(file));
+ }
+ };
+ }
+
+ /**
+ * Creates a InputMap from keys.
+ * keys describes the InputMap, every even indexed
+ * item is either a KeyStroke or a String representing a KeyStroke and every
+ * odd indexed item is the Object associated with that KeyStroke in an
+ * ActionMap.
+ *
+ * @param keys the Object array describing the InputMap as above
+ *
+ * @return A new input map.
+ */
+ public static InputMap makeInputMap(Object[] keys)
+ {
+ InputMap retMap = new InputMapUIResource();
+ loadKeyBindings(retMap, keys);
+ return retMap;
+ }
+
+ /**
+ * Convenience method for building lists of KeyBindings.
+ * keyBindingList is an array of KeyStroke-Action pairs where
+ * even indexed elements are KeyStrokes or Strings representing KeyStrokes
+ * and odd indexed elements are the associated Actions.
+ *
+ * @param keyBindingList the array of KeyStroke-Action pairs
+ * @return a JTextComponent.KeyBinding array
+ */
+ public static JTextComponent.KeyBinding[] makeKeyBindings(
+ Object[] keyBindingList)
+ {
+ JTextComponent.KeyBinding[] retBindings =
+ new JTextComponent.KeyBinding[keyBindingList.length / 2];
+ for (int i = 0; i < keyBindingList.length - 1; i += 2)
+ {
+ KeyStroke stroke;
+ if (keyBindingList[i] instanceof KeyStroke)
+ stroke = (KeyStroke) keyBindingList[i];
+ else
+ stroke = KeyStroke.getKeyStroke((String) keyBindingList[i]);
+ retBindings[i / 2] = new JTextComponent.KeyBinding(stroke,
+ (String) keyBindingList[i + 1]);
+ }
+ return retBindings;
+ }
+
+ /**
+ * Invoked when the user attempts an invalid operation. The default
+ * implementation just beeps. Subclasses that wish to change this need to
+ * override this method.
+ *
+ * @param component the component the error occured in
+ */
+ public void provideErrorFeedback(Component component)
+ {
+ Toolkit.getDefaultToolkit().beep();
+ }
+
+ /**
+ * Returns a string that displays and identifies this object's properties.
+ *
+ * @return string containing the description and class name.
+ */
+ public String toString()
+ {
+ return getDescription() + " " + getClass().getName();
+ }
+
+ /**
+ * UIManager.setLookAndFeel calls this method just before we're replaced by
+ * a new default look and feel.
+ */
+ public void uninitialize()
+ {
+ // We do nothing here. This method is meant to be overridden by
+ // LookAndFeel implementations.
+ }
+
+ /**
+ * Convenience method for un-installing a component's default border on the
+ * specified component if the border is currently an instance of UIResource.
+ *
+ * @param c the component (null not permitted).
+ */
+ public static void uninstallBorder(JComponent c)
+ {
+ if (c.getBorder() instanceof UIResource)
+ c.setBorder(null);
+ }
+
+ /**
+ * This methods installs a UI property if it hasn't already been set by an
+ * application. This method is used by UI delegates that install a default
+ * value for a property with a primitive type but do not want to override
+ * a value that has been set by an application.
+ *
+ * The supported properties depend on the actual type of the component and
+ * are listed in the table below. The supported properties are of course
+ * inherited to subclasses.
+ *
+ *
+ *
+ * @param c the component to install the property to
+ * @param propertyName the name of the property
+ * @param value the value of the property
+ *
+ * @throws IllegalArgumentException if the specified property cannot be set
+ * by this method
+ * @throws ClassCastException if the property value does not match the
+ * property type
+ * @throws NullPointerException if c or
+ * propertyValue is null
+ *
+ * @since 1.5
+ */
+ public static void installProperty(JComponent c, String propertyName,
+ Object value)
+ {
+ c.setUIProperty(propertyName, value);
+ }
+}
diff --git a/libjava/classpath/javax/swing/MenuElement.java b/libjava/classpath/javax/swing/MenuElement.java
new file mode 100644
index 000000000..dab7b9cf1
--- /dev/null
+++ b/libjava/classpath/javax/swing/MenuElement.java
@@ -0,0 +1,89 @@
+/* MenuElement.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+
+/**
+ * Defines the methods that any menu element in a {@link JMenu} must
+ * implement.
+ *
+ * @author Andrew Selkirk
+ */
+public interface MenuElement
+{
+
+ /**
+ * processMouseEvent
+ * @param event TODO
+ * @param path TODO
+ * @param manager TODO
+ */
+ void processMouseEvent(MouseEvent event, MenuElement[] path,
+ MenuSelectionManager manager);
+
+ /**
+ * processKeyEvent
+ * @param event TODO
+ * @param path TODO
+ * @param manager TODO
+ */
+ void processKeyEvent(KeyEvent event, MenuElement[] path,
+ MenuSelectionManager manager);
+
+ /**
+ * menuSelectionChanged
+ * @param included TODO
+ */
+ void menuSelectionChanged(boolean included);
+
+ /**
+ * getSubElements
+ * @returns MenuElement[]
+ */
+ MenuElement[] getSubElements();
+
+ /**
+ * getComponent
+ * @returns Component
+ */
+ Component getComponent();
+
+}
diff --git a/libjava/classpath/javax/swing/MenuSelectionManager.java b/libjava/classpath/javax/swing/MenuSelectionManager.java
new file mode 100644
index 000000000..0b654499d
--- /dev/null
+++ b/libjava/classpath/javax/swing/MenuSelectionManager.java
@@ -0,0 +1,440 @@
+/* MenuSelectionManager.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Vector;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+import javax.swing.event.EventListenerList;
+
+/**
+ * This class manages current menu selectection. It provides
+ * methods to clear and set current selected menu path.
+ * It also fires StateChange event to its registered
+ * listeners whenever selected path of the current menu hierarchy
+ * changes.
+ *
+ */
+public class MenuSelectionManager
+{
+ /** ChangeEvent fired when selected path changes*/
+ protected ChangeEvent changeEvent = new ChangeEvent(this);
+
+ /** List of listeners for this MenuSelectionManager */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /** Default manager for the current menu hierarchy*/
+ private static final MenuSelectionManager manager = new MenuSelectionManager();
+
+ /** Path to the currently selected menu */
+ private Vector selectedPath = new Vector();
+
+ /**
+ * Fires StateChange event to registered listeners
+ */
+ protected void fireStateChanged()
+ {
+ ChangeListener[] listeners = getChangeListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners[i].stateChanged(changeEvent);
+ }
+
+ /**
+ * Adds ChangeListener to this MenuSelectionManager
+ *
+ * @param listener ChangeListener to add
+ */
+ public void addChangeListener(ChangeListener listener)
+ {
+ listenerList.add(ChangeListener.class, listener);
+ }
+
+ /**
+ * Removes ChangeListener from the list of registered listeners
+ * for this MenuSelectionManager.
+ *
+ * @param listener ChangeListner to remove
+ */
+ public void removeChangeListener(ChangeListener listener)
+ {
+ listenerList.remove(ChangeListener.class, listener);
+ }
+
+ /**
+ * Returns list of registered listeners with MenuSelectionManager
+ *
+ * @since 1.4
+ */
+ public ChangeListener[] getChangeListeners()
+ {
+ return (ChangeListener[]) listenerList.getListeners(ChangeListener.class);
+ }
+
+ /**
+ * Unselects all the menu elements on the selection path
+ */
+ public void clearSelectedPath()
+ {
+ // Send events from the bottom most item in the menu - hierarchy to the
+ // top most
+ for (int i = selectedPath.size() - 1; i >= 0; i--)
+ ((MenuElement) selectedPath.get(i)).menuSelectionChanged(false);
+
+ // clear selected path
+ selectedPath.clear();
+
+ // notify all listeners that the selected path was changed
+ fireStateChanged();
+ }
+
+ /**
+ * This method returns menu element on the selected path that contains
+ * given source point. If no menu element on the selected path contains this
+ * point, then null is returned.
+ *
+ * @param source Component relative to which sourcePoint is given
+ * @param sourcePoint point for which we want to find menu element that contains it
+ *
+ * @return Returns menu element that contains given source point and belongs
+ * to the currently selected path. Null is return if no such menu element found.
+ */
+ public Component componentForPoint(Component source, Point sourcePoint)
+ {
+ // Convert sourcePoint to screen coordinates.
+ Point sourcePointOnScreen = sourcePoint;
+
+ if (source.isShowing())
+ SwingUtilities.convertPointToScreen(sourcePointOnScreen, source);
+
+ Point compPointOnScreen;
+ Component resultComp = null;
+
+ // For each menu element on the selected path, express its location
+ // in terms of screen coordinates and check if there is any
+ // menu element on the selected path that contains given source point.
+ for (int i = 0; i < selectedPath.size(); i++)
+ {
+ Component comp = ((Component) selectedPath.get(i));
+ Dimension size = comp.getSize();
+
+ // convert location of this menu item to screen coordinates
+ compPointOnScreen = comp.getLocationOnScreen();
+
+ if (compPointOnScreen.x <= sourcePointOnScreen.x
+ && sourcePointOnScreen.x < compPointOnScreen.x + size.width
+ && compPointOnScreen.y <= sourcePointOnScreen.y
+ && sourcePointOnScreen.y < compPointOnScreen.y + size.height)
+ {
+ Point p = sourcePointOnScreen;
+
+ if (comp.isShowing())
+ SwingUtilities.convertPointFromScreen(p, comp);
+
+ resultComp = SwingUtilities.getDeepestComponentAt(comp, p.x, p.y);
+ break;
+ }
+ }
+ return resultComp;
+ }
+
+ /**
+ * Returns shared instance of MenuSelection Manager
+ *
+ * @return default Manager
+ */
+ public static MenuSelectionManager defaultManager()
+ {
+ return manager;
+ }
+
+ /**
+ * Returns path representing current menu selection
+ *
+ * @return Current selection path
+ */
+ public MenuElement[] getSelectedPath()
+ {
+ MenuElement[] path = new MenuElement[selectedPath.size()];
+
+ for (int i = 0; i < path.length; i++)
+ path[i] = (MenuElement) selectedPath.get(i);
+
+ return path;
+ }
+
+ /**
+ * Returns true if specified component is part of current menu
+ * heirarchy and false otherwise
+ *
+ * @param c Component for which to check
+ * @return True if specified component is part of current menu
+ */
+ public boolean isComponentPartOfCurrentMenu(Component c)
+ {
+ MenuElement[] subElements;
+ boolean ret = false;
+ for (int i = 0; i < selectedPath.size(); i++)
+ {
+ // Check first element.
+ MenuElement first = (MenuElement) selectedPath.get(i);
+ if (SwingUtilities.isDescendingFrom(c, first.getComponent()))
+ {
+ ret = true;
+ break;
+ }
+ else
+ {
+ // Check sub elements.
+ subElements = first.getSubElements();
+ for (int j = 0; j < subElements.length; j++)
+ {
+ MenuElement me = subElements[j];
+ if (me != null
+ && (SwingUtilities.isDescendingFrom(c, me.getComponent())))
+ {
+ ret = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return ret;
+ }
+
+ /**
+ * Processes key events on behalf of the MenuElements. MenuElement
+ * instances should always forward their key events to this method and
+ * get their {@link MenuElement#processKeyEvent(KeyEvent, MenuElement[],
+ * MenuSelectionManager)} eventually called back.
+ *
+ * @param e the key event
+ */
+ public void processKeyEvent(KeyEvent e)
+ {
+ MenuElement[] selection = (MenuElement[])
+ selectedPath.toArray(new MenuElement[selectedPath.size()]);
+ if (selection.length == 0)
+ return;
+
+ MenuElement[] path;
+ for (int index = selection.length - 1; index >= 0; index--)
+ {
+ MenuElement el = selection[index];
+ // This method's main purpose is to forward key events to the
+ // relevant menu items, so that they can act in response to their
+ // mnemonics beeing typed. So we also need to forward the key event
+ // to all the subelements of the currently selected menu elements
+ // in the path.
+ MenuElement[] subEls = el.getSubElements();
+ path = null;
+ for (int subIndex = 0; subIndex < subEls.length; subIndex++)
+ {
+ MenuElement sub = subEls[subIndex];
+ // Skip elements that are not showing or not enabled.
+ if (sub == null || ! sub.getComponent().isShowing()
+ || ! sub.getComponent().isEnabled())
+ {
+ continue;
+ }
+
+ if (path == null)
+ {
+ path = new MenuElement[index + 2];
+ System.arraycopy(selection, 0, path, 0, index + 1);
+ }
+ path[index + 1] = sub;
+ sub.processKeyEvent(e, path, this);
+ if (e.isConsumed())
+ break;
+ }
+ if (e.isConsumed())
+ break;
+ }
+
+ // Dispatch to first element in selection if it hasn't been consumed.
+ if (! e.isConsumed())
+ {
+ path = new MenuElement[1];
+ path[0] = selection[0];
+ path[0].processKeyEvent(e, path, this);
+ }
+ }
+
+ /**
+ * Forwards given mouse event to all of the source subcomponents.
+ *
+ * @param event Mouse event
+ */
+ public void processMouseEvent(MouseEvent event)
+ {
+ Component source = ((Component) event.getSource());
+
+ // In the case of drag event, event.getSource() returns component
+ // where drag event originated. However menu element processing this
+ // event should be the one over which mouse is currently located,
+ // which is not necessary the source of the drag event.
+ Component mouseOverMenuComp;
+
+ // find over which menu element the mouse is currently located
+ if (event.getID() == MouseEvent.MOUSE_DRAGGED
+ || event.getID() == MouseEvent.MOUSE_RELEASED)
+ mouseOverMenuComp = componentForPoint(source, event.getPoint());
+ else
+ mouseOverMenuComp = source;
+
+ // Process this event only if mouse is located over some menu element
+ if (mouseOverMenuComp != null && (mouseOverMenuComp instanceof MenuElement))
+ {
+ MenuElement[] path = getPath(mouseOverMenuComp);
+ ((MenuElement) mouseOverMenuComp).processMouseEvent(event, path,
+ manager);
+
+ // FIXME: Java specification says that mouse events should be
+ // forwarded to subcomponents. The code below does it, but
+ // menu's work fine without it. This code is commented for now.
+
+ /*
+ MenuElement[] subComponents = ((MenuElement) mouseOverMenuComp)
+ .getSubElements();
+
+ for (int i = 0; i < subComponents.length; i++)
+ {
+ subComponents[i].processMouseEvent(event, path, manager);
+ }
+ */
+ }
+ else
+ {
+ if (event.getID() == MouseEvent.MOUSE_RELEASED)
+ clearSelectedPath();
+ }
+ }
+
+ /**
+ * Sets menu selection to the specified path
+ *
+ * @param path new selection path
+ */
+ public void setSelectedPath(MenuElement[] path)
+ {
+ if (path == null)
+ {
+ clearSelectedPath();
+ return;
+ }
+
+ int minSize = path.length; // size of the smaller path.
+ int currentSize = selectedPath.size();
+ int firstDiff = 0;
+
+ // Search first item that is different in the current and new path.
+ for (int i = 0; i < minSize; i++)
+ {
+ if (i < currentSize && (MenuElement) selectedPath.get(i) == path[i])
+ firstDiff++;
+ else
+ break;
+ }
+
+ // Remove items from selection and send notification.
+ for (int i = currentSize - 1; i >= firstDiff; i--)
+ {
+ MenuElement el = (MenuElement) selectedPath.get(i);
+ selectedPath.remove(i);
+ el.menuSelectionChanged(false);
+ }
+
+ // Add new items to selection and send notification.
+ for (int i = firstDiff; i < minSize; i++)
+ {
+ if (path[i] != null)
+ {
+ selectedPath.add(path[i]);
+ path[i].menuSelectionChanged(true);
+ }
+ }
+
+ fireStateChanged();
+ }
+
+ /**
+ * Returns path to the specified component
+ *
+ * @param c component for which to find path for
+ *
+ * @return path to the specified component
+ */
+ private MenuElement[] getPath(Component c)
+ {
+ // FIXME: There is the same method in BasicMenuItemUI. However I
+ // cannot use it here instead of this method, since I cannot assume that
+ // all the menu elements on the selected path are JMenuItem or JMenu.
+ // For now I've just duplicated it here. Please
+ // fix me or delete me if another better approach will be found, and
+ // this method will not be necessary.
+ ArrayList path = new ArrayList();
+
+ // if given component is JMenu, we also need to include
+ // it's popup menu in the path
+ if (c instanceof JMenu)
+ path.add(((JMenu) c).getPopupMenu());
+ while (c instanceof MenuElement)
+ {
+ path.add(0, (MenuElement) c);
+
+ if (c instanceof JPopupMenu)
+ c = ((JPopupMenu) c).getInvoker();
+ else
+ c = c.getParent();
+ }
+
+ MenuElement[] pathArray = new MenuElement[path.size()];
+ path.toArray(pathArray);
+ return pathArray;
+ }
+}
diff --git a/libjava/classpath/javax/swing/MutableComboBoxModel.java b/libjava/classpath/javax/swing/MutableComboBoxModel.java
new file mode 100644
index 000000000..93091786e
--- /dev/null
+++ b/libjava/classpath/javax/swing/MutableComboBoxModel.java
@@ -0,0 +1,82 @@
+/* MutableComboBoxModel.java --
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+/**
+ * A data model used in {@link JComboBox}es that keeps track of the
+ * components data and provides methods to insert and remove elements from
+ * it. The classes implementing this interface should
+ * fire appropriate events indicating the undergoing change in the data model.
+ *
+ * @author Andrew Selkirk
+ * @author Olga Rodimina
+ */
+public interface MutableComboBoxModel extends ComboBoxModel
+{
+ /**
+ * This method adds given object to its data model.
+ *
+ * @param object element to add to the data model.
+ */
+ void addElement(Object object);
+
+ /**
+ * This method removes elements located at the given index in the data
+ * model.
+ *
+ * @param index index specifying location of the element to remove.
+ */
+ void removeElementAt(int index);
+
+ /**
+ * This method inserts givent element to the data model, at the specified
+ * index.
+ *
+ * @param object element to insert
+ * @param index index specifying the position in the data model where the
+ * given element should be inserted.
+ */
+ void insertElementAt(Object object, int index);
+
+ /**
+ * This method removes given element from the data model
+ *
+ * @param object element to remove.
+ */
+ void removeElement(Object object);
+}
diff --git a/libjava/classpath/javax/swing/OverlayLayout.java b/libjava/classpath/javax/swing/OverlayLayout.java
new file mode 100644
index 000000000..b037a09a7
--- /dev/null
+++ b/libjava/classpath/javax/swing/OverlayLayout.java
@@ -0,0 +1,412 @@
+/* OverlayLayout.java -- A layout manager
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.AWTError;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager2;
+import java.io.Serializable;
+
+/**
+ * A layout manager that lays out the components of a container one over
+ * another.
+ *
+ * The components take as much space as is available in the container, but not
+ * more than specified by their maximum size.
+ *
+ * The overall layout is mainly affected by the components
+ * alignmentX and alignmentY properties. All
+ * components are aligned, so that their alignment points (for either
+ * direction) are placed in one line (the baseline for this direction).
+ *
+ * For example: An X alignment of 0.0 means that the component's alignment
+ * point is at it's left edge, an X alignment of 0.5 means that the alignment
+ * point is in the middle, an X alignment of 1.0 means, the aligment point is
+ * at the right edge. So if you have three components, the first with 0.0, the
+ * second with 0.5 and the third with 1.0, then they are laid out like this:
+ *
+ *
+ * The above picture shows the X alignment between the components. An Y
+ * alignment like shown above cannot be achieved with this layout manager. The
+ * components are place on top of each other, with the X alignment shown above.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Andrew Selkirk
+ */
+public class OverlayLayout implements LayoutManager2, Serializable
+{
+ private static final long serialVersionUID = 18082829169631543L;
+
+ /**
+ * The container to be laid out.
+ */
+ private Container target;
+
+ /**
+ * The size requirements of the containers children for the X direction.
+ */
+ private SizeRequirements[] xChildren;
+
+ /**
+ * The size requirements of the containers children for the Y direction.
+ */
+ private SizeRequirements[] yChildren;
+
+ /**
+ * The size requirements of the container to be laid out for the X direction.
+ */
+ private SizeRequirements xTotal;
+
+ /**
+ * The size requirements of the container to be laid out for the Y direction.
+ */
+ private SizeRequirements yTotal;
+
+ /**
+ * The offsets of the child components in the X direction.
+ */
+ private int[] offsetsX;
+
+ /**
+ * The offsets of the child components in the Y direction.
+ */
+ private int[] offsetsY;
+
+ /**
+ * The spans of the child components in the X direction.
+ */
+ private int[] spansX;
+
+ /**
+ * The spans of the child components in the Y direction.
+ */
+ private int[] spansY;
+
+ /**
+ * Creates a new OverlayLayout for the specified container.
+ *
+ * @param target the container to be laid out
+ */
+ public OverlayLayout(Container target)
+ {
+ this.target = target;
+ }
+
+ /**
+ * Notifies the layout manager that the layout has become invalid. It throws
+ * away cached layout information and recomputes it the next time it is
+ * requested.
+ *
+ * @param target not used here
+ */
+ public void invalidateLayout(Container target)
+ {
+ xChildren = null;
+ yChildren = null;
+ xTotal = null;
+ yTotal = null;
+ offsetsX = null;
+ offsetsY = null;
+ spansX = null;
+ spansY = null;
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param string not used here
+ * @param component not used here
+ */
+ public void addLayoutComponent(String string, Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param component not used here
+ * @param constraints not used here
+ */
+ public void addLayoutComponent(Component component, Object constraints)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is not used in this layout manager.
+ *
+ * @param component not used here
+ */
+ public void removeLayoutComponent(Component component)
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Returns the preferred size of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the preferred size of the container that is laid out
+ */
+ public Dimension preferredLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.preferred, yTotal.preferred);
+ }
+
+ /**
+ * Returns the minimum size of the container that is laid out. This is
+ * computed by the children's minimum sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the minimum size of the container that is laid out
+ */
+ public Dimension minimumLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.minimum, yTotal.minimum);
+ }
+
+ /**
+ * Returns the maximum size of the container that is laid out. This is
+ * computed by the children's maximum sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the maximum size of the container that is laid out
+ */
+ public Dimension maximumLayoutSize(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return new Dimension(xTotal.maximum, yTotal.maximum);
+ }
+
+ /**
+ * Returns the X alignment of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the X alignment of the container that is laid out
+ */
+ public float getLayoutAlignmentX(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return xTotal.alignment;
+ }
+
+ /**
+ * Returns the Y alignment of the container that is laid out. This is
+ * computed by the children's preferred sizes, taking their alignments into
+ * account.
+ *
+ * @param target not used here
+ *
+ * @return the X alignment of the container that is laid out
+ */
+ public float getLayoutAlignmentY(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkTotalRequirements();
+ return yTotal.alignment;
+ }
+
+ /**
+ * Lays out the container and it's children.
+ *
+ * The children are laid out one over another.
+ *
+ * The components take as much space as is available in the container, but
+ * not more than specified by their maximum size.
+ *
+ * The overall layout is mainly affected by the components
+ * alignmentX and alignmentY properties. All
+ * components are aligned, so that their alignment points (for either
+ * direction) are placed in one line (the baseline for this direction).
+ *
+ * For example: An X alignment of 0.0 means that the component's alignment
+ * point is at it's left edge, an X alignment of 0.5 means that the alignment
+ * point is in the middle, an X alignment of 1.0 means, the aligment point is
+ * at the right edge. So if you have three components, the first with 0.0,
+ * the second with 0.5 and the third with 1.0, then they are laid out like
+ * this:
+ *
+ *
+ * The above picture shows the X alignment between the components. An Y
+ * alignment like shown above cannot be achieved with this layout manager.
+ * The components are place on top of each other, with the X alignment shown
+ * above.
+ *
+ * @param target not used here
+ */
+ public void layoutContainer(Container target)
+ {
+ if (target != this.target)
+ throw new AWTError("OverlayLayout can't be shared");
+
+ checkLayout();
+ Component[] children = target.getComponents();
+ for (int i = 0; i < children.length; i++)
+ children[i].setBounds(offsetsX[i], offsetsY[i], spansX[i], spansY[i]);
+ }
+
+ /**
+ * Makes sure that the xChildren and yChildren fields are correctly set up.
+ * A call to {@link #invalidateLayout(Container)} sets these fields to null,
+ * so they have to be set up again.
+ */
+ private void checkRequirements()
+ {
+ if (xChildren == null || yChildren == null)
+ {
+ Component[] children = target.getComponents();
+ xChildren = new SizeRequirements[children.length];
+ yChildren = new SizeRequirements[children.length];
+ for (int i = 0; i < children.length; i++)
+ {
+ if (! children[i].isVisible())
+ {
+ xChildren[i] = new SizeRequirements();
+ yChildren[i] = new SizeRequirements();
+ }
+ else
+ {
+ xChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().width,
+ children[i].getPreferredSize().width,
+ children[i].getMaximumSize().width,
+ children[i].getAlignmentX());
+ yChildren[i] =
+ new SizeRequirements(children[i].getMinimumSize().height,
+ children[i].getPreferredSize().height,
+ children[i].getMaximumSize().height,
+ children[i].getAlignmentY());
+ }
+ }
+ }
+ }
+
+ /**
+ * Makes sure that the xTotal and yTotal fields are set up correctly. A call
+ * to {@link #invalidateLayout} sets these fields to null and they have to be
+ * recomputed.
+ */
+ private void checkTotalRequirements()
+ {
+ if (xTotal == null || yTotal == null)
+ {
+ checkRequirements();
+ xTotal = SizeRequirements.getAlignedSizeRequirements(xChildren);
+ yTotal = SizeRequirements.getAlignedSizeRequirements(yChildren);
+ }
+ }
+
+ /**
+ * Makes sure that the offsetsX, offsetsY, spansX and spansY fields are set
+ * up correctly. A call to {@link #invalidateLayout} sets these fields
+ * to null and they have to be recomputed.
+ */
+ private void checkLayout()
+ {
+ if (offsetsX == null || offsetsY == null || spansX == null
+ || spansY == null)
+ {
+ checkRequirements();
+ checkTotalRequirements();
+ int len = target.getComponents().length;
+ offsetsX = new int[len];
+ offsetsY = new int[len];
+ spansX = new int[len];
+ spansY = new int[len];
+
+ Insets in = target.getInsets();
+ int width = target.getWidth() - in.left - in.right;
+ int height = target.getHeight() - in.top - in.bottom;
+
+ SizeRequirements.calculateAlignedPositions(width, xTotal,
+ xChildren, offsetsX, spansX);
+ SizeRequirements.calculateAlignedPositions(height, yTotal,
+ yChildren, offsetsY, spansY);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/Popup.java b/libjava/classpath/javax/swing/Popup.java
new file mode 100644
index 000000000..65be7cfe1
--- /dev/null
+++ b/libjava/classpath/javax/swing/Popup.java
@@ -0,0 +1,301 @@
+/* Popup.java --
+ Copyright (C) 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.FlowLayout;
+import java.awt.Point;
+import java.awt.Rectangle;
+
+
+/**
+ * Manages a popup window that displays a Component on top of
+ * everything else.
+ *
+ *
To obtain an instance of Popup, use the
+ * {@link javax.swing.PopupFactory}.
+ *
+ * @since 1.4
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class Popup
+{
+ /**
+ * Constructs a new Popup given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which x and
+ * y are relative, or null for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the Popup.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if contents
+ * is null.
+ */
+ protected Popup(Component owner, Component contents,
+ int x, int y)
+ {
+ if (contents == null)
+ throw new IllegalArgumentException();
+
+ // The real stuff happens in the implementation of subclasses,
+ // for instance JWindowPopup.
+ }
+
+
+ /**
+ * Constructs a new Popup.
+ */
+ protected Popup()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Displays the Popup on the screen. Nothing happens
+ * if it is currently shown.
+ */
+ public void show()
+ {
+ // Implemented by subclasses, for instance JWindowPopup.
+ }
+
+
+ /**
+ * Removes the Popup from the screen. Nothing happens
+ * if it is currently hidden.
+ */
+ public void hide()
+ {
+ // Implemented by subclasses, for instance JWindowPopup.
+ }
+
+
+ /**
+ * A Popup that uses a JWindow for
+ * displaying its contents.
+ *
+ * @see PopupFactory#getPopup
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+ static class JWindowPopup
+ extends Popup
+ {
+ /**
+ * The JWindow used for displaying the contents
+ * of the popup.
+ */
+ JWindow window;
+
+ private Component contents;
+
+ /**
+ * Constructs a new JWindowPopup given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which x and
+ * y are relative, or null for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the Popup.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if contents
+ * is null.
+ */
+ public JWindowPopup(Component owner, Component contents,
+ int x, int y)
+ {
+ /* Checks whether contents is null. */
+ super(owner, contents, x, y);
+
+ this.contents = contents;
+ window = new JWindow(SwingUtilities.getWindowAncestor(owner));
+ window.getContentPane().add(contents);
+ window.setLocation(x, y);
+ window.setFocusableWindowState(false);
+ }
+
+
+ /**
+ * Displays the popup's JWindow on the screen.
+ * Nothing happens if it is already visible.
+ */
+ public void show()
+ {
+ window.setSize(contents.getSize());
+ window.show();
+ }
+
+
+ /**
+ * Removes the popup's JWindow from the
+ * screen. Nothing happens if it is currently not visible.
+ */
+ public void hide()
+ {
+ /* Calling dispose() instead of hide() will conserve native
+ * system resources, for example memory in an X11 server.
+ * They will automatically be re-allocated by a call to
+ * show().
+ */
+ window.dispose();
+ }
+ }
+
+ /**
+ * A popup that displays itself within the JLayeredPane of a JRootPane of
+ * the containment hierarchy of the owner component.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+ static class LightweightPopup extends Popup
+ {
+ /**
+ * The owner component for this popup.
+ */
+ Component owner;
+
+ /**
+ * The contents that should be shown.
+ */
+ Component contents;
+
+ /**
+ * The X location in screen coordinates.
+ */
+ int x;
+
+ /**
+ * The Y location in screen coordinates.
+ */
+ int y;
+
+ /**
+ * The panel that holds the content.
+ */
+ private JPanel panel;
+
+ /**
+ * The layered pane of the owner.
+ */
+ private JLayeredPane layeredPane;
+
+ /**
+ * Constructs a new LightweightPopup given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the component that should own the popup window; this
+ * provides the JRootPane in which we place the popup window
+ *
+ * @param contents the contents that will be displayed inside
+ * the Popup.
+ *
+ * @param x the horizontal position where the Popup will appear in screen
+ * coordinates
+ *
+ * @param y the vertical position where the Popup will appear in screen
+ * coordinates
+ *
+ * @throws IllegalArgumentException if contents
+ * is null.
+ */
+ public LightweightPopup(Component owner, Component contents, int x, int y)
+ {
+ super(owner, contents, x, y);
+ this.owner = owner;
+ this.contents = contents;
+ this.x = x;
+ this.y = y;
+
+ JRootPane rootPane = SwingUtilities.getRootPane(owner);
+ JLayeredPane layeredPane = rootPane.getLayeredPane();
+ this.layeredPane = layeredPane;
+ }
+
+ /**
+ * Places the popup within the JLayeredPane of the owner component and
+ * makes it visible.
+ */
+ public void show()
+ {
+ // We insert a JPanel between the layered pane and the contents so we
+ // can fiddle with the setLocation() method without disturbing a
+ // JPopupMenu (which overrides setLocation in an unusual manner).
+ if (panel == null)
+ {
+ panel = new JPanel();
+ panel.setLayout(new FlowLayout(0, 0, 0));
+ }
+
+ panel.add(contents);
+ panel.setSize(contents.getSize());
+ Point layeredPaneLoc = layeredPane.getLocationOnScreen();
+ panel.setLocation(x - layeredPaneLoc.x, y - layeredPaneLoc.y);
+ layeredPane.add(panel, JLayeredPane.POPUP_LAYER, 0);
+ panel.repaint();
+ }
+
+ /**
+ * Removes the popup from the JLayeredPane thus making it invisible.
+ */
+ public void hide()
+ {
+ Rectangle bounds = panel.getBounds();
+ layeredPane.remove(panel);
+ layeredPane.repaint(bounds.x, bounds.y, bounds.width, bounds.height);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/PopupFactory.java b/libjava/classpath/javax/swing/PopupFactory.java
new file mode 100644
index 000000000..9468c8864
--- /dev/null
+++ b/libjava/classpath/javax/swing/PopupFactory.java
@@ -0,0 +1,171 @@
+/* PopupFactory.java --
+ Copyright (C) 2003 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+
+
+/**
+ * A factory for Popup objects. These are used to
+ * managed little windows that float over everything else,
+ * typically containing a popup menu.
+ *
+ * @since 1.4
+ *
+ * @author Sascha Brawer (brawer@dandelis.ch)
+ */
+public class PopupFactory
+{
+ /**
+ * The shared factory object.
+ *
+ * @see #getSharedInstance
+ * @see #setSharedInstance
+ */
+ private static PopupFactory sharedFactory;
+
+
+ /**
+ * Constructs a new PopupFactory. Usually, a single
+ * PopupFactory is shared among multiple consumers
+ * of Popup. Use {@link #getSharedInstance} to retrieve
+ * the current factory.
+ */
+ public PopupFactory()
+ {
+ // Nothing to do here.
+ }
+
+
+ /**
+ * Sets the shared factory.
+ *
+ * @param factory the PopupFactory that future invocations of
+ * {@link #getSharedInstance} will return.
+ *
+ * @throws IllegalArgumentException if factory
+ * is null.
+ */
+ public static void setSharedInstance(PopupFactory factory)
+ {
+ if (factory == null)
+ throw new IllegalArgumentException();
+
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ sharedFactory = factory;
+ }
+
+
+ /**
+ * Retrieves the shared factory, creating a new factory if
+ * necessary.
+ *
+ * @return a PopupFactory that can be used
+ * to create Popup objects.
+ */
+ public static PopupFactory getSharedInstance()
+ {
+ /* Swing is not designed to be thread-safe, so there is no
+ * need to synchronize the access to the global variable.
+ */
+ if (sharedFactory == null)
+ sharedFactory = new PopupFactory();
+
+ return sharedFactory;
+ }
+
+
+ /**
+ * Creates a new Popup given its owner,
+ * contents and the screen position where the popup
+ * will appear.
+ *
+ * @param owner the Component to which x and
+ * y are relative, or null for
+ * placing the popup relative to the origin of the screen.
+ *
+ * @param contents the contents that will be displayed inside
+ * the Popup.
+ *
+ * @param x the horizontal position where the Popup will appear.
+ *
+ * @param y the vertical position where the Popup will appear.
+ *
+ * @throws IllegalArgumentException if contents
+ * is null.
+ */
+ public Popup getPopup(Component owner, Component contents,
+ int x, int y)
+ {
+ Popup popup = null;
+ // By default we enable lightweight popups since they are more efficient
+ // than heavyweight popups.
+ boolean lightweightEnabled = true;
+ // Special case JPopupMenu here, since it supports a lightweightEnabled
+ // flag that we must respect.
+ if (contents instanceof JPopupMenu)
+ {
+ JPopupMenu menu = (JPopupMenu) contents;
+ lightweightEnabled = menu.isLightWeightPopupEnabled();
+ }
+
+ // If we have a root pane and the contents fits within the root pane and
+ // lightweight popups are enabled, than we can use a lightweight popup.
+ JRootPane root = SwingUtilities.getRootPane(owner);
+ if (root != null)
+ {
+ Point rootLoc = root.getLocationOnScreen();
+ Dimension contentsSize = contents.getSize();
+ Dimension rootSize = root.getSize();
+ if (x >= rootLoc.x && y > rootLoc.y
+ && (x - rootLoc.x) + contentsSize.width < rootSize.width
+ && (y - rootLoc.y) + contentsSize.height < rootSize.height)
+ popup = new Popup.LightweightPopup(owner, contents, x, y);
+ else
+ popup = new Popup.JWindowPopup(owner, contents, x, y);
+ }
+ else
+ popup = new Popup.JWindowPopup(owner, contents, x, y);
+ return popup;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ProgressMonitor.java b/libjava/classpath/javax/swing/ProgressMonitor.java
new file mode 100644
index 000000000..0d7ab3e27
--- /dev/null
+++ b/libjava/classpath/javax/swing/ProgressMonitor.java
@@ -0,0 +1,460 @@
+/* ProgressMonitor.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.accessibility.AccessibleContext;
+
+/**
+ *
Using this class you can easily monitor tasks where you cannot
+ * estimate the duration exactly.
+ *
+ *
A ProgressMonitor instance waits until the first time setProgress
+ * is called. When millisToDecideToPopup time elapsed the
+ * instance estimates the duration until the whole operation is completed.
+ * If this duration exceeds millisToPopup a non-modal dialog
+ * with a message and a progress bar is shown.
+ *
+ *
The value of millisToDecideToPopup defaults to
+ * 500 and millisToPopup to
+ * 2000.
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @since 1.2
+ * @status updated to 1.2
+ */
+public class ProgressMonitor
+{
+
+ /**
+ * The accessible content for this component
+ */
+ protected AccessibleContext accessibleContext;
+
+ /**
+ * parentComponent
+ */
+ Component component;
+
+ /**
+ * note
+ */
+ String note;
+
+ /**
+ * message
+ */
+ Object message;
+
+ /**
+ * millisToDecideToPopup
+ */
+ int millisToDecideToPopup = 500;
+
+ /**
+ * millisToPopup
+ */
+ int millisToPopup = 2000;
+
+ int min, max, progress;
+
+ JProgressBar progressBar;
+
+ JLabel noteLabel;
+
+ JDialog progressDialog;
+
+ Timer timer;
+
+ boolean canceled;
+
+ /**
+ * Creates a new ProgressMonitor instance. This is used to
+ * monitor a task and pops up a dialog if the task is taking a long time to
+ * run.
+ *
+ * @param component The parent component of the progress dialog or
+ * null.
+ * @param message A constant message object which works in the way it does
+ * in {@link JOptionPane}.
+ * @param note A string message which can be changed while the operation goes
+ * on.
+ * @param minimum The minimum value for the operation (start value).
+ * @param maximum The maximum value for the operation (end value).
+ */
+ public ProgressMonitor(Component component, Object message,
+ String note, int minimum, int maximum)
+ {
+
+ // Set data.
+ this.component = component;
+ this.message = message;
+ this.note = note;
+
+ min = minimum;
+ max = maximum;
+ }
+
+ /**
+ *
Hides the dialog and stops any measurements.
+ *
+ *
Has no effect when setProgress is not at least
+ * called once.
When called for the first time this initializes a timer
+ * which decides after millisToDecideToPopup time
+ * whether to show a progress dialog or not.
+ *
+ *
If the progress value equals or exceeds the maximum
+ * value the progress dialog is closed automatically.
+ *
+ * @param progress New progress value.
+ */
+ public void setProgress(int progress)
+ {
+ this.progress = progress;
+
+ // Initializes and starts a timer with a task
+ // which measures the duration and displays
+ // a progress dialog if neccessary.
+ if (timer == null && progressDialog == null)
+ {
+ timer = new Timer(25, null);
+ timer.addActionListener(new TimerListener());
+ timer.start();
+ }
+
+ // Cancels timer and hides progress dialog if the
+ // maximum value is reached.
+ if (progressBar != null && this.progress >= progressBar.getMaximum())
+ {
+ // The reason for using progressBar.getMaximum() instead of max is that
+ // we want to prevent that changes to the value have any effect after the
+ // progress dialog is visible (This is how the JDK behaves.).
+ close();
+ }
+
+ }
+
+ /**
+ * Returns the minimum or start value of the operation.
+ *
+ * @return Minimum or start value of the operation.
+ */
+ public int getMinimum()
+ {
+ return min;
+ }
+
+ /**
+ *
Use this method to set the minimum or start value of
+ * your operation.
+ *
+ *
For typical application like copy operation this will be
+ * zero.
+ *
+ *
Keep in mind that changing this value after the progress
+ * dialog is made visible has no effect upon the progress bar.
+ *
+ * @param minimum The new minimum value.
+ */
+ public void setMinimum(int minimum)
+ {
+ min = minimum;
+ }
+
+ /**
+ * Return the maximum or end value of your operation.
+ *
+ * @return Maximum or end value.
+ */
+ public int getMaximum()
+ {
+ return max;
+ }
+
+ /**
+ *
Sets the maximum or end value of the operation to the
+ * given integer.
+ *
+ * @param maximum
+ */
+ public void setMaximum(int maximum)
+ {
+ max = maximum;
+ }
+
+ /**
+ * Returns whether the user canceled the operation.
+ *
+ * @return Whether the operation was canceled.
+ */
+ public boolean isCanceled()
+ {
+ // The value is predefined to false
+ // and changes only when the user clicks
+ // the cancel button in the progress dialog.
+ return canceled;
+ }
+
+ /**
+ * Returns the amount of milliseconds to wait
+ * until the ProgressMonitor should decide whether
+ * a progress dialog is to be shown or not.
+ *
+ * @return The duration in milliseconds.
+ */
+ public int getMillisToDecideToPopup()
+ {
+ return millisToDecideToPopup;
+ }
+
+ /**
+ * Sets the amount of milliseconds to wait until the
+ * ProgressMonitor should decide whether a progress dialog
+ * is to be shown or not.
+ *
+ *
This method has no effect when the progress dialog
+ * is already visible.
+ *
+ * @param time The duration in milliseconds.
+ */
+ public void setMillisToDecideToPopup(int time)
+ {
+ millisToDecideToPopup = time;
+ }
+
+ /**
+ * Returns the number of milliseconds to wait before displaying the progress
+ * dialog. The default value is 2000.
+ *
+ * @return The number of milliseconds.
+ *
+ * @see #setMillisToPopup(int)
+ */
+ public int getMillisToPopup()
+ {
+ return millisToPopup;
+ }
+
+ /**
+ * Sets the number of milliseconds to wait before displaying the progress
+ * dialog.
+ *
+ * @param time the number of milliseconds.
+ *
+ * @see #getMillisToPopup()
+ */
+ public void setMillisToPopup(int time)
+ {
+ millisToPopup = time;
+ }
+
+ /**
+ * Returns a message which is shown in the progress dialog.
+ *
+ * @return The changeable message visible in the progress dialog.
+ */
+ public String getNote()
+ {
+ return note;
+ }
+
+ /**
+ *
Set the message shown in the progess dialog.
+ *
+ *
Changing the note while the progress dialog is visible
+ * is possible.
+ *
+ * @param note A message shown in the progress dialog.
+ */
+ public void setNote(String note)
+ {
+ if (noteLabel != null)
+ {
+ noteLabel.setText(note);
+ }
+ else
+ {
+ this.note = note;
+ }
+ }
+
+ /**
+ * Internal method that creates the progress dialog.
+ */
+ void createDialog()
+ {
+ // If there is no note we suppress the generation of the
+ // label.
+ Object[] tmp = (note == null) ?
+ new Object[]
+ {
+ message,
+ progressBar = new JProgressBar(min, max)
+ }
+ :
+ new Object[]
+ {
+ message,
+ noteLabel = new JLabel(note),
+ progressBar = new JProgressBar(min, max)
+ };
+
+ JOptionPane pane = new JOptionPane(tmp, JOptionPane.INFORMATION_MESSAGE);
+
+ // FIXME: Internationalize the button
+ JButton cancelButton = new JButton("Cancel");
+ cancelButton.addActionListener(new ActionListener()
+ {
+ public void actionPerformed(ActionEvent ae)
+ {
+ canceled = true;
+ }
+ });
+
+ pane.setOptions(new Object[] { cancelButton });
+
+ // FIXME: Internationalize the title
+ progressDialog = pane.createDialog(component, "Progress ...");
+ progressDialog.setModal(false);
+ progressDialog.setResizable(true);
+
+ progressDialog.pack();
+ progressDialog.setVisible(true);
+
+ }
+
+ /** An ActionListener implementation which does the measurements
+ * and estimations of the ProgressMonitor.
+ */
+ class TimerListener implements ActionListener
+ {
+ long timestamp;
+
+ int lastProgress;
+
+ boolean first = true;
+
+ TimerListener()
+ {
+ timestamp = System.currentTimeMillis();
+ }
+
+ public void actionPerformed(ActionEvent ae)
+ {
+ long now = System.currentTimeMillis();
+
+ if (first)
+ {
+ if ((now - timestamp) > millisToDecideToPopup)
+ {
+ first = false;
+
+
+ long expected = (progress - min == 0) ?
+ (now - timestamp) * (max - min) :
+ (now - timestamp) * (max - min) / (progress - min);
+
+ if (expected > millisToPopup)
+ {
+ createDialog();
+ }
+ }
+ else
+ {
+ // We have not waited long enough to make a decision,
+ // so return and try again when the timer is invoked.
+ return;
+ }
+ }
+ else if (progressDialog != null)
+ {
+ // The progress dialog is being displayed. We now calculate
+ // whether setting the progress bar to the current progress
+ // value would result in a visual difference.
+ int delta = progress - progressBar.getValue();
+
+ if ((delta * progressBar.getWidth() / (max - min)) > 0)
+ {
+ // At least one pixel would change.
+ progressBar.setValue(progress);
+ }
+ }
+ else
+ {
+ // No dialog necessary
+ timer.stop();
+ timer = null;
+ }
+
+ timestamp = now;
+ }
+ }
+
+ /**
+ * Gets the accessible context.
+ *
+ * @return the accessible context.
+ */
+ public AccessibleContext getAccessibleContext()
+ {
+ return accessibleContext;
+ }
+}
diff --git a/libjava/classpath/javax/swing/ProgressMonitorInputStream.java b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java
new file mode 100644
index 000000000..ba98bf68e
--- /dev/null
+++ b/libjava/classpath/javax/swing/ProgressMonitorInputStream.java
@@ -0,0 +1,249 @@
+/* ProgressMonitorInputStream.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+
+import java.io.FilterInputStream;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.IOException;
+
+/**
+ * An input stream with a {@link ProgressMonitor}.
+ *
+ * @author Andrew Selkirk
+ * @author Robert Schuster (robertschuster@fsfe.org)
+ * @status updated to 1.2
+ * @since 1.2
+ */
+public class ProgressMonitorInputStream extends FilterInputStream
+{
+
+ /**
+ * The monitor watching the progress of the input stream.
+ */
+ private ProgressMonitor monitor;
+
+ /**
+ * read
+ */
+ private int read;
+
+ /**
+ * Creates a new ProgressMonitorInputStream.
+ *
+ * @param component the parent component for the progress monitor dialog.
+ * @param message the task description.
+ * @param stream the underlying input stream.
+ */
+ public ProgressMonitorInputStream(Component component, Object message,
+ InputStream stream)
+ {
+ super(stream);
+
+ int max = 0;
+
+ try
+ {
+ max = stream.available();
+ }
+ catch ( IOException ioe )
+ {
+ // Behave like the JDK here.
+ }
+
+ monitor = new ProgressMonitor(component, message, null, 0, max);
+ }
+
+ /**
+ * Resets the input stream to the point where {@link #mark(int)} was called.
+ *
+ * @exception IOException TODO
+ */
+ public void reset() throws IOException
+ {
+ super.reset();
+
+ checkMonitorCanceled();
+
+ // TODO: The docs says the monitor should be resetted. But to which
+ // value? (mark is not overridden)
+ }
+
+ /**
+ * Reads an unsigned byte from the input stream and returns it as an
+ * int in the range of 0-255. Returns -1 if the end of the
+ * stream has been reached. The progress monitor is updated.
+ *
+ * @return int
+ *
+ * @exception IOException if there is a problem reading the stream.
+ */
+ public int read() throws IOException
+ {
+ int t = super.read();
+
+ monitor.setProgress(++read);
+
+ checkMonitorCanceled();
+
+ return t;
+ }
+
+ /**
+ * Reads bytes from the input stream and stores them in the supplied array,
+ * and updates the progress monitor (or closes it if the end of the stream
+ * is reached).
+ *
+ * @param data the data array for returning bytes read from the stream.
+ *
+ * @return The number of bytes read, or -1 if there are no more bytes in the
+ * stream.
+ *
+ * @throws IOException if there is a problem reading bytes from the stream.
+ */
+ public int read(byte[] data) throws IOException
+ {
+ int t = super.read(data);
+
+ if ( t > 0 )
+ {
+ read += t;
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+ }
+ else
+ {
+ monitor.close();
+ }
+
+ return t;
+ }
+
+ /**
+ * Reads up to length bytes from the input stream and stores
+ * them in the supplied array at the given index, and updates the progress
+ * monitor (or closes it if the end of the stream is reached).
+ *
+ * @param data the data array for returning bytes read from the stream.
+ * @param offset the offset into the array where the bytes should be written.
+ * @param length the maximum number of bytes to read from the stream.
+ *
+ * @return The number of bytes read, or -1 if there are no more bytes in the
+ * stream.
+ *
+ * @throws IOException if there is a problem reading bytes from the stream.
+ */
+ public int read(byte[] data, int offset, int length) throws IOException
+ {
+ int t = super.read(data, offset, length);
+
+ if ( t > 0 )
+ {
+ read += t;
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+ }
+ else
+ {
+ monitor.close();
+ }
+
+ return t;
+ }
+
+ /**
+ * Skips the specified number of bytes and updates the
+ * {@link ProgressMonitor}.
+ *
+ * @param length the number of bytes to skip.
+ *
+ * @return The actual number of bytes skipped.
+ *
+ * @throws IOException if there is a problem skipping bytes in the stream.
+ */
+ public long skip(long length) throws IOException
+ {
+ long t = super.skip(length);
+
+ // 'read' may overflow here in rare situations.
+ assert ( (long) read + t <= (long) Integer.MAX_VALUE );
+
+ read += (int) t;
+
+ monitor.setProgress(read);
+
+ checkMonitorCanceled();
+
+ return t;
+ }
+
+ /**
+ * Closes the input stream and the associated {@link ProgressMonitor}.
+ *
+ * @throws IOException if there is a problem closing the input stream.
+ */
+ public void close() throws IOException
+ {
+ super.close();
+ monitor.close();
+ }
+
+ /**
+ * Returns the {@link ProgressMonitor} used by this input stream.
+ *
+ * @return The progress monitor.
+ */
+ public ProgressMonitor getProgressMonitor()
+ {
+ return monitor;
+ }
+
+ private void checkMonitorCanceled() throws InterruptedIOException
+ {
+ if (monitor.isCanceled())
+ {
+ throw new InterruptedIOException("ProgressMonitor was canceled");
+ }
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/Renderer.java b/libjava/classpath/javax/swing/Renderer.java
new file mode 100644
index 000000000..4759c5b60
--- /dev/null
+++ b/libjava/classpath/javax/swing/Renderer.java
@@ -0,0 +1,68 @@
+/* Renderer.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+
+/**
+ * This interface is not used and exists only for compatibility.
+ * It probably has been replaced by {@link ListCellRenderer}, {@link
+ * javax.swing.table.TableCellRenderer} and {@link
+ * javax.swing.tree.TreeCellRenderer}.
+ *
+ * @specnote This interface is not used and exists only for compatibility.
+ *
+ * @author Andrew Selkirk
+ */
+public interface Renderer
+{
+ /**
+ * setValue
+ * @param value TODO
+ * @param selected TODO
+ */
+ void setValue(Object value, boolean selected);
+
+ /**
+ * getComponent
+ * @returns Component
+ */
+ Component getComponent();
+
+
+}
diff --git a/libjava/classpath/javax/swing/RepaintManager.java b/libjava/classpath/javax/swing/RepaintManager.java
new file mode 100644
index 000000000..23c05a262
--- /dev/null
+++ b/libjava/classpath/javax/swing/RepaintManager.java
@@ -0,0 +1,860 @@
+/* RepaintManager.java --
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import gnu.classpath.SystemProperties;
+import gnu.java.awt.LowPriorityEvent;
+
+import java.applet.Applet;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.EventQueue;
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.Window;
+import java.awt.event.InvocationEvent;
+import java.awt.image.VolatileImage;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ *
The repaint manager holds a set of dirty regions, invalid components,
+ * and a double buffer surface. The dirty regions and invalid components
+ * are used to coalesce multiple revalidate() and repaint() calls in the
+ * component tree into larger groups to be refreshed "all at once"; the
+ * double buffer surface is used by root components to paint
+ * themselves.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Audrius Meskauskas (audriusa@bioinformatics.org)
+ */
+public class RepaintManager
+{
+ /**
+ * An InvocationEvent subclass that implements LowPriorityEvent. This is used
+ * to defer the execution of RepaintManager requests as long as possible on
+ * the event queue. This way we make sure that all available input is
+ * processed before getting active with the RepaintManager. This allows
+ * for better optimization (more validate and repaint requests can be
+ * coalesced) and thus has a positive effect on performance for GUI
+ * applications under heavy load.
+ */
+ private static class RepaintWorkerEvent
+ extends InvocationEvent
+ implements LowPriorityEvent
+ {
+
+ /**
+ * Creates a new RepaintManager event.
+ *
+ * @param source the source
+ * @param runnable the runnable to execute
+ */
+ public RepaintWorkerEvent(Object source, Runnable runnable,
+ Object notifier, boolean catchEx)
+ {
+ super(source, runnable, notifier, catchEx);
+ }
+
+ /**
+ * An application that I met implements its own event dispatching and
+ * calls dispatch() via reflection, and only checks declared methods,
+ * that is, it expects this method to be in the event's class, not
+ * in a superclass. So I put this in here... sigh.
+ */
+ public void dispatch()
+ {
+ super.dispatch();
+ }
+ }
+
+ /**
+ * The current repaint managers, indexed by their ThreadGroups.
+ */
+ static WeakHashMap currentRepaintManagers;
+
+ /**
+ * A rectangle object to be reused in damaged regions calculation.
+ */
+ private static Rectangle rectCache = new Rectangle();
+
+ /**
+ *
A helper class which is placed into the system event queue at
+ * various times in order to facilitate repainting and layout. There is
+ * typically only one of these objects active at any time. When the
+ * {@link RepaintManager} is told to queue a repaint, it checks to see if
+ * a {@link RepaintWorker} is "live" in the system event queue, and if
+ * not it inserts one using {@link SwingUtilities#invokeLater}.
+ *
+ *
When the {@link RepaintWorker} comes to the head of the system
+ * event queue, its {@link RepaintWorker#run} method is executed by the
+ * swing paint thread, which revalidates all invalid components and
+ * repaints any damage in the swing scene.
+ */
+ private class RepaintWorker
+ implements Runnable
+ {
+
+ boolean live;
+
+ public RepaintWorker()
+ {
+ live = false;
+ }
+
+ public synchronized void setLive(boolean b)
+ {
+ live = b;
+ }
+
+ public synchronized boolean isLive()
+ {
+ return live;
+ }
+
+ public void run()
+ {
+ try
+ {
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager rm =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ rm.validateInvalidComponents();
+ rm.paintDirtyRegions();
+ }
+ finally
+ {
+ setLive(false);
+ }
+ }
+
+ }
+
+ /**
+ * A table storing the dirty regions of components. The keys of this
+ * table are components, the values are rectangles. Each component maps
+ * to exactly one rectangle. When more regions are marked as dirty on a
+ * component, they are union'ed with the existing rectangle.
+ *
+ * This is package private to avoid a synthetic accessor method in inner
+ * class.
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ private HashMap dirtyComponents;
+
+ /**
+ * The dirtyComponents which is used in paintDiryRegions to avoid unnecessary
+ * locking.
+ */
+ private HashMap dirtyComponentsWork;
+
+ /**
+ * A single, shared instance of the helper class. Any methods which mark
+ * components as invalid or dirty eventually activate this instance. It
+ * is added to the event queue if it is not already active, otherwise
+ * reused.
+ *
+ * @see #addDirtyRegion
+ * @see #addInvalidComponent
+ */
+ private RepaintWorker repaintWorker;
+
+ /**
+ * The set of components which need revalidation, in the "layout" sense.
+ * There is no additional information about "what kind of layout" they
+ * need (as there is with dirty regions), so it is just a vector rather
+ * than a table.
+ *
+ * @see #addInvalidComponent
+ * @see #removeInvalidComponent
+ * @see #validateInvalidComponents
+ */
+ private ArrayList invalidComponents;
+
+ /**
+ * Whether or not double buffering is enabled on this repaint
+ * manager. This is merely a hint to clients; the RepaintManager will
+ * always return an offscreen buffer when one is requested.
+ *
+ * @see #isDoubleBufferingEnabled
+ * @see #setDoubleBufferingEnabled
+ */
+ private boolean doubleBufferingEnabled;
+
+ /**
+ * The offscreen buffers. This map holds one offscreen buffer per
+ * Window/Applet and releases them as soon as the Window/Applet gets garbage
+ * collected.
+ */
+ private WeakHashMap offscreenBuffers;
+
+ /**
+ * The maximum width and height to allocate as a double buffer. Requests
+ * beyond this size are ignored.
+ *
+ * @see #paintDirtyRegions
+ * @see #getDoubleBufferMaximumSize
+ * @see #setDoubleBufferMaximumSize
+ */
+ private Dimension doubleBufferMaximumSize;
+
+
+ /**
+ * Create a new RepaintManager object.
+ */
+ public RepaintManager()
+ {
+ dirtyComponents = new HashMap();
+ dirtyComponentsWork = new HashMap();
+ invalidComponents = new ArrayList();
+ repaintWorker = new RepaintWorker();
+ doubleBufferMaximumSize = new Dimension(2000,2000);
+ doubleBufferingEnabled =
+ SystemProperties.getProperty("gnu.swing.doublebuffering", "true")
+ .equals("true");
+ offscreenBuffers = new WeakHashMap();
+ }
+
+ /**
+ * Returns the RepaintManager for the current thread's
+ * thread group. The default implementation ignores the
+ * component parameter and returns the same repaint manager
+ * for all components.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(Component component)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ RepaintManager currentManager =
+ (RepaintManager) currentRepaintManagers.get(threadGroup);
+ if (currentManager == null)
+ {
+ currentManager = new RepaintManager();
+ currentRepaintManagers.put(threadGroup, currentManager);
+ }
+ return currentManager;
+ }
+
+ /**
+ * Returns the RepaintManager for the current thread's
+ * thread group. The default implementation ignores the
+ * component parameter and returns the same repaint manager
+ * for all components.
+ *
+ * This method is only here for backwards compatibility with older versions
+ * of Swing and simply forwards to {@link #currentManager(Component)}.
+ *
+ * @param component a component to look up the manager of
+ *
+ * @return the current repaint manager for the calling thread's thread group
+ * and the specified component
+ *
+ * @see #setCurrentManager
+ */
+ public static RepaintManager currentManager(JComponent component)
+ {
+ return currentManager((Component)component);
+ }
+
+ /**
+ * Sets the repaint manager for the calling thread's thread group.
+ *
+ * @param manager the repaint manager to set for the current thread's thread
+ * group
+ *
+ * @see #currentManager(Component)
+ */
+ public static void setCurrentManager(RepaintManager manager)
+ {
+ if (currentRepaintManagers == null)
+ currentRepaintManagers = new WeakHashMap();
+
+ ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
+ currentRepaintManagers.put(threadGroup, manager);
+ }
+
+ /**
+ * Add a component to the {@link #invalidComponents} vector. If the
+ * {@link #repaintWorker} class is not active, insert it in the system
+ * event queue.
+ *
+ * @param component The component to add
+ *
+ * @see #removeInvalidComponent
+ */
+ public void addInvalidComponent(JComponent component)
+ {
+ Component validateRoot = null;
+ Component c = component;
+ while (c != null)
+ {
+ // Special cases we don't bother validating are when the invalidated
+ // component (or any of it's ancestors) is inside a CellRendererPane
+ // or if it doesn't have a peer yet (== not displayable).
+ if (c instanceof CellRendererPane || ! c.isDisplayable())
+ return;
+ if (c instanceof JComponent && ((JComponent) c).isValidateRoot())
+ {
+ validateRoot = c;
+ break;
+ }
+
+ c = c.getParent();
+ }
+
+ // If we didn't find a validate root, then we don't validate.
+ if (validateRoot == null)
+ return;
+
+ // Make sure the validate root and all of it's ancestors are visible.
+ c = validateRoot;
+ while (c != null)
+ {
+ if (! c.isVisible() || ! c.isDisplayable())
+ return;
+ c = c.getParent();
+ }
+
+ if (invalidComponents.contains(validateRoot))
+ return;
+
+ //synchronized (invalidComponents)
+ // {
+ invalidComponents.add(validateRoot);
+ // }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+
+ /**
+ * Remove a component from the {@link #invalidComponents} vector.
+ *
+ * @param component The component to remove
+ *
+ * @see #addInvalidComponent
+ */
+ public void removeInvalidComponent(JComponent component)
+ {
+ synchronized (invalidComponents)
+ {
+ invalidComponents.remove(component);
+ }
+ }
+
+ /**
+ * Add a region to the set of dirty regions for a specified component.
+ * This involves union'ing the new region with any existing dirty region
+ * associated with the component. If the {@link #repaintWorker} class
+ * is not active, insert it in the system event queue.
+ *
+ * @param component The component to add a dirty region for
+ * @param x The left x coordinate of the new dirty region
+ * @param y The top y coordinate of the new dirty region
+ * @param w The width of the new dirty region
+ * @param h The height of the new dirty region
+ *
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public void addDirtyRegion(JComponent component, int x, int y,
+ int w, int h)
+ {
+ if (w <= 0 || h <= 0 || !component.isShowing())
+ return;
+ component.computeVisibleRect(rectCache);
+ SwingUtilities.computeIntersection(x, y, w, h, rectCache);
+
+ if (! rectCache.isEmpty())
+ {
+ synchronized (dirtyComponents)
+ {
+ Rectangle dirtyRect = (Rectangle)dirtyComponents.get(component);
+ if (dirtyRect != null)
+ {
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y,
+ rectCache.width, rectCache.height,
+ dirtyRect);
+ }
+ else
+ {
+ dirtyComponents.put(component, rectCache.getBounds());
+ }
+ }
+
+ if (! repaintWorker.isLive())
+ {
+ repaintWorker.setLive(true);
+ invokeLater(repaintWorker);
+ }
+ }
+ }
+
+ /**
+ * Get the dirty region associated with a component, or null
+ * if the component has no dirty region.
+ *
+ * @param component The component to get the dirty region of
+ *
+ * @return The dirty region of the component
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ * @see #markCompletelyDirty
+ */
+ public Rectangle getDirtyRegion(JComponent component)
+ {
+ Rectangle dirty = (Rectangle) dirtyComponents.get(component);
+ if (dirty == null)
+ dirty = new Rectangle();
+ return dirty;
+ }
+
+ /**
+ * Mark a component as dirty over its entire bounds.
+ *
+ * @param component The component to mark as dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public void markCompletelyDirty(JComponent component)
+ {
+ addDirtyRegion(component, 0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * Remove all dirty regions for a specified component
+ *
+ * @param component The component to mark as clean
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyDirty
+ */
+ public void markCompletelyClean(JComponent component)
+ {
+ synchronized (dirtyComponents)
+ {
+ dirtyComponents.remove(component);
+ }
+ }
+
+ /**
+ * Return true if the specified component is completely
+ * contained within its dirty region, otherwise false
+ *
+ * @param component The component to check for complete dirtyness
+ *
+ * @return Whether the component is completely dirty
+ *
+ * @see #dirtyComponents
+ * @see #addDirtyRegion
+ * @see #getDirtyRegion
+ * @see #isCompletelyDirty
+ * @see #markCompletelyClean
+ */
+ public boolean isCompletelyDirty(JComponent component)
+ {
+ boolean dirty = false;
+ Rectangle r = getDirtyRegion(component);
+ if(r.width == Integer.MAX_VALUE && r.height == Integer.MAX_VALUE)
+ dirty = true;
+ return dirty;
+ }
+
+ /**
+ * Validate all components which have been marked invalid in the {@link
+ * #invalidComponents} vector.
+ */
+ public void validateInvalidComponents()
+ {
+ // We don't use an iterator here because that would fail when there are
+ // components invalidated during the validation of others, which happens
+ // quite frequently. Instead we synchronize the access a little more.
+ while (invalidComponents.size() > 0)
+ {
+ Component comp;
+ synchronized (invalidComponents)
+ {
+ comp = (Component) invalidComponents.remove(0);
+ }
+ // Validate the validate component.
+ if (! (comp.isVisible() && comp.isShowing()))
+ continue;
+ comp.validate();
+ }
+ }
+
+ /**
+ * Repaint all regions of all components which have been marked dirty in the
+ * {@link #dirtyComponents} table.
+ */
+ public void paintDirtyRegions()
+ {
+ // Short circuit if there is nothing to paint.
+ if (dirtyComponents.size() == 0)
+ return;
+
+ // Swap dirtyRegions with dirtyRegionsWork to avoid locking.
+ synchronized (dirtyComponents)
+ {
+ HashMap swap = dirtyComponents;
+ dirtyComponents = dirtyComponentsWork;
+ dirtyComponentsWork = swap;
+ }
+
+ // Compile a set of repaint roots.
+ HashSet repaintRoots = new HashSet();
+ Set components = dirtyComponentsWork.keySet();
+ for (Iterator i = components.iterator(); i.hasNext();)
+ {
+ JComponent dirty = (JComponent) i.next();
+ compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots);
+ }
+
+ for (Iterator i = repaintRoots.iterator(); i.hasNext();)
+ {
+ JComponent comp = (JComponent) i.next();
+ Rectangle damaged = (Rectangle) dirtyComponentsWork.remove(comp);
+ if (damaged == null || damaged.isEmpty())
+ continue;
+ comp.paintImmediately(damaged);
+ }
+ dirtyComponentsWork.clear();
+ }
+
+ /**
+ * Compiles a list of components that really get repainted. This is called
+ * once for each component in the dirtyRegions HashMap, each time with
+ * another dirty parameter. This searches up the component
+ * hierarchy of dirty to find the highest parent that is also
+ * marked dirty and merges the dirty regions.
+ *
+ * @param dirtyRegions the dirty regions
+ * @param dirty the component for which to find the repaint root
+ * @param roots the list to which new repaint roots get appended
+ */
+ private void compileRepaintRoots(HashMap dirtyRegions, JComponent dirty,
+ HashSet roots)
+ {
+ Component current = dirty;
+ Component root = dirty;
+
+ // This will contain the dirty region in the root coordinate system,
+ // possibly clipped by ancestor's bounds.
+ Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty);
+ rectCache.setBounds(originalDirtyRect);
+
+ // The bounds of the current component.
+ int x = dirty.getX();
+ int y = dirty.getY();
+ int w = dirty.getWidth();
+ int h = dirty.getHeight();
+
+ // Do nothing if dirty region is clipped away by the component's bounds.
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+ if (rectCache.isEmpty())
+ return;
+
+ // The cumulated offsets.
+ int dx = 0;
+ int dy = 0;
+ // The actual offset for the found root.
+ int rootDx = 0;
+ int rootDy = 0;
+
+ // Search the highest component that is also marked dirty.
+ Component parent;
+ while (true)
+ {
+ parent = current.getParent();
+ if (parent == null || !(parent instanceof JComponent))
+ break;
+
+ current = parent;
+ // Update the offset.
+ dx += x;
+ dy += y;
+ rectCache.x += x;
+ rectCache.y += y;
+
+ x = current.getX();
+ y = current.getY();
+ w = current.getWidth();
+ h = current.getHeight();
+ rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache);
+
+ // Don't paint if the dirty regions is clipped away by any of
+ // its ancestors.
+ if (rectCache.isEmpty())
+ return;
+
+ // We can skip to the next up when this parent is not dirty.
+ if (dirtyRegions.containsKey(parent))
+ {
+ root = current;
+ rootDx = dx;
+ rootDy = dy;
+ }
+ }
+
+ // Merge the rectangles of the root and the requested component if
+ // the are different.
+ if (root != dirty)
+ {
+ rectCache.x += rootDx - dx;
+ rectCache.y += rootDy - dy;
+ Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root);
+ SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width,
+ rectCache.height, dirtyRect);
+ }
+
+ // Adds the root to the roots set.
+ if (! roots.contains(root))
+ roots.add(root);
+ }
+
+ /**
+ * Get an offscreen buffer for painting a component's image. This image
+ * may be smaller than the proposed dimensions, depending on the value of
+ * the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param component The component to return an offscreen buffer for
+ * @param proposedWidth The proposed width of the offscreen buffer
+ * @param proposedHeight The proposed height of the offscreen buffer
+ *
+ * @return A shared offscreen buffer for painting
+ */
+ public Image getOffscreenBuffer(Component component, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(component);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight)
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = component.createImage(width, height);
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+ /**
+ * Blits the back buffer of the specified root component to the screen.
+ * This is package private because it must get called by JComponent.
+ *
+ * @param comp the component to be painted
+ * @param x the area to paint on screen, in comp coordinates
+ * @param y the area to paint on screen, in comp coordinates
+ * @param w the area to paint on screen, in comp coordinates
+ * @param h the area to paint on screen, in comp coordinates
+ */
+ void commitBuffer(Component comp, int x, int y, int w, int h)
+ {
+ Component root = comp;
+ while (root != null
+ && ! (root instanceof Window || root instanceof Applet))
+ {
+ x += root.getX();
+ y += root.getY();
+ root = root.getParent();
+ }
+
+ if (root != null)
+ {
+ Graphics g = root.getGraphics();
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer != null)
+ {
+ // Make sure we have a sane clip at this point.
+ g.clipRect(x, y, w, h);
+ g.drawImage(buffer, 0, 0, root);
+ g.dispose();
+ }
+ }
+ }
+
+ /**
+ * Creates and returns a volatile offscreen buffer for the specified
+ * component that can be used as a double buffer. The returned image
+ * is a {@link VolatileImage}. Its size will be (proposedWidth,
+ * proposedHeight) except when the maximum double buffer size
+ * has been set in this RepaintManager.
+ *
+ * @param comp the Component for which to create a volatile buffer
+ * @param proposedWidth the proposed width of the buffer
+ * @param proposedHeight the proposed height of the buffer
+ *
+ * @since 1.4
+ *
+ * @see VolatileImage
+ */
+ public Image getVolatileOffscreenBuffer(Component comp, int proposedWidth,
+ int proposedHeight)
+ {
+ Component root = SwingUtilities.getWindowAncestor(comp);
+ Image buffer = (Image) offscreenBuffers.get(root);
+ if (buffer == null
+ || buffer.getWidth(null) < proposedWidth
+ || buffer.getHeight(null) < proposedHeight
+ || !(buffer instanceof VolatileImage))
+ {
+ int width = Math.max(proposedWidth, root.getWidth());
+ width = Math.min(doubleBufferMaximumSize.width, width);
+ int height = Math.max(proposedHeight, root.getHeight());
+ height = Math.min(doubleBufferMaximumSize.height, height);
+ buffer = root.createVolatileImage(width, height);
+ if (buffer != null)
+ offscreenBuffers.put(root, buffer);
+ }
+ return buffer;
+ }
+
+
+ /**
+ * Get the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferMaximumSize
+ */
+ public Dimension getDoubleBufferMaximumSize()
+ {
+ return doubleBufferMaximumSize;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferMaximumSize} property.
+ *
+ * @param size The new value of the property
+ *
+ * @see #getDoubleBufferMaximumSize
+ */
+ public void setDoubleBufferMaximumSize(Dimension size)
+ {
+ doubleBufferMaximumSize = size;
+ }
+
+ /**
+ * Set the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @param buffer The new value of the property
+ *
+ * @see #isDoubleBufferingEnabled
+ */
+ public void setDoubleBufferingEnabled(boolean buffer)
+ {
+ doubleBufferingEnabled = buffer;
+ }
+
+ /**
+ * Get the value of the {@link #doubleBufferingEnabled} property.
+ *
+ * @return The current value of the property
+ *
+ * @see #setDoubleBufferingEnabled
+ */
+ public boolean isDoubleBufferingEnabled()
+ {
+ return doubleBufferingEnabled;
+ }
+
+ public String toString()
+ {
+ return "RepaintManager";
+ }
+
+ /**
+ * Sends an RepaintManagerEvent to the event queue with the specified
+ * runnable. This is similar to SwingUtilities.invokeLater(), only that the
+ * event is a low priority event in order to defer the execution a little
+ * more.
+ */
+ private void invokeLater(Runnable runnable)
+ {
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ EventQueue evQueue = tk.getSystemEventQueue();
+ InvocationEvent ev = new RepaintWorkerEvent(evQueue, runnable, null, false);
+ evQueue.postEvent(ev);
+ }
+}
diff --git a/libjava/classpath/javax/swing/RootPaneContainer.java b/libjava/classpath/javax/swing/RootPaneContainer.java
new file mode 100644
index 000000000..4b1bece21
--- /dev/null
+++ b/libjava/classpath/javax/swing/RootPaneContainer.java
@@ -0,0 +1,96 @@
+/* RootPaneContainer.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+
+/**
+ * Components that contain a single {@link JRootPane} as only child
+ * implement this interface, typically this is implemented by the
+ * Swing top-level containers.
+ *
+ * @author Andrew Selkirk
+ */
+public interface RootPaneContainer
+{
+
+ /**
+ * getRootPane
+ * @returns JRootPane
+ */
+ JRootPane getRootPane();
+
+ /**
+ * setContentPane
+ * @param contentPane TODO
+ */
+ void setContentPane(Container contentPane);
+
+ /**
+ * getContentPane
+ * @returns Container
+ */
+ Container getContentPane();
+
+ /**
+ * setLayeredPane
+ * @param layeredPane TODO
+ */
+ void setLayeredPane(JLayeredPane layeredPane);
+
+ /**
+ * getLayeredPane
+ * @returns JLayeredPane
+ */
+ JLayeredPane getLayeredPane();
+
+ /**
+ * setGlassPane
+ * @param glassPane TODO
+ */
+ void setGlassPane(Component glassPane);
+
+ /**
+ * getGlassPane
+ * @returns Component
+ */
+ Component getGlassPane();
+
+}
diff --git a/libjava/classpath/javax/swing/ScrollPaneConstants.java b/libjava/classpath/javax/swing/ScrollPaneConstants.java
new file mode 100644
index 000000000..b5860609f
--- /dev/null
+++ b/libjava/classpath/javax/swing/ScrollPaneConstants.java
@@ -0,0 +1,152 @@
+/* ScrollPaneConstants.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+/**
+ * Defines some constants that are used in {@link JScrollPane} and related
+ * components.
+ *
+ * @author Andrew Selkirk
+ */
+public interface ScrollPaneConstants
+{
+ /**
+ * VIEWPORT
+ */
+ String VIEWPORT = "VIEWPORT";
+
+ /**
+ * VERTICAL_SCROLLBAR
+ */
+ String VERTICAL_SCROLLBAR = "VERTICAL_SCROLLBAR";
+
+ /**
+ * HORIZONTAL_SCROLLBAR
+ */
+ String HORIZONTAL_SCROLLBAR = "HORIZONTAL_SCROLLBAR";
+
+ /**
+ * ROW_HEADER
+ */
+ String ROW_HEADER = "ROW_HEADER";
+
+ /**
+ * COLUMN_HEADER
+ */
+ String COLUMN_HEADER = "COLUMN_HEADER";
+
+ /**
+ * LOWER_LEFT_CORNER
+ */
+ String LOWER_LEFT_CORNER = "LOWER_LEFT_CORNER";
+
+ /**
+ * LOWER_RIGHT_CORNER
+ */
+ String LOWER_RIGHT_CORNER = "LOWER_RIGHT_CORNER";
+
+ /**
+ * UPPER_LEFT_CORNER
+ */
+ String UPPER_LEFT_CORNER = "UPPER_LEFT_CORNER";
+
+ /**
+ * UPPER_RIGHT_CORNER
+ */
+ String UPPER_RIGHT_CORNER = "UPPER_RIGHT_CORNER";
+
+ /**
+ * LOWER_LEADING_CORNER
+ */
+ String LOWER_LEADING_CORNER = "LOWER_LEADING_CORNER";
+
+ /**
+ * LOWER_TRAILING_CORNER
+ */
+ String LOWER_TRAILING_CORNER = "LOWER_TRAILING_CORNER";
+
+ /**
+ * UPPER_LEADING_CORNER
+ */
+ String UPPER_LEADING_CORNER = "UPPER_LEADING_CORNER";
+
+ /**
+ * UPPER_TRAILING_CORNER
+ */
+ String UPPER_TRAILING_CORNER = "UPPER_TRAILING_CORNER";
+
+ /**
+ * VERTICAL_SCROLLBAR_POLICY
+ */
+ String VERTICAL_SCROLLBAR_POLICY = "VERTICAL_SCROLLBAR_POLICY";
+
+ /**
+ * HORIZONTAL_SCROLLBAR_POLICY
+ */
+ String HORIZONTAL_SCROLLBAR_POLICY = "HORIZONTAL_SCROLLBAR_POLICY";
+
+ /**
+ * VERTICAL_SCROLLBAR_AS_NEEDED
+ */
+ int VERTICAL_SCROLLBAR_AS_NEEDED = 20;
+
+ /**
+ * VERTICAL_SCROLLBAR_NEVER
+ */
+ int VERTICAL_SCROLLBAR_NEVER = 21;
+
+ /**
+ * VERTICAL_SCROLLBAR_ALWAYS
+ */
+ int VERTICAL_SCROLLBAR_ALWAYS = 22;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_AS_NEEDED
+ */
+ int HORIZONTAL_SCROLLBAR_AS_NEEDED = 30;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_NEVER
+ */
+ int HORIZONTAL_SCROLLBAR_NEVER = 31;
+
+ /**
+ * HORIZONTAL_SCROLLBAR_ALWAYS
+ */
+ int HORIZONTAL_SCROLLBAR_ALWAYS = 32;
+}
diff --git a/libjava/classpath/javax/swing/ScrollPaneLayout.java b/libjava/classpath/javax/swing/ScrollPaneLayout.java
new file mode 100644
index 000000000..fa1743bed
--- /dev/null
+++ b/libjava/classpath/javax/swing/ScrollPaneLayout.java
@@ -0,0 +1,491 @@
+/* ScrollPaneLayout.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+import java.awt.Rectangle;
+import java.io.Serializable;
+
+import javax.swing.border.Border;
+
+/**
+ * ScrollPaneLayout
+ * @author Andrew Selkirk
+ * @version 1.0
+ */
+public class ScrollPaneLayout
+ implements LayoutManager, ScrollPaneConstants, Serializable
+{
+ private static final long serialVersionUID = -4480022884523193743L;
+
+ public static class UIResource extends ScrollPaneLayout
+ implements javax.swing.plaf.UIResource
+ {
+ public UIResource()
+ {
+ super();
+ }
+ }
+
+ protected JViewport viewport;
+ protected JScrollBar vsb;
+ protected JScrollBar hsb;
+ protected JViewport rowHead;
+ protected JViewport colHead;
+ protected Component lowerLeft;
+ protected Component lowerRight;
+ protected Component upperLeft;
+ protected Component upperRight;
+ protected int vsbPolicy;
+ protected int hsbPolicy;
+
+ public ScrollPaneLayout()
+ {
+ // Nothing to do here.
+ }
+
+ public void syncWithScrollPane(JScrollPane scrollPane)
+ {
+ viewport = scrollPane.getViewport();
+ rowHead = scrollPane.getRowHeader();
+ colHead = scrollPane.getColumnHeader();
+ vsb = scrollPane.getVerticalScrollBar();
+ hsb = scrollPane.getHorizontalScrollBar();
+ vsbPolicy = scrollPane.getVerticalScrollBarPolicy();
+ hsbPolicy = scrollPane.getHorizontalScrollBarPolicy();
+ lowerLeft = scrollPane.getCorner(LOWER_LEFT_CORNER);
+ lowerRight = scrollPane.getCorner(LOWER_RIGHT_CORNER);
+ upperLeft = scrollPane.getCorner(UPPER_LEFT_CORNER);
+ upperRight = scrollPane.getCorner(UPPER_RIGHT_CORNER);
+ }
+
+ /**
+ * Removes an existing component. If oldComponent is not null
+ * and is not equal to newComponent, oldComponent must be removed
+ * from its parent.
+ * @param oldComponent the old Component that may need to be removed.
+ * @param newComponent the Component to add.
+ * @return the newComponent
+ */
+ protected Component addSingletonComponent(Component oldComponent,
+ Component newComponent)
+ {
+ if (oldComponent != null && oldComponent != newComponent)
+ oldComponent.getParent().remove(oldComponent);
+ return newComponent;
+ }
+
+ /**
+ * Add the specified component to the layout.
+ * @param key must be one of VIEWPORT, VERTICAL_SCROLLBAR,
+ * HORIZONTAL_SCROLLBAR, ROW_HEADER, COLUMN_HEADER,
+ * LOWER_RIGHT_CORNER, LOWER_LEFT_CORNER, UPPER_RIGHT_CORNER,
+ * UPPER_LEFT_CORNER.
+ * @param component the Component to add
+ * @throws IllegalArgumentException if key is not as above
+ */
+ public void addLayoutComponent(String key, Component component)
+ {
+ if (key == VIEWPORT)
+ viewport = (JViewport) component;
+ else if (key == VERTICAL_SCROLLBAR)
+ vsb = (JScrollBar) component;
+ else if (key == HORIZONTAL_SCROLLBAR)
+ hsb = (JScrollBar) component;
+ else if (key == ROW_HEADER)
+ rowHead = (JViewport) component;
+ else if (key == COLUMN_HEADER)
+ colHead = (JViewport) component;
+ else if (key == LOWER_RIGHT_CORNER)
+ lowerRight = component;
+ else if (key == UPPER_RIGHT_CORNER)
+ upperRight = component;
+ else if (key == LOWER_LEFT_CORNER)
+ lowerLeft = component;
+ else if (key == UPPER_LEFT_CORNER)
+ upperLeft = component;
+ else
+ throw new IllegalArgumentException();
+ }
+
+ public void removeLayoutComponent(Component component)
+ {
+ if (component == viewport)
+ viewport = null;
+ else if (component == vsb)
+ vsb = null;
+ else if (component == hsb)
+ hsb = null;
+ else if (component == rowHead)
+ rowHead = null;
+ else if (component == colHead)
+ colHead = null;
+ else if (component == lowerRight)
+ lowerRight = null;
+ else if (component == upperRight)
+ upperRight = null;
+ else if (component == lowerLeft)
+ lowerLeft = null;
+ else if (component == upperLeft)
+ upperLeft = null;
+ }
+
+ public int getVerticalScrollBarPolicy()
+ {
+ return vsbPolicy;
+ }
+
+ /**
+ * Sets the vertical scrollbar policy.
+ * @param policy must be one of VERTICAL_SCROLLBAR_AS_NEEDED,
+ * VERTICAL_SCROLLBAR_NEVER, VERTICAL_SCROLLBAR_ALWAYS.
+ * @throws IllegalArgumentException if policy is not one of the valid
+ * JScrollBar policies.
+ */
+ public void setVerticalScrollBarPolicy(int policy)
+ {
+ if (policy != VERTICAL_SCROLLBAR_AS_NEEDED &&
+ policy != VERTICAL_SCROLLBAR_NEVER &&
+ policy != VERTICAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("Illegal Scrollbar Policy");
+ vsbPolicy = policy;
+ }
+
+ public int getHorizontalScrollBarPolicy()
+ {
+ return hsbPolicy;
+ }
+
+ /**
+ * Sets the horizontal scrollbar policy.
+ * @param policy must be one of HORIZONTAL_SCROLLBAR_AS_NEEDED,
+ * HORIZONTAL_SCROLLBAR_NEVER, HORIZONTAL_SCROLLBAR_ALWAYS.
+ * @throws IllegalArgumentException if policy is not one of the valid
+ * JScrollbar policies.
+ */
+ public void setHorizontalScrollBarPolicy(int policy)
+ {
+ if (policy != HORIZONTAL_SCROLLBAR_AS_NEEDED &&
+ policy != HORIZONTAL_SCROLLBAR_NEVER &&
+ policy != HORIZONTAL_SCROLLBAR_ALWAYS)
+ throw new IllegalArgumentException("Illegal Scrollbar Policy");
+ hsbPolicy = policy;
+ }
+
+ public JViewport getViewport()
+ {
+ return viewport;
+ }
+
+ public JScrollBar getHorizontalScrollBar()
+ {
+ return hsb;
+ }
+
+ public JScrollBar getVerticalScrollBar()
+ {
+ return vsb;
+ }
+
+ public JViewport getRowHeader()
+ {
+ return rowHead;
+ }
+
+ public JViewport getColumnHeader()
+ {
+ return colHead;
+ }
+
+ /**
+ * Returns the Component at the specified corner.
+ * @param key the corner.
+ * @return the Component at the specified corner, or null if
+ * key is not one of the four valid corners.
+ */
+ public Component getCorner(String key)
+ {
+ if (key == LOWER_RIGHT_CORNER)
+ return lowerRight;
+ else if (key == UPPER_RIGHT_CORNER)
+ return upperRight;
+ else if (key == LOWER_LEFT_CORNER)
+ return lowerLeft;
+ else if (key == UPPER_LEFT_CORNER)
+ return upperLeft;
+ return null;
+ }
+
+ public Dimension preferredLayoutSize(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ Dimension viewportSize = viewport.getPreferredSize();
+ Dimension viewSize = viewport.getViewSize();
+ int width = viewportSize.width;
+ int height = viewportSize.height;
+
+ // horizontal scrollbar needed if the view's preferred width
+ // is larger than the viewport's preferred width
+ if (hsb != null && viewSize.width > viewportSize.width)
+ height += hsb.getPreferredSize().height;
+
+ // vertical scrollbar needed if the view's preferred height
+ // is larger than the viewport's preferred height
+ if (vsb != null && viewSize.height > viewportSize.height)
+ width += vsb.getPreferredSize().width;
+ if (rowHead != null && rowHead.isVisible())
+ width += rowHead.getPreferredSize().width;
+ if (colHead != null && colHead.isVisible())
+ height += colHead.getPreferredSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ Insets i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
+ Insets i = sc.getInsets();
+ return new Dimension(width + i.left + i.right,
+ height + i.left + i.right);
+ }
+
+ public Dimension minimumLayoutSize(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ Insets i = sc.getInsets();
+ Dimension viewportMinSize = sc.getViewport().getMinimumSize();
+
+ int width = i.left + i.right + viewportMinSize.width;
+ if (sc.getVerticalScrollBarPolicy()
+ != JScrollPane.VERTICAL_SCROLLBAR_NEVER)
+ width += sc.getVerticalScrollBar().getMinimumSize().width;
+
+ int height = i.top + i.bottom + viewportMinSize.height;
+ if (sc.getHorizontalScrollBarPolicy()
+ != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER)
+ height += sc.getHorizontalScrollBar().getMinimumSize().height;
+
+ // Add insets of viewportBorder if present.
+ Border vpBorder = sc.getViewportBorder();
+ if (vpBorder != null)
+ {
+ i = vpBorder.getBorderInsets(sc);
+ width += i.left + i.right;
+ height += i.top + i.bottom;
+ }
+
+ return new Dimension(width, height);
+ }
+
+ /**
+ *
+ * +----+--------------------+----+ y1
+ * | c1 | column header | c2 |
+ * +----+--------------------+----+ y2
+ * | r | | v |
+ * | o | | |
+ * | w | | s |
+ * | | | r |
+ * | h | | o |
+ * | e | viewport | l |
+ * | a | | l |
+ * | d | | b |
+ * | e | | a |
+ * | r | | r |
+ * +----+--------------------+----+ y3
+ * | c3 | h scrollbar | c4 |
+ * +----+--------------------+----+ y4
+ * x1 x2 x3 x4
+ *
+ */
+ public void layoutContainer(Container parent)
+ {
+ // Sun's implementation simply throws a ClassCastException if
+ // parent is no JScrollPane, so do we.
+ JScrollPane sc = (JScrollPane) parent;
+ JViewport viewport = sc.getViewport();
+ Component view = viewport.getView();
+
+ // If there is no view in the viewport, there is no work to be done.
+ if (view == null)
+ return;
+
+ Dimension viewSize = viewport.getView().getPreferredSize();
+
+ int x1 = 0, x2 = 0, x3 = 0, x4 = 0;
+ int y1 = 0, y2 = 0, y3 = 0, y4 = 0;
+ Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null);
+
+ // If there is a viewportBorder, remove its insets from the available
+ // space.
+ Border vpBorder = sc.getViewportBorder();
+ Insets vpi;
+ if (vpBorder != null)
+ vpi = vpBorder.getBorderInsets(sc);
+ else
+ vpi = new Insets(0, 0, 0, 0);
+
+ x1 = scrollPaneBounds.x;
+ y1 = scrollPaneBounds.y;
+ x4 = scrollPaneBounds.x + scrollPaneBounds.width;
+ y4 = scrollPaneBounds.y + scrollPaneBounds.height;
+ if (colHead != null)
+ y2 = y1 + colHead.getPreferredSize().height;
+ else
+ y2 = y1;
+
+ if (rowHead != null)
+ x2 = x1 + rowHead.getPreferredSize().width;
+ else
+ x2 = x1;
+
+ int vsbPolicy = sc.getVerticalScrollBarPolicy();
+ int hsbPolicy = sc.getHorizontalScrollBarPolicy();
+
+ int vsWidth = 0;
+ int hsHeight = 0;
+
+ boolean showVsb =
+ (vsb != null)
+ && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS)
+ || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
+ && viewSize.height > (y4 - y2)));
+
+ if (showVsb)
+ vsWidth = vsb.getPreferredSize().width;
+
+ // The horizontal scroll bar may become necessary if the vertical scroll
+ // bar appears, reducing the space, left for the component.
+
+ boolean showHsb =
+ (hsb != null)
+ && ((hsbPolicy == HORIZONTAL_SCROLLBAR_ALWAYS)
+ || (hsbPolicy == HORIZONTAL_SCROLLBAR_AS_NEEDED
+ && viewSize.width > (x4 - x2 - vsWidth)));
+
+ if (showHsb)
+ hsHeight = hsb.getPreferredSize().height;
+
+ // If the horizontal scroll bar appears, and the vertical scroll bar
+ // was not necessary assuming that there is no horizontal scroll bar,
+ // the vertical scroll bar may become necessary because the horizontal
+ // scroll bar reduces the vertical space for the component.
+ if (!showVsb)
+ {
+ showVsb =
+ (vsb != null)
+ && ((vsbPolicy == VERTICAL_SCROLLBAR_ALWAYS)
+ || (vsbPolicy == VERTICAL_SCROLLBAR_AS_NEEDED
+ && viewSize.height > (y4 - y2)));
+
+ if (showVsb)
+ vsWidth = vsb.getPreferredSize().width;
+ }
+
+ x3 = x4 - vsWidth;
+ y3 = y4 - hsHeight;
+
+ // now set the layout
+ if (viewport != null)
+ viewport.setBounds(new Rectangle(x2 + vpi.left, y2 + vpi.top,
+ x3 - x2 - vpi.left - vpi.right,
+ y3 - y2 - vpi.top - vpi.bottom));
+
+ if (colHead != null)
+ colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1));
+
+ if (rowHead != null)
+ rowHead.setBounds(new Rectangle(x1, y2, x2 - x1, y3 - y2));
+
+ if (showVsb)
+ {
+ vsb.setVisible(true);
+ vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2 ));
+ }
+ else if (vsb != null)
+ vsb.setVisible(false);
+
+ if (showHsb)
+ {
+ hsb.setVisible(true);
+ hsb.setBounds(new Rectangle(x2 , y3, x3 - x2, y4 - y3));
+ }
+ else if (hsb != null)
+ hsb.setVisible(false);
+
+ if (upperLeft != null)
+ upperLeft.setBounds(new Rectangle(x1, y1, x2 - x1, y2 - y1));
+
+ if (upperRight != null)
+ upperRight.setBounds(new Rectangle(x3, y1, x4 - x3, y2 - y1));
+
+ if (lowerLeft != null)
+ lowerLeft.setBounds(new Rectangle(x1, y3, x2 - x1, y4 - y3));
+
+ if (lowerRight != null)
+ lowerRight.setBounds(new Rectangle(x3, y3, x4 - x3, y4 - y3));
+ }
+
+ /**
+ * Returns the bounds of the border around a ScrollPane's viewport.
+ *
+ * @param scrollPane the ScrollPane for which's viewport the border
+ * is requested
+ *
+ * @deprecated As of Swing 1.1 replaced by
+ * {@link javax.swing.JScrollPane#getViewportBorderBounds}.
+ */
+ public Rectangle getViewportBorderBounds(JScrollPane scrollPane)
+ {
+ return null;
+ }
+
+
+}
diff --git a/libjava/classpath/javax/swing/Scrollable.java b/libjava/classpath/javax/swing/Scrollable.java
new file mode 100644
index 000000000..26fae2429
--- /dev/null
+++ b/libjava/classpath/javax/swing/Scrollable.java
@@ -0,0 +1,106 @@
+/* Scrollable.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+
+/**
+ * Defines the method that a component should implement to behave nicely
+ * in {@link JScrollPane}s. Note that this is not required for a component
+ * to be used in a JScrollPane, but can highly improve the
+ * user experience when scrolling the component.
+ */
+public interface Scrollable
+{
+ Dimension getPreferredScrollableViewportSize();
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given
+ * scrolling direction and orientation when scrolling in small amounts
+ * like table lines.
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @return the preferred scrolling distance, negative if up or left.
+ */
+ int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
+ int direction);
+
+ /**
+ * Return the preferred scrolling amount (in pixels) for the given
+ * scrolling direction and orientation when scrolling in large amounts
+ * (pages).
+ *
+ * @param visibleRect the currently visible part of the component.
+ * @param orientation the scrolling orientation
+ * @param direction the scrolling direction (negative - up, positive -down).
+ * The values greater than one means that more mouse wheel or similar
+ * events were generated, and hence it is better to scroll the longer
+ * distance.
+ *
+ * @return the preferred scrolling distance, negative if up or left.
+ */
+ int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
+ int direction);
+
+ /**
+ * Return true if the width of the scrollable is always equal to the
+ * view, where it is displayed, width (for instance, the text area with
+ * the word wrap). In such case, the horizontal scrolling should not be
+ * performed.
+ *
+ * @return true is no horizontal scrolling is assumed, faster otherwise.
+ */
+ boolean getScrollableTracksViewportWidth();
+
+ /**
+ * Return true if the height of the scrollable is always equal to the view,
+ * where it is displayed, height.In such case, the vertical scrolling should
+ * not be performed.
+ *
+ * @return true is no horizontal scrolling is assumed, faster otherwise.
+ */
+ boolean getScrollableTracksViewportHeight();
+
+}
diff --git a/libjava/classpath/javax/swing/SingleSelectionModel.java b/libjava/classpath/javax/swing/SingleSelectionModel.java
new file mode 100644
index 000000000..2a76ff389
--- /dev/null
+++ b/libjava/classpath/javax/swing/SingleSelectionModel.java
@@ -0,0 +1,103 @@
+/* SingleSelectionModel.java --
+ Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+/**
+ * A data model that is used in components that support at most one
+ * selected element, like {@link JTabbedPane}, {@link JMenu} and
+ * {@link JPopupMenu}.
+ *
+ * @author Andrew Selkirk
+ */
+public interface SingleSelectionModel
+{
+ /**
+ * Returns the selected index or -1 if there is no selection.
+ *
+ * @return The selected index.
+ *
+ * @see #setSelectedIndex(int)
+ */
+ int getSelectedIndex();
+
+ /**
+ * Sets the selected index and, if this is different to the previous
+ * selection, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param index the index (use -1 to represent no selection).
+ *
+ * @see #getSelectedIndex()
+ * @see #clearSelection
+ */
+ void setSelectedIndex(int index);
+
+ /**
+ * Clears the selection by setting the selected index to -1 and
+ * sends a {@link ChangeEvent} to all registered listeners. If the selected
+ * index is already -1, this method does nothing.
+ */
+ void clearSelection();
+
+ /**
+ * Returns true if there is a selection, and false
+ * otherwise.
+ *
+ * @return A boolean.
+ */
+ boolean isSelected();
+
+ /**
+ * Registers a listener to receive {@link ChangeEvent} notifications from
+ * this model whenever the selected index changes.
+ *
+ * @param listener the listener to add.
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Deregisters a listener so that it no longer receives {@link ChangeEvent}
+ * notifications from this model.
+ *
+ * @param listener the listener to remove.
+ */
+ void removeChangeListener(ChangeListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/SizeRequirements.java b/libjava/classpath/javax/swing/SizeRequirements.java
new file mode 100644
index 000000000..1ee0e285c
--- /dev/null
+++ b/libjava/classpath/javax/swing/SizeRequirements.java
@@ -0,0 +1,523 @@
+/* SizeRequirements.java --
+ Copyright (C) 2002, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+
+/**
+ * This class calculates information about the size and position requirements
+ * of components.
+ *
+ * Two types of layout are supported:
+ *
+ *
Tiled: the components are placed at position top-left or bottom-right
+ * position within their allocated space
+ *
Aligned: the components are placed aligned in their allocated space
+ * according to their alignment value
+ *
+ *
+ * @author Andrew Selkirk
+ * @author Roman Kennke (roman@kennke.org)
+ */
+public class SizeRequirements implements Serializable
+{
+ /**
+ * The serialVersionUID.
+ */
+ private static final long serialVersionUID = 9217749429906736553L;
+
+ /**
+ * The minimum reasonable width or height of a component.
+ */
+ public int minimum;
+
+ /**
+ * The preferred width or height of a component.
+ */
+ public int preferred;
+
+ /**
+ * The maximum reasonable width or height of a component.
+ */
+ public int maximum;
+
+ /**
+ * The horizontal or vertical alignment of a component.
+ */
+ public float alignment;
+
+ /**
+ * Creates a SizeRequirements object with minimum, preferred and
+ * maximum size set to zero, and an alignment value of 0.5.
+ */
+ public SizeRequirements()
+ {
+ this (0, 0, 0, 0.5F);
+ }
+
+ /**
+ * Creates a SizeRequirements object with the specified minimum,
+ * preferred, maximum and alignment values.
+ *
+ * @param min the minimum reasonable size of the component
+ * @param pref the preferred size of the component
+ * @param max the maximum size of the component
+ * @param align the alignment of the component
+ */
+ public SizeRequirements(int min, int pref, int max, float align)
+ {
+ minimum = min;
+ preferred = pref;
+ maximum = max;
+ alignment = align;
+ }
+
+ /**
+ * Returns a String representation of this SizeRequirements object,
+ * containing information about the minimum, preferred, maximum and
+ * alignment value.
+ *
+ * @return a String representation of this SizeRequirements object
+ */
+ public String toString()
+ {
+ StringBuilder b = new StringBuilder();
+ b.append("<[");
+ b.append(minimum);
+ b.append(',');
+ b.append(preferred);
+ b.append(',');
+ b.append(maximum);
+ b.append("]@");
+ b.append(alignment);
+ b.append('>');
+ return b.toString();
+ }
+
+ /**
+ * Calculates how much space is nessecary to place a set of components
+ * end-to-end. The size requirements of the components is specified
+ * in children.
+ *
+ * @param children the SizeRequirements of each of the components
+ *
+ * @return the SizeRequirements that describe how much space is needed
+ * to place the components end-to-end
+ */
+ public static SizeRequirements
+ getTiledSizeRequirements(SizeRequirements[] children)
+ {
+ long minimum = 0;
+ long preferred = 0;
+ long maximum = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ minimum += children[i].minimum;
+ preferred += children[i].preferred;
+ maximum += children[i].maximum;
+ }
+ // Overflow check.
+ if (minimum > Integer.MAX_VALUE)
+ minimum = Integer.MAX_VALUE;
+ if (preferred > Integer.MAX_VALUE)
+ preferred = Integer.MAX_VALUE;
+ if (maximum > Integer.MAX_VALUE)
+ maximum = Integer.MAX_VALUE;
+ SizeRequirements result = new SizeRequirements((int) minimum,
+ (int) preferred,
+ (int) maximum,
+ 0.5F);
+ return result;
+ }
+
+ /**
+ * Calculates how much space is nessecary to place a set of components
+ * aligned according to their alignment value.
+ * The size requirements of the components is specified in
+ * children.
+ *
+ * @param children the SizeRequirements of each of the components
+ *
+ * @return the SizeRequirements that describe how much space is needed
+ * to place the components aligned
+ */
+ public static SizeRequirements
+ getAlignedSizeRequirements(SizeRequirements[] children)
+ {
+ float minLeft = 0;
+ float minRight = 0;
+ float prefLeft = 0;
+ float prefRight = 0;
+ float maxLeft = 0;
+ float maxRight = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ float myMinLeft = children[i].minimum * children[i].alignment;
+ float myMinRight = children[i].minimum - myMinLeft;
+ minLeft = Math.max(myMinLeft, minLeft);
+ minRight = Math.max(myMinRight, minRight);
+ float myPrefLeft = children[i].preferred * children[i].alignment;
+ float myPrefRight = children[i].preferred - myPrefLeft;
+ prefLeft = Math.max(myPrefLeft, prefLeft);
+ prefRight = Math.max(myPrefRight, prefRight);
+ float myMaxLeft = children[i].maximum * children[i].alignment;
+ float myMaxRight = children[i].maximum - myMaxLeft;
+ maxLeft = Math.max(myMaxLeft, maxLeft);
+ maxRight = Math.max(myMaxRight, maxRight);
+ }
+ int minSize = (int) (minLeft + minRight);
+ int prefSize = (int) (prefLeft + prefRight);
+ int maxSize = (int) (maxLeft + maxRight);
+ float align = prefLeft / (prefRight + prefLeft);
+ if (Float.isNaN(align))
+ align = 0;
+ return new SizeRequirements(minSize, prefSize, maxSize, align);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * allocated, the total size requirements of the set of
+ * components in total (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in children.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays offsets and spans.
+ *
+ * The components are placed in the forward direction, beginning with
+ * an offset of 0.
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ */
+ public static void calculateTiledPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans)
+ {
+ calculateTiledPositions(allocated, total, children, offsets, spans, true);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * allocated, the total size requirements of the set of
+ * components in total (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in children.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays offsets and spans.
+ *
+ * Depending on the value of forward the components are
+ * placed in the forward direction (left-right or top-bottom), where
+ * the offsets begin with 0, or in the reverse direction
+ * (right-left or bottom-top).
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ * @param forward whether the components should be placed in the forward
+ * direction (left-right or top-bottom) or reverse direction
+ * (right-left or bottom-top)
+ */
+ public static void calculateTiledPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans,
+ boolean forward)
+ {
+ int span = 0;
+ if (forward)
+ {
+ int offset = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ offsets[i] = offset;
+ spans[i] = children[i].preferred;
+ span += spans[i];
+ offset += children[i].preferred;
+ }
+ }
+ else
+ {
+ int offset = allocated;
+ for (int i = 0; i < children.length; i++)
+ {
+ offset -= children[i].preferred;
+ offsets[i] = offset;
+ span += spans[i];
+ spans[i] = children[i].preferred;
+ }
+ }
+ // Adjust spans so that we exactly fill the allocated region. If
+ if (span > allocated)
+ adjustSmaller(allocated, children, spans, span);
+ else if (span < allocated)
+ adjustGreater(allocated, children, spans, span);
+
+ // Adjust offsets.
+ if (forward)
+ {
+ int offset = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ offsets[i] = offset;
+ offset += spans[i];
+ }
+ }
+ else
+ {
+ int offset = allocated;
+ for (int i = 0; i < children.length; i++)
+ {
+ offset -= spans[i];
+ offsets[i] = offset;
+ }
+ }
+ }
+
+ private static void adjustSmaller(int allocated, SizeRequirements[] children,
+ int[] spans, int span)
+ {
+ // Sum up (prefSize - minSize) over all children
+ int sumDelta = 0;
+ for (int i = 0; i < children.length; i++)
+ sumDelta += children[i].preferred - children[i].minimum;
+
+ // If we have sumDelta == 0, then all components have prefSize == maxSize
+ // and we can't do anything about it.
+ if (sumDelta == 0)
+ return;
+
+ // Adjust all sizes according to their preferred and minimum sizes.
+ for (int i = 0; i < children.length; i++)
+ {
+ double factor = ((double) (children[i].preferred - children[i].minimum))
+ / ((double) sumDelta);
+ // In case we have a sumDelta of 0, the factor should also be 0.
+ if (Double.isNaN(factor))
+ factor = 0;
+ spans[i] -= factor * (span - allocated);
+ }
+ }
+
+ private static void adjustGreater(int allocated, SizeRequirements[] children,
+ int[] spans, int span)
+ {
+ // Sum up (maxSize - prefSize) over all children
+ long sumDelta = 0;
+ for (int i = 0; i < children.length; i++)
+ {
+ sumDelta += children[i].maximum - children[i].preferred;
+ }
+
+ // If we have sumDelta == 0, then all components have prefSize == maxSize
+ // and we can't do anything about it.
+ if (sumDelta == 0)
+ return;
+
+ // Adjust all sizes according to their preferred and minimum sizes.
+ for (int i = 0; i < children.length; i++)
+ {
+ double factor = ((double) (children[i].maximum - children[i].preferred))
+ / ((double) sumDelta);
+ spans[i] += factor * (allocated - span);
+ }
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * allocated, the total size requirements of the set of
+ * components in total (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in children.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays offsets and spans.
+ *
+ * The components are tiled in the forward direction, beginning with
+ * an offset of 0.
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param offsets will hold the offset values for each component
+ * @param spans will hold the span values for each component
+ */
+ public static void calculateAlignedPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offsets, int[] spans)
+ {
+ calculateAlignedPositions(allocated, total, children, offsets, spans,
+ true);
+ }
+
+ /**
+ * Calculate the offsets and spans of the components, when they should
+ * be placed end-to-end.
+ *
+ * You must specify the amount of allocated space in
+ * allocated, the total size requirements of the set of
+ * components in total (this can be calculated using
+ * {@link #getTiledSizeRequirements} and the size requirements of the
+ * components in children.
+ *
+ * The calculated offset and span values for each component are then
+ * stored in the arrays offsets and spans.
+ *
+ * Depending on the value of forward the components are
+ * placed in the forward direction (left-right or top-bottom), where
+ * the offsets begin with 0, or in the reverse direction
+ * (right-left or bottom-top).
+ *
+ * @param allocated the amount of allocated space
+ * @param total the total size requirements of the components
+ * @param children the size requirement of each component
+ * @param spans will hold the span values for each component
+ * @param forward whether the components should be placed in the forward
+ * direction (left-right or top-bottom) or reverse direction
+ * (right-left or bottom-top)
+ */
+ public static void calculateAlignedPositions(int allocated,
+ SizeRequirements total,
+ SizeRequirements[] children,
+ int[] offset, int[] spans,
+ boolean forward)
+ {
+ // First we compute the position of the baseline.
+ float baseline = allocated * total.alignment;
+
+ // Now we can layout the components along the baseline.
+ for (int i = 0; i < children.length; i++)
+ {
+ float align = children[i].alignment;
+ // Try to fit the component into the available space.
+ int[] spanAndOffset = new int[2];
+ if (align < .5F || baseline == 0)
+ adjustFromRight(children[i], baseline, allocated, spanAndOffset);
+ else
+ adjustFromLeft(children[i], baseline, allocated, spanAndOffset);
+ spans[i] = spanAndOffset[0];
+ offset[i] = spanAndOffset[1];
+ }
+ }
+
+ /**
+ * Adjusts the span and offset of a component for the aligned layout.
+ *
+ * @param reqs
+ * @param baseline
+ * @param allocated
+ * @param spanAndOffset
+ */
+ private static void adjustFromRight(SizeRequirements reqs, float baseline,
+ int allocated, int[] spanAndOffset)
+ {
+ float right = allocated - baseline;
+ // If the resulting span exceeds the maximum of the component, then adjust
+ // accordingly.
+ float maxRight = ((float) reqs.maximum) * (1.F - reqs.alignment);
+ if (right / (1.F - reqs.alignment) > reqs.maximum)
+ right = maxRight;
+ // If we have not enough space on the left side, then adjust accordingly.
+ if (right / (1.F - reqs.alignment) * reqs.alignment > allocated - baseline)
+ right = ((float) (allocated - baseline))
+ / reqs.alignment * (1.F - reqs.alignment);
+
+ spanAndOffset[0] = (int) (right / (1.F - reqs.alignment));
+ spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
+ }
+
+ /**
+ * Adjusts the span and offset of a component for the aligned layout.
+ *
+ * @param reqs
+ * @param baseline
+ * @param allocated
+ * @param spanAndOffset
+ */
+ private static void adjustFromLeft(SizeRequirements reqs, float baseline,
+ int allocated, int[] spanAndOffset)
+ {
+ float left = baseline;
+ // If the resulting span exceeds the maximum of the component, then adjust
+ // accordingly.
+ float maxLeft = ((float) reqs.maximum) * reqs.alignment;
+ if (left / reqs.alignment > reqs.maximum)
+ left = maxLeft;
+ // If we have not enough space on the right side, then adjust accordingly.
+ if (left / reqs.alignment * (1.F - reqs.alignment) > allocated - baseline)
+ left = ((float) (allocated - baseline))
+ / (1.F - reqs.alignment) * reqs.alignment;
+
+ spanAndOffset[0] = (int) (left / reqs.alignment);
+ spanAndOffset[1] = (int) (baseline - spanAndOffset[0] * reqs.alignment);
+ }
+
+ /**
+ * Returns an array of new preferred sizes for the children based on
+ * delta. delta specifies a change in the
+ * allocated space. The sizes of the children will be shortened or
+ * lengthened to accomodate the new allocation.
+ *
+ * @param delta the change of the size of the total allocation for
+ * the components
+ * @param children the size requirements of each component
+ *
+ * @return the new preferred sizes for each component
+ */
+ public static int[] adjustSizes(int delta, SizeRequirements[] children)
+ {
+ return null; // TODO
+ }
+}
diff --git a/libjava/classpath/javax/swing/SizeSequence.java b/libjava/classpath/javax/swing/SizeSequence.java
new file mode 100644
index 000000000..69c7366be
--- /dev/null
+++ b/libjava/classpath/javax/swing/SizeSequence.java
@@ -0,0 +1,225 @@
+/* SizeSequence.java --
+ Copyright (C) 2002, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.util.Arrays;
+
+/**
+ * A sequence of values that represent the dimensions (widths or heights) of
+ * some collection of items (for example, the widths of the columns in a table).
+ *
+ * @author Andrew Selkirk
+ */
+public class SizeSequence
+{
+ // TODO: Sun's API specification for this class contains an implementation
+ // note regarding the encoding for the element sizes. We currently use the
+ // simple size encoding but we should look at improving this.
+
+ /** Storage for the element sizes. */
+ private int[] sizes;
+
+ /**
+ * Creates a new empty SizeSequence instance.
+ */
+ public SizeSequence()
+ {
+ sizes = new int[0];
+ }
+
+ /**
+ * Creates a new SizeSequence instance with the specified number
+ * of elements, each having a size of 0.
+ *
+ * @param numEntries the number of elements.
+ */
+ public SizeSequence(int numEntries)
+ {
+ this(numEntries, 0);
+ }
+
+ /**
+ * Creates a new SizeSequence instance with the specified number
+ * of elements all having the same size (value).
+ *
+ * @param numEntries the number of elements.
+ * @param value the value for each element.
+ */
+ public SizeSequence(int numEntries, int value)
+ {
+ sizes = new int[numEntries];
+ Arrays.fill(sizes, value);
+ }
+
+ /**
+ * Creates a new SizeSequence instance using the specified
+ * element sizes.
+ *
+ * @param sizes the element sizes (null not permitted).
+ */
+ public SizeSequence(int[] sizes)
+ {
+ this.sizes = (int[]) sizes.clone();
+ }
+
+ /**
+ * Sets the size of the element at the specified index.
+ *
+ * @param index the index.
+ * @param size the size.
+ */
+ public void setSize(int index, int size)
+ {
+ if (index >= 0 && index < sizes.length)
+ sizes[index] = size;
+ }
+
+ /**
+ * Returns the index of the element that contains the specified position.
+ *
+ * @param position the position.
+ *
+ * @return The index of the element that contains the specified position.
+ */
+ public int getIndex(int position)
+ {
+ int i = 0;
+ int runningTotal = 0;
+ while (i < sizes.length && position >= runningTotal + sizes[i])
+ {
+ runningTotal += sizes[i];
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * Returns the size of the specified element, or 0 if the element index is
+ * outside the defined range.
+ *
+ * @param index the element index.
+ *
+ * @return The size of the specified element, or 0 if the element index is
+ * outside the defined range.
+ */
+ public int getSize(int index)
+ {
+ if (index < 0 || index >= sizes.length)
+ return 0;
+ return sizes[index];
+ }
+
+ /**
+ * Sets the sizes for the elements in the sequence.
+ *
+ * @param sizes the element sizes (null not permitted).
+ */
+ public void setSizes(int[] sizes)
+ {
+ this.sizes = (int[]) sizes.clone();
+ }
+
+ /**
+ * Returns an array containing the sizes for all the elements in the sequence.
+ *
+ * @return The element sizes.
+ */
+ public int[] getSizes()
+ {
+ return (int[]) sizes.clone();
+ }
+
+ /**
+ * Returns the position of the specified element.
+ *
+ * @param index the element index.
+ *
+ * @return The position.
+ */
+ public int getPosition(int index)
+ {
+ int position;
+ int loop;
+ position = 0;
+ for (loop = 0; loop < index; loop++)
+ position += sizes[loop];
+ return position;
+
+ }
+
+ /**
+ * Inserts new entries into the sequence at the start position.
+ * There are length new entries each having the specified
+ * value.
+ *
+ * @param start the start element.
+ * @param length the number of elements to insert.
+ * @param value the size for each of the new elements.
+ */
+ public void insertEntries(int start, int length, int value)
+ {
+ int[] newSizes = new int[sizes.length + length];
+ System.arraycopy(sizes, 0, newSizes, 0, start);
+ for (int i = start; i < start + length; i++)
+ newSizes[i] = value;
+ System.arraycopy(sizes, start, newSizes, start + length,
+ sizes.length - start);
+ sizes = newSizes;
+ }
+
+ /**
+ * Removes the element(s) at index start (the number of elements
+ * removed is length).
+ *
+ * @param start the index of the first element to remove.
+ * @param length the number of elements to remove.
+ */
+ public void removeEntries(int start, int length)
+ {
+ // Sanity check.
+ if ((start + length) > sizes.length)
+ throw new IllegalArgumentException("Specified start/length that "
+ + "is greater than available sizes");
+
+ int[] newSizes = new int[sizes.length - length];
+ System.arraycopy(sizes, 0, newSizes, 0, start);
+ System.arraycopy(sizes, start + length, newSizes, start,
+ sizes.length - start - length);
+ sizes = newSizes;
+ }
+}
diff --git a/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
new file mode 100644
index 000000000..3c07dbd93
--- /dev/null
+++ b/libjava/classpath/javax/swing/SortingFocusTraversalPolicy.java
@@ -0,0 +1,333 @@
+/* SortingFocusTraversalPolicy.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * @author Graydon Hoare
+ * @author Michael Koch
+ *
+ * @since 1.4
+ */
+public class SortingFocusTraversalPolicy
+ extends InternalFrameFocusTraversalPolicy
+{
+ /**
+ * The comparator used to sort elements in the focus traversal cycle
+ * managed by this class.
+ */
+ Comparator comparator;
+
+ /**
+ *
Whether or not to perform an "implicit DownCycle" when selecting
+ * successor components within a focus cycle.
+ *
+ *
When this is true, requesting the "next" component following a
+ * component which is a focus cycle root (and, necessarily, a container)
+ * will enter the focus cycle root of that container, and return its
+ * default focus.
+ *
+ *
When this property is false, requesting the "next" component will
+ * simply advance within the containing focus cycle, subject to the
+ * {@link #comparator} order and the {@link #accept} judgment.
+ *
+ * @see #getImplicitDownCycleTraversal()
+ */
+ boolean implicitDownCycleTraversal = true;
+
+ /**
+ * Creates a new SortingFocusTraversalPolicy with no
+ * comparator set.
+ */
+ protected SortingFocusTraversalPolicy()
+ {
+ // Do nothing here.
+ }
+
+ /**
+ * Creates a new SortingFocusTraversalPolicy with the given
+ * comparator set.
+ *
+ * @param comparator the comparator to set
+ */
+ public SortingFocusTraversalPolicy(Comparator super Component> comparator)
+ {
+ this.comparator = comparator;
+ }
+
+ /**
+ * Decide whether a component is an acceptable focus owner.
+ *
+ * @param comp The component which is a candidate for focus ownership.
+ *
+ * @return true if the component is focusable, displayable, visible, and
+ * enabled; otherwise false
+ */
+ protected boolean accept(Component comp)
+ {
+ return (comp.isVisible()
+ && comp.isDisplayable()
+ && comp.isEnabled()
+ && comp.isFocusable());
+ }
+
+ /**
+ * Get the current value of the {@link #comparator} property.
+ *
+ * @return the current value of the property
+ *
+ * @see #setComparator
+ */
+ protected Comparator super Component> getComparator()
+ {
+ return comparator;
+ }
+
+ /**
+ * Set the current value of the {@link #comparator} property.
+ *
+ * @param comparator the new value of the property
+ *
+ * @see #getComparator
+ */
+ protected void setComparator(Comparator super Component> comparator)
+ {
+ this.comparator = comparator;
+ }
+
+ private TreeSet getSortedCycle(Container root, TreeSet set)
+ {
+ if (set == null)
+ set = (getComparator() == null
+ ? new TreeSet()
+ : new TreeSet(getComparator()));
+
+ if (root != null)
+ {
+ Component[] comps = root.getComponents();
+ for (int i = 0; i < comps.length; ++i)
+ {
+ Component c = comps[i];
+ if (accept(c))
+ set.add(c);
+ if (c instanceof Container)
+ getSortedCycle((Container) c, set);
+ }
+ }
+ return set;
+ }
+
+ /**
+ * Return the component which follows the specified component in this
+ * focus cycle, relative to the order imposed by {@link
+ * #comparator}. Candidate components are only considered if they are
+ * accepted by the {@link #accept} method.
+ *
+ * If {@link #getImplicitDownCycleTraversal} is true and the
+ * comp is a focus cycle root, an "implicit DownCycle"
+ * occurs and the method returns the
+ * getDefaultComponent(comp).
+ *
+ * @param root the focus cycle root to search for a successor within
+ * @param comp the component to search for the successor of
+ *
+ * @return the component following the specified component under
+ * the specified root, or null if no such component is found
+ *
+ * @throws IllegalArgumentException if either argument is null, or
+ * if the root is not a focus cycle root of the component
+ */
+ public Component getComponentAfter(Container root,
+ Component comp)
+ {
+ if (comp == null || root == null || !comp.isFocusCycleRoot(root))
+ throw new IllegalArgumentException();
+
+ if (getImplicitDownCycleTraversal()
+ && comp instanceof Container
+ && ((Container)comp).isFocusCycleRoot())
+ {
+ return getDefaultComponent((Container) comp);
+ }
+
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ while (i.hasNext())
+ {
+ Component c = (Component) i.next();
+ if (c != null && c.equals(comp))
+ {
+ if (i.hasNext())
+ return (Component) i.next();
+ break;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * Return the component which precedes the specified component in this
+ * focus cycle, relative to the order imposed by {@link
+ * #comparator}. Candidate components are only considered if they are
+ * accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for a predecessor within
+ * @param comp the component to search for the predecessor of
+ *
+ * @return the component preceding the specified component under the
+ * specified root, or null if no such component is found
+ *
+ * @throws IllegalArgumentException if either argument is null, or
+ * if the root is not a focus cycle root of the component
+ */
+ public Component getComponentBefore(Container root,
+ Component comp)
+ {
+ if (comp == null || root == null || !comp.isFocusCycleRoot(root))
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ Component prev = null;
+ while (i.hasNext())
+ {
+ Component c = (Component) i.next();
+ if (c != null && c.equals(comp))
+ break;
+ prev = c;
+ }
+ return prev;
+ }
+
+ /**
+ * Return the default component of root, which is by default
+ * the same as the first component, returned by {@link
+ * #getFirstComponent}.
+ *
+ * @param root the focus cycle root to return the default component of
+ *
+ * @return the default focus component for root
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getDefaultComponent(Container root)
+ {
+ return getFirstComponent(root);
+ }
+
+ /**
+ * Return the first focusable component of the focus cycle root
+ * comp under the ordering imposed by the {@link
+ * #comparator} property. Candidate components are only considered if
+ * they are accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for the first component of
+ *
+ * @return the first component under root, or null if
+ * no components are found.
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getFirstComponent(Container root)
+ {
+ if (root == null)
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ if (i.hasNext())
+ return (Component) i.next();
+ return null;
+ }
+
+ /**
+ * Return the last focusable component of the focus cycle root
+ * comp under the ordering imposed by the {@link
+ * #comparator} property. Candidate components are only considered if
+ * they are accepted by the {@link #accept} method.
+ *
+ * @param root the focus cycle root to search for the last component of
+ *
+ * @return the last component under root, or null if
+ * no components are found.
+ *
+ * @throws IllegalArgumentException if root is null
+ */
+ public Component getLastComponent(Container root)
+ {
+ if (root == null)
+ throw new IllegalArgumentException();
+ TreeSet set = getSortedCycle(root, null);
+ Iterator i = set.iterator();
+ Component last = null;
+ while (i.hasNext())
+ last = (Component) i.next();
+ return last;
+ }
+
+ /**
+ * Return the current value of the {@link #implicitDownCycleTraversal}
+ * property.
+ *
+ * @return the current value of the property
+ *
+ * @see #setImplicitDownCycleTraversal
+ */
+ public boolean getImplicitDownCycleTraversal()
+ {
+ return implicitDownCycleTraversal;
+ }
+
+ /**
+ * Set the current value of the {@link #implicitDownCycleTraversal}
+ * property.
+ *
+ * @param down the new value of the property
+ *
+ * @see #getImplicitDownCycleTraversal
+ */
+ public void setImplicitDownCycleTraversal(boolean down)
+ {
+ implicitDownCycleTraversal = down;
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpinnerDateModel.java b/libjava/classpath/javax/swing/SpinnerDateModel.java
new file mode 100644
index 000000000..f0e9d1ec4
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerDateModel.java
@@ -0,0 +1,317 @@
+/* SpinnerDateModel.java --
+ Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * A date model used by the {@link JSpinner} component. This implements a
+ * spinner model for dates, rotating a calendar field such as month, year,
+ * day, week, hour, minute.
+ *
+ * @author Sven de Marothy
+ * @since 1.4
+ */
+public class SpinnerDateModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /** The current date. */
+ private Calendar date;
+
+ /**
+ * A constraint on the start or earliest permitted date (null
+ * for no minimum).
+ */
+ private Comparable start;
+
+ /**
+ * A constraint on the end or latest permitted date (null for no
+ * maximum).
+ */
+ private Comparable end;
+
+ /**
+ * The calendar field used to calculate the previous or next date.
+ */
+ private int calendarField;
+
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = -4802518107105940612L;
+
+ /**
+ * Constructs a SpinnerDateModel using the current date,
+ * no start or end limit, and {@link Calendar#DAY_OF_MONTH} as the calendar
+ * field.
+ */
+ public SpinnerDateModel()
+ {
+ this(new Date(), null, null, Calendar.DAY_OF_MONTH);
+ }
+
+ /**
+ * Constructs a SpinnerDateModel with the specified value, lower
+ * and upper bounds, and which spins the specified calendar field.
+ *
+ * The start and end limits must have a
+ * compareTo method that supports instances of {@link Date}, but
+ * do not themselves need to be instances of {@link Date} (although typically
+ * they are).
+ *
+ * @param value the initial value/date (null not permitted).
+ * @param start a constraint that specifies the earliest permitted date
+ * value, or null for no lower limit.
+ * @param end a constraint that specifies the latest permitted date value,
+ * or null for no upper limit.
+ * @param calendarField the Calendar field to spin,
+ * (Calendar.ZONE_OFFSET and Calendar.DST_OFFSET are invalid)
+ */
+ public SpinnerDateModel(Date value, Comparable start, Comparable end,
+ int calendarField)
+ {
+ if (value == null)
+ throw new IllegalArgumentException("Null 'value' argument.");
+ if (start != null && start.compareTo(value) > 0)
+ throw new IllegalArgumentException("Require value on or after start.");
+ if (end != null && end.compareTo(value) < 0)
+ throw new IllegalArgumentException("Require value on or before end.");
+ date = Calendar.getInstance();
+ date.setTime(value);
+ this.start = start;
+ this.end = end;
+ setCalendarField(calendarField);
+ }
+
+ /**
+ * Returns the {@link Calendar} field used to calculate the previous and
+ * next dates in the sequence.
+ *
+ * @return The date field code.
+ */
+ public int getCalendarField()
+ {
+ return calendarField;
+ }
+
+ /**
+ * Returns the current date/time.
+ *
+ * @return The current date/time (never null).
+ *
+ * @see #getValue()
+ */
+ public Date getDate()
+ {
+ return date.getTime();
+ }
+
+ /**
+ * Returns the lower limit on the date/time value, or null if
+ * there is no minimum date/time.
+ *
+ * @return The lower limit.
+ *
+ * @see #setStart(Comparable)
+ */
+ public Comparable getStart()
+ {
+ return start;
+ }
+
+ /**
+ * Returns the upper limit on the date/time value, or null if
+ * there is no maximum date/time.
+ *
+ * @return The upper limit.
+ *
+ * @see #setEnd(Comparable)
+ */
+ public Comparable getEnd()
+ {
+ return end;
+ }
+
+ /**
+ * Returns the current date in the sequence (this method returns the same as
+ * {@link #getDate()}).
+ *
+ * @return The current date (never null).
+ */
+ public Object getValue()
+ {
+ return date.getTime();
+ }
+
+ /**
+ * Returns the next date in the sequence, or null if the
+ * next date is past the upper limit (if one is specified). The current date
+ * is not changed.
+ *
+ * @return The next date, or null if the current value is
+ * the latest date represented by the model.
+ *
+ * @see #getEnd()
+ */
+ public Object getNextValue()
+ {
+ Calendar nextCal = Calendar.getInstance();
+ nextCal.setTime(date.getTime());
+ nextCal.roll(calendarField, true);
+ Date nextDate = nextCal.getTime();
+ if (end != null)
+ if (end.compareTo(nextDate) < 0)
+ return null;
+ return nextDate;
+ }
+
+ /**
+ * Returns the previous date in the sequence, or null if the
+ * previous date is prior to the lower limit (if one is specified). The
+ * current date is not changed.
+ *
+ * @return The previous date, or null if the current value is
+ * the earliest date represented by the model.
+ *
+ * @see #getStart()
+ */
+ public Object getPreviousValue()
+ {
+ Calendar prevCal = Calendar.getInstance();
+ prevCal.setTime(date.getTime());
+ prevCal.roll(calendarField, false);
+ Date prevDate = prevCal.getTime();
+ if (start != null)
+ if (start.compareTo(prevDate) > 0)
+ return null;
+ return prevDate;
+ }
+
+ /**
+ * Sets the date field to change when calculating the next and previous
+ * values. It must be a valid {@link Calendar} field, excluding
+ * {@link Calendar#ZONE_OFFSET} and {@link Calendar#DST_OFFSET}.
+ *
+ * @param calendarField the calendar field to set.
+ *
+ * @throws IllegalArgumentException if calendarField is not
+ * a valid code.
+ */
+ public void setCalendarField(int calendarField)
+ {
+ if (calendarField < 0 || calendarField >= Calendar.FIELD_COUNT
+ || calendarField == Calendar.ZONE_OFFSET
+ || calendarField == Calendar.DST_OFFSET)
+ throw new IllegalArgumentException("Illegal calendarField");
+
+ if (this.calendarField != calendarField)
+ {
+ this.calendarField = calendarField;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the lower limit for the date/time value and, if the new limit is
+ * different to the old limit, sends a {@link ChangeEvent} to all registered
+ * listeners. A null value is interpreted as "no lower limit".
+ * No check is made to ensure that the current date/time is on or after the
+ * new lower limit - the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * start is {@link Serializable}.
+ *
+ * @param start the new lower limit for the date/time value
+ * (null permitted).
+ */
+ public void setStart(Comparable start)
+ {
+ if (this.start != start)
+ {
+ this.start = start;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the upper limit for the date/time value and, if the new limit is
+ * different to the old limit, sends a {@link ChangeEvent} to all registered
+ * listeners. A null value is interpreted as "no upper limit".
+ * No check is made to ensure that the current date/time is on or before the
+ * new upper limit - the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * end is {@link Serializable}.
+ *
+ * @param end the new upper limit for the date/time value (null
+ * permitted).
+ */
+ public void setEnd(Comparable end)
+ {
+ if (this.end != end)
+ {
+ this.end = end;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Sets the current date and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param value the new date (null not permitted, must be an
+ * instance of Date).
+ *
+ * @throws IllegalArgumentException if value is not an instance
+ * of Date.
+ */
+ public void setValue(Object value)
+ {
+ if (! (value instanceof Date) || value == null)
+ throw new IllegalArgumentException("Value not a date.");
+
+ if (!date.getTime().equals(value))
+ {
+ date.setTime((Date) value);
+ fireStateChanged();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpinnerListModel.java b/libjava/classpath/javax/swing/SpinnerListModel.java
new file mode 100644
index 000000000..de4926afd
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerListModel.java
@@ -0,0 +1,295 @@
+/* SpinnerListModel.java -- A spinner model backed by a list or an array.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * An implementation of SpinnerModel which uses the values
+ * contained within a list or an array. The backing list or array is
+ * only stored as a reference within the class. As a result, changes
+ * made elsewhere to the members of the list or array are reflected by
+ * this model.
+ *
+ *
+ * The model itself inherits a list of ChangeListeners from
+ * AbstractSpinnerModel. As this code is unaware of changes
+ * made to the backing list or array, it is the responsibility of the
+ * application using the model to invoke fireStateChanged(),
+ * in order to notify any ChangeListeners, when the list or array
+ * changes. The model handles notification when the reference itself
+ * is changed via setList() or when the current value is
+ * set directly using setValue().
+ *
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ * @see SpinnerModel
+ * @see AbstractSpinnerModel
+ * @see JSpinner
+ * @since 1.4
+ */
+
+public class SpinnerListModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 3358804052191994516L;
+
+ /**
+ * The backing list for this model.
+ */
+ private List list;
+
+ /**
+ * The current index in the list.
+ */
+ private transient int index;
+
+ /**
+ * Constructs a default SpinnerListModel. This
+ * is a model backed by a list containing only the single
+ * String element, "empty".
+ */
+ public SpinnerListModel()
+ {
+ List defaultList;
+
+ // Create an empty list.
+ defaultList = new ArrayList();
+ // Add the string "empty".
+ defaultList.add("empty");
+ // Set the list.
+ setList(defaultList);
+ }
+
+ /**
+ * Constructs a SpinnerListModel using the supplied list.
+ * The model maintains a reference to this list, and returns
+ * consecutive elements in response to calls to getNextValue().
+ * The initial value is that at position 0, so an initial call
+ * to getValue() returns the same as list.get(0).
+ *
+ * @param list The list to use for this model.
+ *
+ * @throws IllegalArgumentException if the list is null or contains no
+ * elements.
+ *
+ * @see SpinnerListModel#getNextValue()
+ * @see SpinnerListModel#getValue()
+ */
+ public SpinnerListModel(List> list)
+ {
+ // Retain a reference to the valid list.
+ setList(list);
+ }
+
+ /**
+ * Constructs a SpinnerListModel using the supplied array.
+ * The model stores a reference to the wrapper list returned by
+ * Arrays.asList(). The wrapper list reflects modifications
+ * in the underlying array, so these changes will also be reflected
+ * by the model. The model produces consecutive elements from the array
+ * in response to calls to getNextValue(). The initial
+ * value returned by getValue() is the same as
+ * array[0].
+ *
+ * @param array The array to use for this model.
+ *
+ * @throws IllegalArgumentException if the array is null or contains
+ * no elements.
+ *
+ * @see Arrays#asList(Object[])
+ * @see SpinnerListModel#getNextValue()
+ * @see SpinnerListModel#getValue()
+ */
+ public SpinnerListModel(Object[] array)
+ {
+ // Check for a null or zero-sized array.
+ if (array == null || array.length == 0)
+ {
+ throw new IllegalArgumentException("The supplied array was invalid.");
+ }
+
+ // Retain a reference to a wrapper around the valid array.
+ // The array, in list form, will be tested again here, but we can't really
+ // avoid this -- a null value to Arrays.asList will throw a
+ // NullPointerException.
+ setList(Arrays.asList(array));
+ }
+
+ /**
+ * Returns the backing list for this model.
+ *
+ * @return The backing list.
+ */
+ public List> getList()
+ {
+ return list;
+ }
+
+ /**
+ * Returns the next value from the list, which is the same as the element
+ * stored at the current index + 1. Null is returned if there are no more
+ * values to be returned (the end of the list has been reached). An
+ * ambiguity can occur here, as null may also be returned as a valid list
+ * element. This operation does not change the current value.
+ *
+ * @return The next value from the list or null.
+ */
+ public Object getNextValue()
+ {
+ // Check for a next value.
+ if (index < (list.size() - 1))
+ // Return the element at the next index.
+ return list.get(index + 1);
+ else
+ // Return null as this is the end of the list.
+ return null;
+ }
+
+ /**
+ * Returns the previous value from the list, which is the same as the element
+ * stored at the current index - 1. Null is returned if there are no more
+ * values to be returned (the start of the list has been reached). An
+ * ambiguity can occur here, as null may also be returned as a valid list
+ * element. This operation does not change the current value.
+ *
+ * @return The previous value from the list or null.
+ */
+ public Object getPreviousValue()
+ {
+ // Check for a previous value.
+ if (index > 0)
+ // Return the element at the previous position.
+ return list.get(index - 1);
+ else
+ // Return null as this is the start of the list.
+ return null;
+ }
+
+ /**
+ * Returns the current value of the model. Initially, this will
+ * be the element at position 0. On later invocations, this will
+ * be the last element returned by getNextValue()
+ * or getPreviousValue().
+ *
+ * @return The current value.
+ *
+ * @see SpinnerListModel#getPreviousValue()
+ * @see SpinnerListModel#getNextValue()
+ */
+ public Object getValue()
+ {
+ return list.get(index);
+ }
+
+ /**
+ * Changes the backing list for this model. The model only stores
+ * a reference to the list, so any changes made to the list elsewhere
+ * will be reflected in the values returned by the model. A
+ * ChangeEvent is fired if the list being used actually
+ * changes (i.e. the new list is not referentially equal (!=) to the
+ * old one).
+ *
+ * @param list The new list to use.
+ *
+ * @throws IllegalArgumentException if the list is null or contains
+ * no elements.
+ *
+ * @see ChangeEvent
+ */
+ public void setList(List> list)
+ {
+ // Check for null or zero size list.
+ if (list == null || list.size() == 0)
+ throw new IllegalArgumentException("The supplied list was invalid.");
+
+ // Check for a change of referenced list.
+ if (this.list != list)
+ {
+ // Store the new list.
+ this.list = list;
+ // Notify listeners of a change.
+ fireStateChanged();
+ }
+ // We reset the other values in either case.
+ // Set the index to 0.
+ index = 0;
+ }
+
+ /**
+ * Sets the current value of the model to be the one supplied.
+ * The value must exist within the backing list in order for
+ * the change to take place. Otherwise, an exception is thrown.
+ * The value used is the first occurrence of the value within
+ * the backing list. Listeners are notified of this change.
+ * Following the change, getNextValue() and
+ * getPreviousValue() return the objects following
+ * and prior to the supplied value, respectively.
+ *
+ * @param value The requested new value of the list.
+ *
+ * @throws IllegalArgumentException if the supplied value does
+ * not exist in the backing list.
+ *
+ * @see SpinnerListModel#getPreviousValue()
+ * @see SpinnerListModel#getNextValue()
+ */
+ public void setValue(Object value)
+ {
+ int valueIndex;
+
+ // Search for the value in the list.
+ valueIndex = list.indexOf(value);
+ // Check for the value being found.
+ if (valueIndex == -1)
+ throw new IllegalArgumentException("The supplied value does not "
+ + "exist in this list");
+ // Make the indices match.
+ index = valueIndex;
+ // Notify the listeners.
+ fireStateChanged();
+ }
+
+}
diff --git a/libjava/classpath/javax/swing/SpinnerModel.java b/libjava/classpath/javax/swing/SpinnerModel.java
new file mode 100644
index 000000000..6cab34da2
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerModel.java
@@ -0,0 +1,111 @@
+/* SpinnerModel.java --
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import javax.swing.event.ChangeListener;
+
+/**
+ * The data model that is used in {@link JSpinner}s.
+ *
+ * @since 1.4
+ */
+public interface SpinnerModel
+{
+ /**
+ * Sets the current value of the model to that specified.
+ * Implementations can choose to refuse to accept the value
+ * and throw an exception instead. For example, a date model
+ * may throw invalid dates, or a list model may throw out
+ * values which don't exist in the underlying list. Models
+ * may also throw out unusual values, such as null. The decision
+ * is left to the discretion of the implementator. If the
+ * operation succeeds, the implementation should also notify
+ * any registered ChangeListeners.
+ *
+ * @param value The new value of the model.
+ * @throws IllegalArgumentException if the model does not accept
+ * the given value.
+ */
+ void setValue(Object value);
+
+ /**
+ * Returns the current value of the model.
+ *
+ * @return The current value.
+ */
+ Object getValue();
+
+ /**
+ * Returns the next value from the model. If the model is bounded,
+ * this method may return null when the upper bound is met.
+ * The current value is not changed.
+ *
+ * @return The next value, or null if there are no more values
+ * to retrieve.
+ */
+ Object getNextValue();
+
+ /**
+ * Returns the previous value from the model. If the model is
+ * bounded, this method may return null when the lower bound is
+ * met. The current value is not changed.
+ *
+ * @return The previous value, or null if there are no more
+ * values to retrieve.
+ */
+ Object getPreviousValue();
+
+ /**
+ * Adds a ChangeListener to the list of registered
+ * listeners. Each listener is notified when the current value
+ * is changed.
+ *
+ * @param listener The new listener to register.
+ */
+ void addChangeListener(ChangeListener listener);
+
+ /**
+ * Removes a given ChangeListener from the list
+ * of registered listeners.
+ *
+ * @param listener The listener to remove.
+ */
+ void removeChangeListener(ChangeListener listener);
+
+}
diff --git a/libjava/classpath/javax/swing/SpinnerNumberModel.java b/libjava/classpath/javax/swing/SpinnerNumberModel.java
new file mode 100644
index 000000000..af3fea65f
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpinnerNumberModel.java
@@ -0,0 +1,361 @@
+/* SpinnerNumberModel.java --
+ Copyright (C) 2002, 2004, 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.io.Serializable;
+
+import javax.swing.event.ChangeEvent;
+
+/**
+ * A model used by the {@link JSpinner} component.
+ *
+ * @author Ka-Hing Cheung
+ * @since 1.4
+ */
+public class SpinnerNumberModel extends AbstractSpinnerModel
+ implements Serializable
+{
+ /**
+ * For compatability with Sun's JDK
+ */
+ private static final long serialVersionUID = 7279176385485777821L;
+
+ /** The current value. */
+ private Number value;
+
+ /** The minimum value (or null). */
+ private Comparable minimum;
+
+ /** The maximum value (or null). */
+ private Comparable maximum;
+
+ /** The step size. */
+ private Number stepSize;
+
+ /**
+ * Creates a SpinnerNumberModel with initial value 0, step 1,
+ * and no maximum nor minimum.
+ */
+ public SpinnerNumberModel()
+ {
+ this(new Integer(0), null, null, new Integer(1));
+ }
+
+ /**
+ * Creates a SpinnerNumberModel with double precision.
+ *
+ * @param value the initial value
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param stepSize the step size
+ * @throws IllegalArgumentException if minimum <= value <= maximum does
+ * not hold.
+ */
+ public SpinnerNumberModel(double value, double minimum, double maximum,
+ double stepSize)
+ {
+ this(new Double(value), new Double(minimum), new Double(maximum),
+ new Double(stepSize));
+ }
+
+ /**
+ * Creates a SpinnerNumberModel with integer precision.
+ *
+ * @param value the initial value
+ * @param minimum the minimum value
+ * @param maximum the maximum value
+ * @param stepSize the step size
+ * @throws IllegalArgumentException if minimum <= value <= maximum does
+ * not hold.
+ */
+ public SpinnerNumberModel(int value, int minimum, int maximum, int stepSize)
+ {
+ this(new Integer(value), new Integer(minimum), new Integer(maximum),
+ new Integer(stepSize));
+ }
+
+ /**
+ * Creates a SpinnerNumberModel with the given attributes. The
+ * caller should ensure that both minimum and
+ * maximum are serializable.
+ *
+ * @param value the initial value (null not permitted).
+ * @param minimum the minimum value (null permitted).
+ * @param maximum the maximum value (null permitted).
+ * @param stepSize the step size (null not permitted).
+ *
+ * @throws IllegalArgumentException if minimum <= value <= maximum
+ * does not hold
+ * @throws IllegalArgumentException if value is
+ * null.
+ * @throws IllegalArgumentException if stepSize is
+ * null.
+ */
+ public SpinnerNumberModel(Number value, Comparable minimum,
+ Comparable maximum, Number stepSize)
+ {
+ if (stepSize == null)
+ throw new IllegalArgumentException("stepSize may not be null");
+ if (value == null)
+ throw new IllegalArgumentException("value may not be null");
+ if (minimum != null)
+ {
+ if (minimum.compareTo(value) > 0)
+ throw new IllegalArgumentException("minimum is not <= value");
+ }
+ if (maximum != null)
+ {
+ if (maximum.compareTo(value) < 0)
+ throw new IllegalArgumentException("maximum is not >= value");
+ }
+
+ this.value = value;
+ this.stepSize = stepSize;
+ this.minimum = minimum;
+ this.maximum = maximum;
+ }
+
+ /**
+ * Sets the current value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param value the new value (null not permitted, must be an
+ * instance of Number).
+ *
+ * @throws IllegalArgumentException if value is not an instance
+ * of Number.
+ */
+ public void setValue(Object value)
+ {
+ if (! (value instanceof Number))
+ throw new IllegalArgumentException("value must be a Number");
+
+ if (!this.value.equals(value))
+ {
+ this.value = (Number) value;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the current value, which for this class is always an instance of
+ * {@link Number}.
+ *
+ * @return The current value.
+ *
+ * @see #getNumber()
+ */
+ public Object getValue()
+ {
+ return value;
+ }
+
+ /**
+ * Returns the next value, or null if adding the step size to
+ * the current value results in a value greater than the maximum value.
+ * The current value is not changed.
+ *
+ * @return The next value, or null if the current value is the
+ * maximum value represented by this model.
+ */
+ public Object getNextValue()
+ {
+ Number num;
+
+ if (value instanceof Double)
+ num = new Double(value.doubleValue() + stepSize.doubleValue());
+ else if (value instanceof Float)
+ num = new Double(value.floatValue() + stepSize.floatValue());
+ else if (value instanceof Long)
+ num = new Long(value.longValue() + stepSize.longValue());
+ else if (value instanceof Integer)
+ num = new Integer(value.intValue() + stepSize.intValue());
+ else if (value instanceof Short)
+ num = new Short((short) (value.shortValue() + stepSize.shortValue()));
+ else
+ num = new Byte((byte) (value.byteValue() + stepSize.byteValue()));
+
+ // check upper bound if set
+ if ((maximum != null) && maximum.compareTo(num) < 0)
+ num = null;
+
+ return num;
+ }
+
+ /**
+ * Returns the previous value, or null if subtracting the
+ * step size from the current value results in a value less than the minimum
+ * value. The current value is not changed.
+ *
+ * @return The previous value, or null if the current value
+ * is the minimum value represented by this model.
+ */
+ public Object getPreviousValue()
+ {
+ Number num;
+
+ if (value instanceof Double)
+ num = new Double(value.doubleValue() - stepSize.doubleValue());
+ else if (value instanceof Float)
+ num = new Double(value.floatValue() - stepSize.floatValue());
+ else if (value instanceof Long)
+ num = new Long(value.longValue() - stepSize.longValue());
+ else if (value instanceof Integer)
+ num = new Integer(value.intValue() - stepSize.intValue());
+ else if (value instanceof Short)
+ num = new Short((short) (value.shortValue() - stepSize.shortValue()));
+ else
+ num = new Byte((byte) (value.byteValue() - stepSize.byteValue()));
+
+ // check lower bound if set
+ if ((minimum != null) && minimum.compareTo(num) > 0)
+ num = null;
+
+ return num;
+ }
+
+ /**
+ * Returns the current value.
+ *
+ * @return The current value.
+ */
+ public Number getNumber()
+ {
+ return value;
+ }
+
+ /**
+ * Returns the minimum value, or null if there is no minimum.
+ *
+ * @return The minimum value.
+ *
+ * @see #setMinimum(Comparable)
+ */
+ public Comparable getMinimum()
+ {
+ return minimum;
+ }
+
+ /**
+ * Sets the minimum value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners. A
+ * null value is interpreted as "no minimum value". No check
+ * is made to ensure that the new minimum is less than or equal to the
+ * current value, the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * newMinimum is {@link Serializable}.
+ *
+ * @param newMinimum the new minimum value (null permitted).
+ *
+ * @see #getMinimum()
+ */
+ public void setMinimum(Comparable newMinimum)
+ {
+ if (minimum != null ? !minimum.equals(newMinimum) : newMinimum != null)
+ {
+ minimum = newMinimum;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the maximum value, or null if there is no maximum.
+ *
+ * @return The maximum value.
+ *
+ * @see #getMinimum()
+ * @see #setMaximum(Comparable)
+ */
+ public Comparable getMaximum()
+ {
+ return maximum;
+ }
+
+ /**
+ * Sets the maximum value and, if the new value is different to the old
+ * value, sends a {@link ChangeEvent} to all registered listeners. A
+ * null value is interpreted as "no maximum value". No check
+ * is made to ensure that the new maximum is greater than or equal to the
+ * current value, the caller is responsible for ensuring that this
+ * relationship holds. In addition, the caller should ensure that
+ * newMaximum is {@link Serializable}.
+ *
+ * @param newMaximum the new maximum (null permitted).
+ *
+ * @see #getMaximum()
+ */
+ public void setMaximum(Comparable newMaximum)
+ {
+ if (maximum != null ? !maximum.equals(newMaximum) : newMaximum != null)
+ {
+ maximum = newMaximum;
+ fireStateChanged();
+ }
+ }
+
+ /**
+ * Returns the step size.
+ *
+ * @return The step size (never null).
+ */
+ public Number getStepSize()
+ {
+ return stepSize;
+ }
+
+ /**
+ * Sets the step size and, if the new step size is different to the old
+ * step size, sends a {@link ChangeEvent} to all registered listeners.
+ *
+ * @param newStepSize the new step size (null not permitted).
+ *
+ * @throws IllegalArgumentException if newStepSize is
+ * null.
+ */
+ public void setStepSize(Number newStepSize)
+ {
+ if (newStepSize == null)
+ throw new IllegalArgumentException();
+
+ if (!stepSize.equals(newStepSize))
+ {
+ stepSize = newStepSize;
+ fireStateChanged();
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/Spring.java b/libjava/classpath/javax/swing/Spring.java
new file mode 100644
index 000000000..4cc06e539
--- /dev/null
+++ b/libjava/classpath/javax/swing/Spring.java
@@ -0,0 +1,745 @@
+/* Spring.java --
+ Copyright (C) 2004 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Dimension;
+
+/**
+ * Calculates the space between component edges, that are layed out by
+ * {@link SpringLayout}.
+ *
+ * A Spring defines a minimum, preferred and maximum distance for each edge
+ * (north, east, south, west) of a component.
+ *
+ * However, springs are not static, their actual values are computed at
+ * runtime. That means, if a Spring C is defined as the sum of Spring A and
+ * Spring B, then the values (min, pref and max) are not calculated at
+ * creation of Spring C, but instead always when {@link #getValue} is
+ * called. So, when Spring A or Spring B changes, this is reflected in
+ * Spring C.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+public abstract class Spring
+{
+
+ /** Indicates a not-set value. **/
+ public static final int UNSET = Integer.MIN_VALUE;
+
+ /**
+ * Creates a new Spring object. This constructor is used by the static
+ * methods which create Springs.
+ */
+ protected Spring()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * Creates a Spring which min, pref and max values are all the same.
+ * These kind of Springs are 'struts'.
+ *
+ * @param val the constant for min, pref and max values.
+ * @return a Spring object with constant values for min, pref and max.
+ */
+ public static Spring constant(int val)
+ {
+ return new SimpleSpring(val, val, val);
+ }
+
+ /** Creates a Spring which min, pref and max values are constants.
+ * @param min the constant for the minimum value.
+ * @param pref the constant for the preferred value.
+ * @param max the constant for the maximum value.
+ * @return a Spring object with constant values for min, pref and max.
+ */
+ public static Spring constant(int min, int pref, int max)
+ {
+ return new SimpleSpring(min, pref, max);
+ }
+
+ /**
+ * Returns the maximum value of the Spring.
+ *
+ * @return the maximum value.
+ */
+ public abstract int getMaximumValue();
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public abstract int getMinimumValue();
+
+ /**
+ * Return the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public abstract int getPreferredValue();
+
+ /**
+ * Return the actual value of this Spring.
+ *
+ * @return the actual value of this Spring.
+ */
+ public abstract int getValue();
+
+ /**
+ * Creates and returns a Spring, which always has the maximum values
+ * min = max(min_s1, min_s2), pref = max(pref_s1, pref_s2), max =
+ * max(max_s1, max_s2).
+ *
+ * @param s1 the first summand of the max Spring.
+ * @param s2 the second summand of the max Spring.
+ * @return a Spring which is max(s1, s2).
+ */
+ public static Spring max(Spring s1, Spring s2)
+ {
+ return new MaxSpring(s1, s2);
+ }
+
+ /**
+ * Creates and returns a Spring, which is always the negation of s.
+ * min = -min_s, pref = -pref_s, max = -max_pref.
+ *
+ * @param s the Spring to be negated.
+ * @return the negative of s.
+ */
+ public static Spring minus(Spring s)
+ {
+ return new MinusSpring(s);
+ }
+
+ /**
+ * Sets the actual value. If value is out of the (min, max)
+ * bounds, then the value is adjusted, so that is inside these bounds.
+ *
+ * @param value the value to be set.
+ */
+ public abstract void setValue(int value);
+
+ private int getShrinkRange()
+ {
+ return (getPreferredValue() - getMinimumValue());
+ }
+
+ private int getExpandRange()
+ {
+ return (getMaximumValue() - getPreferredValue());
+ }
+
+ double getStrain()
+ {
+ int v = getValue();
+ int p = getPreferredValue();
+ int r = (v < p) ? getShrinkRange() : getExpandRange();
+ if (r == 0)
+ r = 1;
+ return (double)(v - p) / r;
+ }
+
+ void setStrain(double strain)
+ {
+ int r = (strain < 0) ? getShrinkRange() : getExpandRange();
+ int v = (getPreferredValue() + (int)(strain * r));
+ setValue(v);
+ }
+
+ /**
+ * Creates and returns a Spring, which is always the sum of s1 and s2.
+ * min_sum = min_s1 + min_s2, pref_sum = pref_s1 + pref_s2, max_sum =
+ * max_s1 + max_s2.
+ *
+ * @param s1 the 1st summand of the sum Spring.
+ * @param s2 the 2nd summand of the sum Spring.
+ * @return a sum which is s1 + s2.
+ */
+ public static Spring sum(Spring s1, Spring s2)
+ {
+ return new AddSpring(s1, s2);
+ }
+
+ /**
+ * Return a new Spring which computes its values by scaling
+ * the values of another spring by a constant factor. If the
+ * factor is negative, the minimum and maximum values of
+ * the argument spring will be interchanged.
+ * @param spring the spring to track
+ * @param factor the factor by which to scale
+ * @return a new multiplicative Spring
+ * @since 1.5
+ */
+ public static Spring scale(final Spring spring, final float factor)
+ {
+ if (spring == null)
+ throw new NullPointerException("spring argument is null");
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return (int) ((factor < 0 ? spring.getMinimumValue()
+ : spring.getMaximumValue())
+ * factor);
+ }
+
+ public int getMinimumValue()
+ {
+ return (int) ((factor < 0 ? spring.getMaximumValue()
+ : spring.getMinimumValue())
+ * factor);
+ }
+
+ public int getPreferredValue()
+ {
+ return (int) (spring.getPreferredValue() * factor);
+ }
+
+ public int getValue()
+ {
+ return (int) (spring.getValue() * factor);
+ }
+
+ public void setValue(int value)
+ {
+ spring.setValue((int) (value / factor));
+ }
+ };
+ }
+
+ /**
+ * Return a new Spring which takes its values from the specified
+ * Component. In particular, the maximum value is taken from
+ * the maximumSize, the minimum value is taken from the minimumSize,
+ * the preferred value is taken from the preferredSize, and the
+ * value is taken from the component's current size. These values
+ * change as the component changes size.
+ * @param component the component
+ * @return a new Spring which tracks the component's width
+ * @since 1.5
+ */
+ public static Spring width(final Component component)
+ {
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return component.getMaximumSize().width;
+ }
+
+ public int getMinimumValue()
+ {
+ return component.getMinimumSize().width;
+ }
+
+ public int getPreferredValue()
+ {
+ return component.getPreferredSize().width;
+ }
+
+ public int getValue()
+ {
+ return component.getSize().width;
+ }
+
+ public void setValue(int value)
+ {
+ Dimension d = component.getSize();
+ component.setSize(value, d.height);
+ }
+ };
+ }
+
+ /**
+ * Return a new Spring which takes its values from the specified
+ * Component. In particular, the maximum value is taken from
+ * the maximumSize, the minimum value is taken from the minimumSize,
+ * the preferred value is taken from the preferredSize, and the
+ * value is taken from the component's current size. These values
+ * change as the component changes size.
+ * @param component the component
+ * @return a new Spring which tracks the component's height
+ * @since 1.5
+ */
+ public static Spring height(final Component component)
+ {
+ return new Spring()
+ {
+ public int getMaximumValue()
+ {
+ return component.getMaximumSize().height;
+ }
+
+ public int getMinimumValue()
+ {
+ return component.getMinimumSize().height;
+ }
+
+ public int getPreferredValue()
+ {
+ return component.getPreferredSize().height;
+ }
+
+ public int getValue()
+ {
+ return component.getSize().height;
+ }
+
+ public void setValue(int value)
+ {
+ Dimension d = component.getSize();
+ component.setSize(d.width, value);
+ }
+ };
+ }
+
+ /**
+ * A simple Spring, that holds constant values for min, pref and max.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class SimpleSpring extends Spring
+ {
+
+ /** The constant value for min. */
+ private final int min;
+
+ /** The constant value for pref. */
+ private final int pref;
+
+ /** The constant value for max. */
+ private final int max;
+
+ /** The actual value of the spring. */
+ private int value;
+
+ public String toString()
+ {
+ return "SimpleSpring of " + value;
+ }
+
+ /**
+ * Creates a new SimpleSpring object.
+ *
+ * @param newMin the constant minimum value.
+ * @param newPref the constant preferred value.
+ * @param newMax the constant maximum value.
+ */
+ public SimpleSpring(int newMin, int newPref, int newMax)
+ {
+ min = newMin;
+ pref = newPref;
+ max = newMax;
+ value = newPref;
+ }
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ return max;
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ return min;
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ return pref;
+ }
+
+ /**
+ * Return the actual current value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ return pref;
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ value = val;
+ }
+ }
+
+
+ /**
+ * A Spring, that is the sum of two other Springs.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class AddSpring extends Spring
+ {
+
+ /** The springs, that are the 'operands' of this Spring. */
+ private final Spring s1;
+ private final Spring s2;
+
+ /** The current value for this Spring. */
+ private int value;
+
+ public String toString()
+ {
+ return "AddSpring of " + s1 + " and " + s2;
+ }
+
+ /**
+ * Creates a new AddSpring object.
+ *
+ * @param s1 the first operand.
+ * @param s2 the second operand.
+ */
+ protected AddSpring(Spring s1, Spring s2)
+ {
+ super();
+ this.s1 = s1;
+ this.s2 = s2;
+ value = Spring.UNSET;
+ }
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ int max1 = s1.getMaximumValue();
+ int max2 = s2.getMaximumValue();
+ return max1 + max2;
+ }
+
+ /**
+ * Return the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ int min1 = s1.getMinimumValue();
+ int min2 = s2.getMinimumValue();
+ return min1 + min2;
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ int pref1 = s1.getPreferredValue();
+ int pref2 = s2.getPreferredValue();
+ return pref1 + pref2;
+ }
+
+ /**
+ * Returns the actual current value of this Spring.
+ *
+ * @return the current value of this Spring.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ {
+ int val1 = s1.getValue();
+ int val2 = s2.getValue();
+ value = val1 + val2;
+ }
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ {
+ if (value != Spring.UNSET)
+ {
+ s1.setValue(Spring.UNSET);
+ s2.setValue(Spring.UNSET);
+ }
+ value = Spring.UNSET;
+ return;
+ }
+
+ value = val;
+
+ //Spead the value over the two components
+ double fStrain = getStrain();
+ s1.setStrain(fStrain);
+ int remainder = val - s1.getValue();
+ s2.setValue(remainder);
+ }
+
+ }
+
+
+ /**
+ * A Spring that is calculated as the negation of another Spring.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class MinusSpring extends Spring
+ {
+
+ /** The Spring from which to calculate the negation. */
+ private final Spring s;
+
+ public String toString()
+ {
+ return "MinusSpring of " + s;
+ }
+
+ /**
+ * Creates a new MinusSpring object.
+ * @param s the Spring from which to calculate the negation.
+ */
+ protected MinusSpring(Spring s)
+ {
+ super();
+ this.s = s;
+ }
+
+ /** Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ return -s.getMinimumValue();
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ return -s.getMaximumValue();
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ return -s.getPreferredValue();
+ }
+
+ /**
+ * Returns the current value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ return -s.getValue();
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ s.setValue(Spring.UNSET);
+ else
+ s.setValue(-val);
+ }
+ }
+
+
+ /**
+ * A Spring, that is calculated as the maximum of two Springs.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ private static final class MaxSpring extends Spring
+ {
+
+ /** The two other Springs from which to calculate the maximum. */
+ private final Spring s1;
+ private final Spring s2;
+
+ public String toString()
+ {
+ return "MaxSpring of " + s1 + " and " + s2;
+ }
+
+ /** The current value of this Spring. */
+ private int value;
+
+ /**
+ * Creates a new MaxSpring object.
+ *
+ * @param s1 the 1st operand.
+ * @param s2 the 2nd operand.
+ */
+ protected MaxSpring(Spring s1, Spring s2)
+ {
+ super();
+ this.s1 = s1;
+ this.s2 = s2;
+ value = Spring.UNSET;
+ }
+
+
+ /**
+ * Returns the maximum value of this Spring.
+ *
+ * @return the maximum value.
+ */
+ public int getMaximumValue()
+ {
+ int max1 = s1.getMaximumValue();
+ int max2 = s2.getMaximumValue();
+ return Math.max(max1, max2);
+ }
+
+ /**
+ * Returns the minimum value of this Spring.
+ *
+ * @return the minimum value.
+ */
+ public int getMinimumValue()
+ {
+ int min1 = s1.getMinimumValue();
+ int min2 = s2.getMinimumValue();
+ return Math.max(min1, min2);
+ }
+
+ /**
+ * Returns the preferred value of this Spring.
+ *
+ * @return the preferred value.
+ */
+ public int getPreferredValue()
+ {
+ int pref1 = s1.getPreferredValue();
+ int pref2 = s2.getPreferredValue();
+ return Math.max(pref1, pref2);
+ }
+
+ /**
+ * Returns the actual value of this Spring.
+ *
+ * @return the current value.
+ */
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ {
+ int val1 = s1.getValue();
+ int val2 = s2.getValue();
+ value = Math.max(val1, val2);
+ }
+ return value;
+ }
+
+ /**
+ * Sets the current value.
+ *
+ * @param val the value to be set.
+ */
+ public void setValue(int val)
+ {
+ if (val == Spring.UNSET)
+ {
+ if (value != Spring.UNSET)
+ {
+ s1.setValue(Spring.UNSET);
+ s2.setValue(Spring.UNSET);
+ }
+ value = Spring.UNSET;
+ return;
+ }
+
+ value = val;
+
+ int p1 = s1.getPreferredValue();
+ int p2 = s2.getPreferredValue();
+
+ if (p1 < p2)
+ {
+ s1.setValue(Math.min(val, p1));
+ s2.setValue(val);
+ }
+ else
+ {
+ s1.setValue(val);
+ s2.setValue(Math.min(val, p2));
+ }
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/SpringLayout.java b/libjava/classpath/javax/swing/SpringLayout.java
new file mode 100644
index 000000000..2be5189b5
--- /dev/null
+++ b/libjava/classpath/javax/swing/SpringLayout.java
@@ -0,0 +1,832 @@
+/* SpringLayout.java --
+ Copyright (C) 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.LayoutManager2;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A very flexible layout manager. Components are laid out by defining the
+ * relationships between them. The relationships are expressed as
+ * {@link Spring}s. You can attach a Spring for each edge of a component and
+ * link it to an edge of a different component. For example, you can say,
+ * the northern edge of component A should be attached to the southern edge
+ * of component B, and the space between them should be something between
+ * x and y pixels, and preferably z pixels.
+ *
While quite simple, this layout manager can be used to emulate most other
+ * layout managers, and can also be used to solve some layout problems, which
+ * would be hard to solve with other layout managers.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+public class SpringLayout implements LayoutManager2
+{
+
+ /** The right edge of a component. */
+ public static final String EAST = "East";
+
+ /** The top edge of a component. */
+ public static final String NORTH = "North";
+
+ /** The bottom edge of a component. */
+ public static final String SOUTH = "South";
+
+ /** The left edge of a component. */
+ public static final String WEST = "West";
+
+ /** maps components to their constraints. */
+ private Map constraintsMap;
+
+ /**
+ * The constraints that define the relationships between components.
+ * Each Constraints object can hold 4 Springs: one for each edge of the
+ * component. Additionally it can hold Springs for the components width
+ * and the components height. Since the height and width constraints are
+ * dependend on the other constraints, a component can be over-constraint.
+ * In this case (like when all of NORTH, SOUTH and HEIGHT are constraint),
+ * the values are adjusted, so that the mathematics still hold true.
+ *
+ * @author Roman Kennke (roman@ontographics.com)
+ */
+ public static class Constraints
+ {
+
+ // The constraints for each edge, and width and height.
+ /** The Spring for the left edge. */
+ private Spring x;
+
+ /** The Spring for the upper edge. */
+ private Spring y;
+
+ /** The Spring for the height. */
+ private Spring height;
+
+ /** The Spring for the width. */
+ private Spring width;
+
+ /** The Spring for the right edge. */
+ private Spring east;
+
+ /** The Spring for the bottom edge. */
+ private Spring south;
+
+ /**
+ In each axis the user can set three values, i.e. x, width, east, if all
+ three are set, then there's no room for manoeuvre so in those cases the
+ third will be described by the below spring which is calculated in terms
+ of the other two
+ */
+ private Spring v;
+ private Spring h;
+
+ /**
+ * Creates a new Constraints object.
+ * There is no constraint set.
+ */
+ public Constraints()
+ {
+ x = y = height = width = east = south = v = h = null;
+ }
+
+ /**
+ * Creates a new Constraints object.
+ *
+ * @param x the constraint for the left edge of the component.
+ * @param y the constraint for the upper edge of the component.
+ */
+ public Constraints(Spring x, Spring y)
+ {
+ this.x = x;
+ this.y = y;
+ width = height = east = south = v = h = null;
+ }
+
+ /**
+ * Creates a new Constraints object.
+ *
+ * @param x the constraint for the left edge of the component.
+ * @param y the constraint for the upper edge of the component.
+ * @param width the constraint for the width of the component.
+ * @param height the constraint for the height of the component.
+ */
+ public Constraints(Spring x, Spring y, Spring width, Spring height)
+ {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ east = south = v = h = null;
+ }
+
+ /**
+ * Create a new Constraints object which tracks the indicated
+ * component. The x and y positions for this Constraints object
+ * are constant Springs created with the component's location at
+ * the time this constructor is called. The width and height
+ * of this Constraints are Springs created using
+ * {@link Spring#width(Component)} and {@link Spring#height(Component)},
+ * respectively.
+ * @param component the component to track
+ * @since 1.5
+ */
+ public Constraints(Component component)
+ {
+ this(Spring.constant(component.getX()),
+ Spring.constant(component.getY()),
+ Spring.width(component),
+ Spring.height(component));
+ }
+
+ /**
+ * Returns the constraint for the edge with the edgeName.
+ * This is expected to be one of
+ * {@link #EAST}, {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ *
+ * @param edgeName the name of the edge.
+ * @return the constraint for the specified edge.
+ */
+ public Spring getConstraint(String edgeName)
+ {
+ Spring retVal = null;
+ if (edgeName.equals(SpringLayout.NORTH))
+ retVal = getY();
+ else if (edgeName.equals(SpringLayout.WEST))
+ retVal = getX();
+ else if (edgeName.equals(SpringLayout.SOUTH))
+ retVal = getSouth();
+ else if (edgeName.equals(SpringLayout.EAST))
+ retVal = getEast();
+ return retVal;
+ }
+
+ /**
+ * Returns the constraint for the height of the component.
+ *
+ * @return the height constraint.
+ */
+ public Spring getHeight()
+ {
+ if (height != null)
+ return height;
+ else if ((v == null) && (y != null) && (south != null))
+ v = Spring.sum(south, Spring.minus(y));
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the width of the component.
+ *
+ * @return the width constraint.
+ */
+ public Spring getWidth()
+ {
+ if (width != null)
+ return width;
+ else if ((h == null) && (x != null) && (east != null))
+ h = Spring.sum(east, Spring.minus(x));
+ return h;
+ }
+
+ /**
+ * Returns the constraint for the left edge of the component.
+ *
+ * @return the left-edge constraint (== WEST).
+ */
+ public Spring getX()
+ {
+ if (x != null)
+ return x;
+ else if ((h == null) && (width != null) && (east != null))
+ h = Spring.sum(east, Spring.minus(width));
+ return h;
+ }
+
+ /**
+ * Returns the constraint for the upper edge of the component.
+ *
+ * @return the upper-edge constraint (== NORTH).
+ */
+ public Spring getY()
+ {
+ if (y != null)
+ return y;
+ else if ((v == null) && (height != null) && (south != null))
+ v = Spring.sum(south, Spring.minus(height));
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the lower edge of the component.
+ *
+ * @return the lower-edge constraint (== SOUTH).
+ */
+ public Spring getSouth()
+ {
+ if (south != null)
+ return south;
+ else if ((v == null) && (height != null) && (y != null))
+ v = Spring.sum(y, height);
+ return v;
+ }
+
+ /**
+ * Returns the constraint for the right edge of the component.
+ *
+ * @return the right-edge constraint (== EAST).
+ */
+ public Spring getEast()
+ {
+ if (east != null)
+ return east;
+ else if ((h == null) && (width != null) && (x != null))
+ h = Spring.sum(x, width);
+ return h;
+ }
+
+ /**
+ * Sets a constraint for the specified edge. If this leads to an
+ * over-constrained situation, the constraints get adjusted, so that
+ * the mathematics still hold true.
+ *
+ * @param edgeName the name of the edge, one of {@link #EAST},
+ * {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ * @param s the constraint to be set.
+ */
+ public void setConstraint(String edgeName, Spring s)
+ {
+
+ if (edgeName.equals(SpringLayout.WEST))
+ setX(s);
+ else if (edgeName.equals(SpringLayout.NORTH))
+ setY(s);
+ else if (edgeName.equals(SpringLayout.EAST))
+ setEast(s);
+ else if (edgeName.equals(SpringLayout.SOUTH))
+ setSouth(s);
+
+ }
+
+ /**
+ * Sets the height-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setHeight(Spring s)
+ {
+ height = s;
+ v = null;
+ if ((south != null) && (y != null) && (height != null))
+ south = null;
+ }
+
+ /**
+ * Sets the width-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setWidth(Spring s)
+ {
+ width = s;
+ h = null;
+ if ((east != null) && (x != null) && (width != null))
+ east = null;
+ }
+
+ /**
+ * Sets the WEST-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setX(Spring s)
+ {
+ x = s;
+ h = null;
+ if ((width != null) && (east != null) && (x != null))
+ width = null;
+ }
+
+ /**
+ * Sets the NORTH-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setY(Spring s)
+ {
+ y = s;
+ v = null;
+ if ((height != null) && (south != null) && (y != null))
+ height = null;
+ }
+
+ /**
+ * Sets the SOUTH-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setSouth(Spring s)
+ {
+ south = s;
+ v = null;
+ if ((height != null) && (south != null) && (y != null))
+ y = null;
+ }
+
+ /**
+ * Sets the EAST-constraint.
+ *
+ * @param s the constraint to be set.
+ */
+ public void setEast(Spring s)
+ {
+ east = s;
+ h = null;
+ if ((width != null) && (east != null) && (x != null))
+ x = null;
+ }
+
+ public void dropCalcResult()
+ {
+ if (x != null)
+ x.setValue(Spring.UNSET);
+ if (y != null)
+ y.setValue(Spring.UNSET);
+ if (width != null)
+ width.setValue(Spring.UNSET);
+ if (height != null)
+ height.setValue(Spring.UNSET);
+ if (east != null)
+ east.setValue(Spring.UNSET);
+ if (south != null)
+ south.setValue(Spring.UNSET);
+ if (h != null)
+ h.setValue(Spring.UNSET);
+ if (v != null)
+ v.setValue(Spring.UNSET);
+ }
+ }
+
+ /**
+ * Creates a new SpringLayout.
+ */
+ public SpringLayout()
+ {
+ constraintsMap = new HashMap();
+ }
+
+ /**
+ * Adds a layout component and a constraint object to this layout.
+ * This method is usually only called by a {@link java.awt.Container}s add
+ * method.
+ *
+ * @param component the component to be added.
+ * @param constraint the constraint to be set.
+ */
+ public void addLayoutComponent(Component component, Object constraint)
+ {
+ constraintsMap.put(component, constraint);
+ }
+
+ /**
+ * Adds a layout component and a constraint object to this layout.
+ * This method is usually only called by a {@link java.awt.Container}s add
+ * method. This method does nothing, since SpringLayout does not manage
+ * String-indexed components.
+ *
+ * @param name the name.
+ * @param c the component to be added.
+ */
+ public void addLayoutComponent(String name, Component c)
+ {
+ // do nothing here.
+ }
+
+ /**
+ * The trick to SpringLayout is that the network of Springs needs to
+ * completely created before the positioning results are generated.
+ *
+ * Using the springs directly during network creation will set their values
+ * before the network is completed, Using Deferred Springs during creation of
+ * the network allows all the edges to be connected together and the network
+ * to be created without resolving the Springs until their results need to be
+ * known, at which point the network is complete and the spring addition and
+ * and substitution calculations will work on a complete and valid network.
+ *
+ * @author Caolan McNamara (caolanm@redhat.com)
+ */
+ private static class DeferredSpring extends Spring
+ {
+ private SpringLayout sl;
+ private String edgeName;
+ private Component c;
+
+ public String toString()
+ {
+ return "DeferredSpring of edge" + edgeName + " of " + "something";
+ }
+
+ public DeferredSpring(SpringLayout s, String edge, Component component)
+ {
+ sl = s;
+ edgeName = edge;
+ c = component;
+ }
+
+ private Spring resolveSpring()
+ {
+ return sl.getConstraints(c).getConstraint(edgeName);
+ }
+
+ public int getMaximumValue()
+ {
+ return resolveSpring().getMaximumValue();
+ }
+
+ public int getMinimumValue()
+ {
+ return resolveSpring().getMinimumValue();
+ }
+
+ public int getPreferredValue()
+ {
+ return resolveSpring().getPreferredValue();
+ }
+
+ public int getValue()
+ {
+ int nRet = resolveSpring().getValue();
+ if (nRet == Spring.UNSET)
+ nRet = getPreferredValue();
+ return nRet;
+ }
+
+ public void setValue(int size)
+ {
+ resolveSpring().setValue(size);
+ }
+ }
+
+ private abstract static class DeferredDimension extends Spring
+ {
+ private int value;
+
+ public DeferredDimension()
+ {
+ value = Spring.UNSET;
+ }
+
+ public void setValue(int val)
+ {
+ value = val;
+ }
+
+ public int getValue()
+ {
+ if (value == Spring.UNSET)
+ return getPreferredValue();
+ return value;
+ }
+ }
+
+ private static class DeferredWidth extends DeferredDimension
+ {
+ private Component c;
+
+
+ public DeferredWidth(Component component)
+ {
+ c = component;
+ }
+
+ public String toString()
+ {
+ return "DeferredWidth of " + "something";
+ }
+
+ //clip max to a value we can do meaningful calculation with
+ public int getMaximumValue()
+ {
+ int widget_width = c.getMaximumSize().width;
+ return Math.min(Short.MAX_VALUE, widget_width);
+ }
+
+ public int getMinimumValue()
+ {
+ return c.getMinimumSize().width;
+ }
+
+ public int getPreferredValue()
+ {
+ return c.getPreferredSize().width;
+ }
+ }
+
+ private static class DeferredHeight extends DeferredDimension
+ {
+ private Component c;
+
+ public String toString()
+ {
+ return "DeferredHeight of " + "something";
+ }
+
+ public DeferredHeight(Component component)
+ {
+ c = component;
+ }
+
+ //clip max to a value we can do meaningful calculations with it
+ public int getMaximumValue()
+ {
+ int widget_height = c.getMaximumSize().height;
+ return Math.min(Short.MAX_VALUE, widget_height);
+ }
+
+ public int getMinimumValue()
+ {
+ return c.getMinimumSize().height;
+ }
+
+ public int getPreferredValue()
+ {
+ return c.getPreferredSize().height;
+ }
+ }
+
+ /**
+ * Returns the constraint of the edge named by edgeName.
+ *
+ * @param c the component from which to get the constraint.
+ * @param edgeName the name of the edge, one of {@link #EAST},
+ * {@link #WEST}, {@link #NORTH} or {@link #SOUTH}.
+ * @return the constraint of the edge edgeName of the
+ * component c.
+ */
+ public Spring getConstraint(String edgeName, Component c)
+ {
+ return new DeferredSpring(this, edgeName, c);
+ }
+
+ /**
+ * Returns the {@link Constraints} object associated with the specified
+ * component.
+ *
+ * @param c the component for which to determine the constraint.
+ * @return the {@link Constraints} object associated with the specified
+ * component.
+ */
+ public SpringLayout.Constraints getConstraints(Component c)
+ {
+ Constraints constraints = (Constraints) constraintsMap.get(c);
+
+ if (constraints == null)
+ {
+ constraints = new Constraints();
+
+ constraints.setWidth(new DeferredWidth(c));
+ constraints.setHeight(new DeferredHeight(c));
+ constraints.setX(Spring.constant(0));
+ constraints.setY(Spring.constant(0));
+
+ constraintsMap.put(c, constraints);
+ }
+
+ return constraints;
+ }
+
+ /**
+ * Returns the X alignment of the Container p.
+ *
+ * @param p
+ * the {@link java.awt.Container} for which to determine the X
+ * alignment.
+ * @return always 0.0
+ */
+ public float getLayoutAlignmentX(Container p)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * Returns the Y alignment of the Container p.
+ *
+ * @param p the {@link java.awt.Container} for which to determine the Y
+ * alignment.
+ * @return always 0.0
+ */
+ public float getLayoutAlignmentY(Container p)
+ {
+ return 0.0F;
+ }
+
+ /**
+ * Recalculate a possibly cached layout.
+ */
+ public void invalidateLayout(Container p)
+ {
+ // nothing to do here yet
+ }
+
+ private Constraints initContainer(Container p)
+ {
+ Constraints c = getConstraints(p);
+
+ c.setX(Spring.constant(0));
+ c.setY(Spring.constant(0));
+ c.setWidth(null);
+ c.setHeight(null);
+ if (c.getEast() == null)
+ c.setEast(Spring.constant(0, 0, Integer.MAX_VALUE));
+ if (c.getSouth() == null)
+ c.setSouth(Spring.constant(0, 0, Integer.MAX_VALUE));
+
+ return c;
+ }
+
+ /**
+ * Lays out the container p.
+ *
+ * @param p the container to be laid out.
+ */
+ public void layoutContainer(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Component[] components = p.getComponents();
+
+ Constraints cs = initContainer(p);
+ cs.dropCalcResult();
+
+ for (int index = 0 ; index < components.length; index++)
+ {
+ Component c = components[index];
+ getConstraints(c).dropCalcResult();
+ }
+
+ int offsetX = p.getInsets().left;
+ int offsetY = p.getInsets().right;
+
+ cs.getX().setValue(0);
+ cs.getY().setValue(0);
+ cs.getWidth().setValue(p.getWidth() - offsetX - insets.right);
+ cs.getHeight().setValue(p.getHeight() - offsetY - insets.bottom);
+
+ for (int index = 0; index < components.length; index++)
+ {
+ Component c = components[index];
+
+ Constraints constraints = getConstraints(c);
+
+ int x = constraints.getX().getValue();
+ int y = constraints.getY().getValue();
+ int width = constraints.getWidth().getValue();
+ int height = constraints.getHeight().getValue();
+
+ c.setBounds(x + offsetX, y + offsetY, width, height);
+ }
+ }
+
+ /**
+ * Calculates the maximum size of the layed out container. This
+ * respects the maximum sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the maximum size of the container.
+ */
+ public Dimension maximumLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getMaximumValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getMaximumValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+
+ /**
+ * Calculates the minimum size of the layed out container. This
+ * respects the minimum sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the minimum size of the container.
+ */
+ public Dimension minimumLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getMinimumValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getMinimumValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+ /**
+ * Calculates the preferred size of the layed out container. This
+ * respects the preferred sizes of all contained components.
+ *
+ * @param p the container to be laid out.
+ * @return the preferred size of the container.
+ */
+ public Dimension preferredLayoutSize(Container p)
+ {
+ java.awt.Insets insets = p.getInsets();
+
+ Constraints cs = initContainer(p);
+
+ int maxX = cs.getWidth().getPreferredValue() + insets.left + insets.right;
+ int maxY = cs.getHeight().getPreferredValue() + insets.top + insets.bottom;
+
+ return new Dimension(maxX, maxY);
+ }
+
+ /**
+ * Attaches the edge e1 of component c1 to
+ * the edge e2 of component c2 width the
+ * fixed strut pad.
+ *
+ * @param e1 the edge of component 1.
+ * @param c1 the component 1.
+ * @param pad the space between the components in pixels.
+ * @param e2 the edge of component 2.
+ * @param c2 the component 2.
+ */
+ public void putConstraint(String e1, Component c1, int pad, String e2,
+ Component c2)
+ {
+ putConstraint(e1, c1, Spring.constant(pad), e2, c2);
+ }
+
+ /**
+ * Attaches the edge e1 of component c1 to
+ * the edge e2 of component c2 width the
+ * {@link Spring} s.
+ *
+ * @param e1 the edge of component 1.
+ * @param c1 the component 1.
+ * @param s the space between the components as a {@link Spring} object.
+ * @param e2 the edge of component 2.
+ * @param c2 the component 2.
+ */
+ public void putConstraint(String e1, Component c1, Spring s, String e2,
+ Component c2)
+ {
+ Constraints constraints1 = getConstraints(c1);
+
+ Spring otherEdge = getConstraint(e2, c2);
+ constraints1.setConstraint(e1, Spring.sum(s, otherEdge));
+
+ }
+
+ /**
+ * Removes a layout component.
+ * @param c the layout component to remove.
+ */
+ public void removeLayoutComponent(Component c)
+ {
+ // do nothing here
+ }
+}
diff --git a/libjava/classpath/javax/swing/SwingConstants.java b/libjava/classpath/javax/swing/SwingConstants.java
new file mode 100644
index 000000000..fccf2ea95
--- /dev/null
+++ b/libjava/classpath/javax/swing/SwingConstants.java
@@ -0,0 +1,76 @@
+/* SwingConstants.java --
+ Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+/**
+ * Defines constant values that are used throughout the Swing packages.
+ */
+public interface SwingConstants
+{
+ int CENTER = 0;
+ int TOP = 1;
+ int LEFT = 2;
+ int BOTTOM = 3;
+ int RIGHT = 4;
+
+ int NORTH = 1;
+ int NORTH_EAST = 2;
+ int EAST = 3;
+ int SOUTH_EAST = 4;
+ int SOUTH = 5;
+ int SOUTH_WEST = 6;
+ int WEST = 7;
+ int NORTH_WEST = 8;
+
+ int HORIZONTAL = 0;
+ int VERTICAL = 1;
+
+ int LEADING = 10;
+ int TRAILING = 11;
+
+ /**
+ * @since 1.4
+ */
+ int NEXT = 12;
+
+ /**
+ * @since 1.4
+ */
+ int PREVIOUS = 13;
+}
diff --git a/libjava/classpath/javax/swing/SwingUtilities.java b/libjava/classpath/javax/swing/SwingUtilities.java
new file mode 100644
index 000000000..61047323f
--- /dev/null
+++ b/libjava/classpath/javax/swing/SwingUtilities.java
@@ -0,0 +1,1754 @@
+/* SwingUtilities.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.applet.Applet;
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.FontMetrics;
+import java.awt.Frame;
+import java.awt.Graphics;
+import java.awt.Insets;
+import java.awt.KeyboardFocusManager;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseEvent;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleStateSet;
+import javax.swing.plaf.ActionMapUIResource;
+import javax.swing.plaf.InputMapUIResource;
+import javax.swing.plaf.basic.BasicHTML;
+import javax.swing.text.View;
+
+/**
+ * A number of static utility functions which are
+ * useful when drawing swing components, dispatching events, or calculating
+ * regions which need painting.
+ *
+ * @author Graydon Hoare (graydon@redhat.com)
+ * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
+ */
+public class SwingUtilities
+ implements SwingConstants
+{
+ /**
+ * This frame should be used as parent for JWindow or JDialog
+ * that doesn't an owner
+ */
+ private static OwnerFrame ownerFrame;
+
+ private SwingUtilities()
+ {
+ // Do nothing.
+ }
+
+ /**
+ * Calculates the portion of the component's bounds which is inside the
+ * component's border insets. This area is usually the area a component
+ * should confine its painting to. The coordinates are returned in terms
+ * of the component's coordinate system, where (0,0) is the
+ * upper left corner of the component's bounds.
+ *
+ * @param c the component to measure the bounds of (if null,
+ * this method returns null).
+ * @param r a carrier to store the return value in (if null, a
+ * new Rectangle instance is created).
+ *
+ * @return The calculated area inside the component and its border insets.
+ */
+ public static Rectangle calculateInnerArea(JComponent c, Rectangle r)
+ {
+ if (c == null)
+ return null;
+ r = c.getBounds(r);
+ Insets i = c.getInsets();
+ r.x = i.left;
+ r.width = r.width - i.left - i.right;
+ r.y = i.top;
+ r.height = r.height - i.top - i.bottom;
+ return r;
+ }
+
+ /**
+ * Returns the focus owner or null if comp is not
+ * the focus owner or a parent of it.
+ *
+ * @param comp the focus owner or a parent of it
+ *
+ * @return the focus owner, or null
+ *
+ * @deprecated 1.4 Replaced by
+ * KeyboardFocusManager.getFocusOwner().
+ */
+ public static Component findFocusOwner(Component comp)
+ {
+ // Get real focus owner.
+ Component focusOwner = KeyboardFocusManager.getCurrentKeyboardFocusManager()
+ .getFocusOwner();
+
+ // Check if comp is the focus owner or a parent of it.
+ Component tmp = focusOwner;
+
+ while (tmp != null)
+ {
+ if (tmp == comp)
+ return focusOwner;
+
+ tmp = tmp.getParent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the Accessible child of the specified component
+ * which appears at the supplied Point. If there is no
+ * child located at that particular pair of co-ordinates, null is returned
+ * instead.
+ *
+ * @param c the component whose children may be found at the specified
+ * point.
+ * @param p the point at which to look for the existence of children
+ * of the specified component.
+ * @return the Accessible child at the point, p,
+ * or null if there is no child at this point.
+ * @see javax.accessibility.AccessibleComponent#getAccessibleAt
+ */
+ public static Accessible getAccessibleAt(Component c, Point p)
+ {
+ return c.getAccessibleContext().getAccessibleComponent().getAccessibleAt(p);
+ }
+
+ /**
+ *
+ * Returns the Accessible child of the specified component
+ * that has the supplied index within the parent component. The indexing
+ * of the children is zero-based, making the first child have an index of
+ * 0.
+ *
+ *
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ *
+ *
+ * @param c the component whose child should be returned.
+ * @param i the index of the child within the parent component.
+ * @return the Accessible child at index i
+ * in the component, c.
+ * @see javax.accessibility.AccessibleContext#getAccessibleChild
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChild
+ */
+ public static Accessible getAccessibleChild(Component c, int i)
+ {
+ return c.getAccessibleContext().getAccessibleChild(i);
+ }
+
+ /**
+ *
+ * Returns the number of Accessible children within
+ * the supplied component.
+ *
+ *
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ *
+ *
+ * @param c the component whose children should be counted.
+ * @return the number of children belonging to the component,
+ * c.
+ * @see javax.accessibility.AccessibleContext#getAccessibleChildrenCount
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleChildrenCount
+ */
+ public static int getAccessibleChildrenCount(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleChildrenCount();
+ }
+
+ /**
+ *
+ * Returns the zero-based index of the specified component
+ * within its parent. If the component doesn't have a parent,
+ * -1 is returned.
+ *
+ *
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ *
+ *
+ * @param c the component whose parental index should be found.
+ * @return the index of the component within its parent, or -1
+ * if the component doesn't have a parent.
+ * @see javax.accessibility.AccessibleContext#getAccessibleIndexInParent
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleIndexInParent
+ */
+ public static int getAccessibleIndexInParent(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleIndexInParent();
+ }
+
+ /**
+ *
+ * Returns a set of AccessibleStates, which represent
+ * the state of the supplied component.
+ *
+ *
+ * Caution is advised when using this method, as its operation relies
+ * on the behaviour of varying implementations of an abstract method.
+ * For greater surety, direct use of the AWT component implementation
+ * of this method is advised.
+ *
+ *
+ * @param c the component whose accessible state should be retrieved.
+ * @return a set of AccessibleState objects, which represent
+ * the state of the supplied component.
+ * @see javax.accessibility.AccessibleContext#getAccessibleStateSet
+ * @see java.awt.Component.AccessibleAWTComponent#getAccessibleStateSet
+ */
+ public static AccessibleStateSet getAccessibleStateSet(Component c)
+ {
+ return c.getAccessibleContext().getAccessibleStateSet();
+ }
+
+ /**
+ * Calculates the bounds of a component in the component's own coordinate
+ * space. The result has the same height and width as the component's
+ * bounds, but its location is set to (0,0).
+ *
+ * @param aComponent The component to measure
+ *
+ * @return The component's bounds in its local coordinate space
+ */
+ public static Rectangle getLocalBounds(Component aComponent)
+ {
+ Rectangle bounds = aComponent.getBounds();
+ return new Rectangle(0, 0, bounds.width, bounds.height);
+ }
+
+ /**
+ * If comp is a RootPaneContainer, return its JRootPane.
+ * Otherwise call getAncestorOfClass(JRootPane.class, a).
+ *
+ * @param comp The component to get the JRootPane of
+ *
+ * @return a suitable JRootPane for comp, or null
+ *
+ * @see javax.swing.RootPaneContainer#getRootPane
+ * @see #getAncestorOfClass
+ */
+ public static JRootPane getRootPane(Component comp)
+ {
+ if (comp instanceof RootPaneContainer)
+ return ((RootPaneContainer)comp).getRootPane();
+ else
+ return (JRootPane) getAncestorOfClass(JRootPane.class, comp);
+ }
+
+ /**
+ * Returns the least ancestor of comp which has the
+ * specified name.
+ *
+ * @param name The name to search for
+ * @param comp The component to search the ancestors of
+ *
+ * @return The nearest ancestor of comp with the given
+ * name, or null if no such ancestor exists
+ *
+ * @see java.awt.Component#getName
+ * @see #getAncestorOfClass
+ */
+ public static Container getAncestorNamed(String name, Component comp)
+ {
+ while (comp != null && (comp.getName() != name))
+ comp = comp.getParent();
+ return (Container) comp;
+ }
+
+ /**
+ * Returns the least ancestor of comp which is an instance
+ * of the specified class.
+ *
+ * @param c The class to search for
+ * @param comp The component to search the ancestors of
+ *
+ * @return The nearest ancestor of comp which is an instance
+ * of the given class, or null if no such ancestor exists
+ *
+ * @see #getAncestorOfClass
+ * @see #windowForComponent
+ */
+ public static Container getAncestorOfClass(Class> c, Component comp)
+ {
+ while (comp != null && (! c.isInstance(comp)))
+ comp = comp.getParent();
+ return (Container) comp;
+ }
+
+ /**
+ * Returns the first ancestor of comp that is a {@link Window}
+ * or null if comp is not contained in a
+ * {@link Window}.
+ *
+ * This is equivalent to calling
+ * getAncestorOfClass(Window, comp) or
+ * windowForComponent(comp).
+ *
+ * @param comp the component for which we are searching the ancestor Window
+ *
+ * @return the first ancestor Window of comp or
+ * null if comp is not contained in a Window
+ */
+ public static Window getWindowAncestor(Component comp)
+ {
+ return (Window) getAncestorOfClass(Window.class, comp);
+ }
+
+ /**
+ * Equivalent to calling getAncestorOfClass(Window, comp).
+ *
+ * @param comp The component to search for an ancestor window
+ *
+ * @return An ancestral window, or null if none exists
+ */
+ public static Window windowForComponent(Component comp)
+ {
+ return (Window) getAncestorOfClass(Window.class, comp);
+ }
+
+ /**
+ * Returns the "root" of the component tree containint comp
+ * The root is defined as either the least ancestor of
+ * comp which is a {@link Window}, or the greatest
+ * ancestor of comp which is a {@link Applet} if no {@link
+ * Window} ancestors are found.
+ *
+ * @param comp The component to search for a root
+ *
+ * @return The root of the component's tree, or null
+ */
+ public static Component getRoot(Component comp)
+ {
+ Applet app = null;
+ Window win = null;
+
+ while (comp != null)
+ {
+ if (win == null && comp instanceof Window)
+ win = (Window) comp;
+ else if (comp instanceof Applet)
+ app = (Applet) comp;
+ comp = comp.getParent();
+ }
+
+ if (win != null)
+ return win;
+ return app;
+ }
+
+ /**
+ * Return true if a descends from b, in other words if b is an ancestor of a.
+ *
+ * @param a The child to search the ancestry of
+ * @param b The potential ancestor to search for
+ * @return true if a is a descendent of b, false otherwise
+ */
+ public static boolean isDescendingFrom(Component a, Component b)
+ {
+ while (true)
+ {
+ if (a == null || b == null)
+ return false;
+ if (a == b)
+ return true;
+ a = a.getParent();
+ }
+ }
+
+ /**
+ * Returns the deepest descendent of parent which is both visible and
+ * contains the point (x,y). Returns parent when either
+ * parent is not a container, or has no children which contain
+ * (x,y). Returns null when either
+ * (x,y) is outside the bounds of parent, or parent is
+ * null.
+ *
+ * @param parent The component to search the descendents of
+ * @param x Horizontal coordinate to search for
+ * @param y Vertical coordinate to search for
+ *
+ * @return A component containing (x,y), or
+ * null
+ *
+ * @see java.awt.Container#findComponentAt(int, int)
+ */
+ public static Component getDeepestComponentAt(Component parent, int x, int y)
+ {
+ if (parent == null || (! parent.contains(x, y)))
+ return null;
+
+ if (! (parent instanceof Container))
+ return parent;
+
+ Container c = (Container) parent;
+ return c.findComponentAt(x, y);
+ }
+
+ /**
+ * Converts a point from a component's local coordinate space to "screen"
+ * coordinates (such as the coordinate space mouse events are delivered
+ * in). This operation is equivalent to translating the point by the
+ * location of the component (which is the origin of its coordinate
+ * space).
+ *
+ * @param p The point to convert
+ * @param c The component which the point is expressed in terms of
+ *
+ * @see #convertPointFromScreen
+ */
+ public static void convertPointToScreen(Point p, Component c)
+ {
+ Point c0 = c.getLocationOnScreen();
+ p.translate(c0.x, c0.y);
+ }
+
+ /**
+ * Converts a point from "screen" coordinates (such as the coordinate
+ * space mouse events are delivered in) to a component's local coordinate
+ * space. This operation is equivalent to translating the point by the
+ * negation of the component's location (which is the origin of its
+ * coordinate space).
+ *
+ * @param p The point to convert
+ * @param c The component which the point should be expressed in terms of
+ */
+ public static void convertPointFromScreen(Point p, Component c)
+ {
+ Point c0 = c.getLocationOnScreen();
+ p.translate(-c0.x, -c0.y);
+ }
+
+ /**
+ * Converts a point (x,y) from the coordinate space of one
+ * component to another. This is equivalent to converting the point from
+ * source space to screen space, then back from screen space
+ * to destination space. If exactly one of the two
+ * Components is null, it is taken to refer to the root
+ * ancestor of the other component. If both are null, no
+ * transformation is done.
+ *
+ * @param source The component which the point is expressed in terms of
+ * @param x Horizontal coordinate of point to transform
+ * @param y Vertical coordinate of point to transform
+ * @param destination The component which the return value will be
+ * expressed in terms of
+ *
+ * @return The point (x,y) converted from the coordinate space of the
+ * source component to the coordinate space of the destination component
+ *
+ * @see #convertPointToScreen
+ * @see #convertPointFromScreen
+ * @see #convertRectangle
+ * @see #getRoot
+ */
+ public static Point convertPoint(Component source, int x, int y,
+ Component destination)
+ {
+ Point pt = new Point(x, y);
+
+ if (source == null && destination == null)
+ return pt;
+
+ if (source == null)
+ source = getRoot(destination);
+
+ if (destination == null)
+ destination = getRoot(source);
+
+ if (source.isShowing() && destination.isShowing())
+ {
+ convertPointToScreen(pt, source);
+ convertPointFromScreen(pt, destination);
+ }
+
+ return pt;
+ }
+
+ public static Point convertPoint(Component source, Point aPoint, Component destination)
+ {
+ return convertPoint(source, aPoint.x, aPoint.y, destination);
+ }
+
+ /**
+ * Converts a rectangle from the coordinate space of one component to
+ * another. This is equivalent to converting the rectangle from
+ * source space to screen space, then back from screen space
+ * to destination space. If exactly one of the two
+ * Components is null, it is taken to refer to the root
+ * ancestor of the other component. If both are null, no
+ * transformation is done.
+ *
+ * @param source The component which the rectangle is expressed in terms of
+ * @param rect The rectangle to convert
+ * @param destination The component which the return value will be
+ * expressed in terms of
+ *
+ * @return A new rectangle, equal in size to the input rectangle, but
+ * with its position converted from the coordinate space of the source
+ * component to the coordinate space of the destination component
+ *
+ * @see #convertPointToScreen
+ * @see #convertPointFromScreen
+ * @see #convertPoint(Component, int, int, Component)
+ * @see #getRoot
+ */
+ public static Rectangle convertRectangle(Component source,
+ Rectangle rect,
+ Component destination)
+ {
+ Point pt = convertPoint(source, rect.x, rect.y, destination);
+ return new Rectangle(pt.x, pt.y, rect.width, rect.height);
+ }
+
+ /**
+ * Convert a mouse event which refrers to one component to another. This
+ * includes changing the mouse event's coordinate space, as well as the
+ * source property of the event. If source is
+ * null, it is taken to refer to destination's
+ * root component. If destination is null, the
+ * new event will remain expressed in source's coordinate
+ * system.
+ *
+ * @param source The component the mouse event currently refers to
+ * @param sourceEvent The mouse event to convert
+ * @param destination The component the new mouse event should refer to
+ *
+ * @return A new mouse event expressed in terms of the destination
+ * component's coordinate space, and with the destination component as
+ * its source
+ *
+ * @see #convertPoint(Component, int, int, Component)
+ */
+ public static MouseEvent convertMouseEvent(Component source,
+ MouseEvent sourceEvent,
+ Component destination)
+ {
+ Point newpt = convertPoint(source, sourceEvent.getX(), sourceEvent.getY(),
+ destination);
+
+ return new MouseEvent(destination, sourceEvent.getID(),
+ sourceEvent.getWhen(), sourceEvent.getModifiersEx(),
+ newpt.x, newpt.y, sourceEvent.getClickCount(),
+ sourceEvent.isPopupTrigger(), sourceEvent.getButton());
+ }
+
+ /**
+ * Recursively walk the component tree under comp calling
+ * updateUI on each {@link JComponent} found. This causes
+ * the entire tree to re-initialize its UI delegates.
+ *
+ * @param comp The component to walk the children of, calling updateUI
+ */
+ public static void updateComponentTreeUI(Component comp)
+ {
+ updateComponentTreeUIImpl(comp);
+ if (comp instanceof JComponent)
+ {
+ JComponent jc = (JComponent) comp;
+ jc.revalidate();
+ }
+ else
+ {
+ comp.invalidate();
+ comp.validate();
+ }
+ comp.repaint();
+ }
+
+ /**
+ * Performs the actual work for {@link #updateComponentTreeUI(Component)}.
+ * This calls updateUI() on c if it is a JComponent, and then walks down
+ * the component tree and calls this method on each child component.
+ *
+ * @param c the component to update the UI
+ */
+ private static void updateComponentTreeUIImpl(Component c)
+ {
+ if (c instanceof JComponent)
+ {
+ JComponent jc = (JComponent) c;
+ jc.updateUI();
+ }
+
+ Component[] components = null;
+ if (c instanceof JMenu)
+ components = ((JMenu) c).getMenuComponents();
+ else if (c instanceof Container)
+ components = ((Container) c).getComponents();
+ if (components != null)
+ {
+ for (int i = 0; i < components.length; ++i)
+ updateComponentTreeUIImpl(components[i]);
+ }
+ }
+
+ /**
+ *
Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.
+ *
+ *
The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.
+ *
+ *
The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * LEADING, TRAILING, LEFT,
+ * RIGHT or CENTER. The vertical position value
+ * should be one fo the constants TOP, BOTTOM
+ * or CENTER.
+ *
+ *
The text-icon gap value controls the number of pixels between the
+ * icon and the text.
+ *
+ *
The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants LEADING,
+ * TRAILING, LEFT, RIGHT or
+ * CENTER. The vertical alignment valus should be one of the
+ * constants TOP, BOTTOM or
+ * CENTER.
+ *
+ *
If the LEADING or TRAILING constants are
+ * given for horizontal alignment or horizontal text position, they are
+ * interpreted relative to the provided component's orientation property,
+ * a constant in the {@link java.awt.ComponentOrientation} class. For
+ * example, if the component's orientation is LEFT_TO_RIGHT,
+ * then the LEADING value is a synonym for LEFT
+ * and the TRAILING value is a synonym for
+ * RIGHT
+ *
+ *
If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.
+ *
+ * @param c A component used for its orientation value
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+
+ public static String layoutCompoundLabel(JComponent c,
+ FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+
+ // Fix up the orientation-based horizontal positions.
+
+ boolean ltr = true;
+ if (c != null && ! c.getComponentOrientation().isLeftToRight())
+ ltr = false;
+
+ switch (horizontalTextPosition)
+ {
+ case LEADING:
+ horizontalTextPosition = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalTextPosition = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
+ }
+
+ // Fix up the orientation-based alignments.
+ switch (horizontalAlignment)
+ {
+ case LEADING:
+ horizontalAlignment = ltr ? LEFT : RIGHT;
+ break;
+ case TRAILING:
+ horizontalAlignment = ltr ? RIGHT : LEFT;
+ break;
+ default:
+ // Nothing to do in the other cases.
+ }
+
+ return layoutCompoundLabelImpl(c, fm, text, icon,
+ verticalAlignment,
+ horizontalAlignment,
+ verticalTextPosition,
+ horizontalTextPosition,
+ viewR, iconR, textR, textIconGap);
+ }
+
+ /**
+ *
Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.
+ *
+ *
The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.
+ *
+ *
The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * LEFT, RIGHT or CENTER. The
+ * vertical position value should be one fo the constants
+ * TOP, BOTTOM or CENTER.
+ *
+ *
The text-icon gap value controls the number of pixels between the
+ * icon and the text.
+ *
+ *
The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants LEFT, RIGHT or
+ * CENTER. The vertical alignment valus should be one of the
+ * constants TOP, BOTTOM or
+ * CENTER.
+ *
+ *
If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.
+ *
+ *
Note that this method does not know how to deal with
+ * horizontal alignments or positions given as LEADING or
+ * TRAILING values. Use the other overloaded variant of this
+ * method if you wish to use such values.
+ *
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+
+ public static String layoutCompoundLabel(FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+ return layoutCompoundLabelImpl(null, fm, text, icon, verticalAlignment,
+ horizontalAlignment, verticalTextPosition,
+ horizontalTextPosition, viewR, iconR, textR,
+ textIconGap);
+ }
+
+ /**
+ *
Layout a "compound label" consisting of a text string and an icon
+ * which is to be placed near the rendered text. Once the text and icon
+ * are laid out, the text rectangle and icon rectangle parameters are
+ * altered to store the calculated positions.
+ *
+ *
The size of the text is calculated from the provided font metrics
+ * object. This object should be the metrics of the font you intend to
+ * paint the label with.
+ *
+ *
The position values control where the text is placed relative to
+ * the icon. The horizontal position value should be one of the constants
+ * LEFT, RIGHT or CENTER. The
+ * vertical position value should be one fo the constants
+ * TOP, BOTTOM or CENTER.
+ *
+ *
The text-icon gap value controls the number of pixels between the
+ * icon and the text.
+ *
+ *
The alignment values control where the text and icon are placed, as
+ * a combined unit, within the view rectangle. The horizontal alignment
+ * value should be one of the constants LEFT, RIGHT or
+ * CENTER. The vertical alignment valus should be one of the
+ * constants TOP, BOTTOM or
+ * CENTER.
+ *
+ *
If the text and icon are equal to or larger than the view
+ * rectangle, the horizontal and vertical alignment values have no
+ * affect.
+ *
+ *
Note that this method does not know how to deal with
+ * horizontal alignments or positions given as LEADING or
+ * TRAILING values. Use the other overloaded variant of this
+ * method if you wish to use such values.
+ *
+ * @param fm The font metrics used to measure the text
+ * @param text The text to place in the compound label
+ * @param icon The icon to place next to the text
+ * @param verticalAlignment The vertical alignment of the label relative
+ * to its component
+ * @param horizontalAlignment The horizontal alignment of the label
+ * relative to its component
+ * @param verticalTextPosition The vertical position of the label's text
+ * relative to its icon
+ * @param horizontalTextPosition The horizontal position of the label's
+ * text relative to its icon
+ * @param viewR The view rectangle, specifying the area which layout is
+ * constrained to
+ * @param iconR A rectangle which is modified to hold the laid-out
+ * position of the icon
+ * @param textR A rectangle which is modified to hold the laid-out
+ * position of the text
+ * @param textIconGap The distance between text and icon
+ *
+ * @return The string of characters, possibly truncated with an elipsis,
+ * which is laid out in this label
+ */
+ private static String layoutCompoundLabelImpl(JComponent c,
+ FontMetrics fm,
+ String text,
+ Icon icon,
+ int verticalAlignment,
+ int horizontalAlignment,
+ int verticalTextPosition,
+ int horizontalTextPosition,
+ Rectangle viewR,
+ Rectangle iconR,
+ Rectangle textR,
+ int textIconGap)
+ {
+
+ // Work out basic height and width.
+
+ if (icon == null)
+ {
+ textIconGap = 0;
+ iconR.width = 0;
+ iconR.height = 0;
+ }
+ else
+ {
+ iconR.width = icon.getIconWidth();
+ iconR.height = icon.getIconHeight();
+ }
+
+ if (text == null || text.equals(""))
+ {
+ textIconGap = 0;
+ textR.width = 0;
+ textR.height = 0;
+ text = "";
+ }
+ else
+ {
+ int availableWidth = viewR.width;
+ if (horizontalTextPosition != CENTER)
+ availableWidth -= iconR.width + textIconGap;
+ View html = c == null ? null
+ : (View) c.getClientProperty(BasicHTML.propertyKey);
+ if (html != null)
+ {
+ textR.width = (int) html.getPreferredSpan(View.X_AXIS);
+ textR.width = Math.min(availableWidth, textR.width);
+ textR.height = (int) html.getPreferredSpan(View.Y_AXIS);
+ }
+ else
+ {
+ int fromIndex = 0;
+ textR.width = fm.stringWidth(text);
+ textR.height = fm.getHeight();
+ if (textR.width > availableWidth)
+ {
+ text = clipString(c, fm, text, availableWidth);
+ textR.width = fm.stringWidth(text);
+ }
+ }
+ }
+
+ // Work out the position of text, assuming the top-left coord
+ // starts at (0,0). We will fix that up momentarily, after these
+ // "position" decisions are made and we look at alignment.
+
+ switch (verticalTextPosition)
+ {
+ case TOP:
+ textR.y = horizontalTextPosition == CENTER ?
+ - textR.height - textIconGap : 0;
+ break;
+ case BOTTOM:
+ textR.y = horizontalTextPosition == CENTER ?
+ iconR.height + textIconGap : iconR.height - textR.height;
+ break;
+ case CENTER:
+ textR.y = iconR.height / 2 - textR.height / 2;
+ break;
+ }
+
+ switch (horizontalTextPosition)
+ {
+ case LEFT:
+ textR.x = -(textR.width + textIconGap);
+ break;
+ case RIGHT:
+ textR.x = iconR.width + textIconGap;
+ break;
+ case CENTER:
+ textR.x = iconR.width / 2 - textR.width / 2;
+ break;
+ }
+
+ // The two rectangles are laid out correctly now, but only assuming
+ // that their upper left corner is at (0,0). If we have any alignment other
+ // than TOP and LEFT, we need to adjust them.
+
+ // These coordinates specify the rectangle that contains both the
+ // icon and text. Move it so that it fullfills the alignment properties.
+ int lx = Math.min(iconR.x, textR.x);
+ int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx;
+ int ly = Math.min(iconR.y, textR.y);
+ int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly;
+ int horizontalAdjustment = 0;
+ int verticalAdjustment = 0;
+ switch (verticalAlignment)
+ {
+ case TOP:
+ verticalAdjustment = viewR.y - ly;
+ break;
+ case BOTTOM:
+ verticalAdjustment = viewR.y + viewR.height - ly - lh;
+ break;
+ case CENTER:
+ verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2;
+ break;
+ }
+ switch (horizontalAlignment)
+ {
+ case LEFT:
+ horizontalAdjustment = viewR.x - lx;
+ break;
+ case RIGHT:
+ horizontalAdjustment = viewR.x + viewR.width - lx - lw;
+ break;
+ case CENTER:
+ horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2));
+ break;
+ }
+ iconR.x += horizontalAdjustment;
+ iconR.y += verticalAdjustment;
+
+ textR.x += horizontalAdjustment;
+ textR.y += verticalAdjustment;
+
+ return text;
+ }
+
+ /**
+ * The method clips the specified string so that it fits into the
+ * available width. It is only called when the text really doesn't fit,
+ * so we don't need to check that again.
+ *
+ * @param c the component
+ * @param fm the font metrics
+ * @param text the text
+ * @param availableWidth the available width
+ *
+ * @return the clipped string
+ */
+ private static String clipString(JComponent c, FontMetrics fm, String text,
+ int availableWidth)
+ {
+ String dots = "...";
+ int dotsWidth = fm.stringWidth(dots);
+ char[] string = text.toCharArray();
+ int endIndex = string.length;
+ while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth
+ && endIndex > 0)
+ endIndex--;
+ String clipped;
+ if (string.length >= endIndex + 3)
+ {
+ string[endIndex] = '.';
+ string[endIndex + 1] = '.';
+ string[endIndex + 2] = '.';
+ clipped = new String(string, 0, endIndex + 3);
+ }
+ else
+ {
+ char[] clippedChars = new char[string.length + 3];
+ System.arraycopy(string, 0, clippedChars, 0, string.length);
+ clippedChars[endIndex] = '.';
+ clippedChars[endIndex + 1] = '.';
+ clippedChars[endIndex + 2] = '.';
+ clipped = new String(clippedChars, 0, endIndex + 3);
+ }
+ return clipped;
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#invokeLater} with the
+ * specified {@link Runnable}.
+ */
+ public static void invokeLater(Runnable doRun)
+ {
+ java.awt.EventQueue.invokeLater(doRun);
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#invokeAndWait} with the
+ * specified {@link Runnable}.
+ */
+ public static void invokeAndWait(Runnable doRun)
+ throws InterruptedException,
+ InvocationTargetException
+ {
+ java.awt.EventQueue.invokeAndWait(doRun);
+ }
+
+ /**
+ * Calls {@link java.awt.EventQueue#isDispatchThread()}.
+ *
+ * @return true if the current thread is the current AWT event
+ * dispatch thread.
+ */
+ public static boolean isEventDispatchThread()
+ {
+ return java.awt.EventQueue.isDispatchThread();
+ }
+
+ /**
+ * This method paints the given component at the given position and size.
+ * The component will be reparented to the container given.
+ *
+ * @param g The Graphics object to draw with.
+ * @param c The Component to draw
+ * @param p The Container to reparent to.
+ * @param x The x coordinate to draw at.
+ * @param y The y coordinate to draw at.
+ * @param w The width of the drawing area.
+ * @param h The height of the drawing area.
+ */
+ public static void paintComponent(Graphics g, Component c, Container p,
+ int x, int y, int w, int h)
+ {
+ Container parent = c.getParent();
+ if (parent != null)
+ parent.remove(c);
+ if (p != null)
+ p.add(c);
+
+ Shape savedClip = g.getClip();
+
+ g.setClip(x, y, w, h);
+ g.translate(x, y);
+
+ c.paint(g);
+
+ g.translate(-x, -y);
+ g.setClip(savedClip);
+ }
+
+ /**
+ * This method paints the given component in the given rectangle.
+ * The component will be reparented to the container given.
+ *
+ * @param g The Graphics object to draw with.
+ * @param c The Component to draw
+ * @param p The Container to reparent to.
+ * @param r The rectangle that describes the drawing area.
+ */
+ public static void paintComponent(Graphics g, Component c,
+ Container p, Rectangle r)
+ {
+ paintComponent(g, c, p, r.x, r.y, r.width, r.height);
+ }
+
+ /**
+ * This method returns the common Frame owner used in JDialogs or
+ * JWindow when no owner is provided.
+ *
+ * @return The common Frame
+ */
+ static Window getOwnerFrame(Window owner)
+ {
+ Window result = owner;
+ if (result == null)
+ {
+ if (ownerFrame == null)
+ ownerFrame = new OwnerFrame();
+ result = ownerFrame;
+ }
+ return result;
+ }
+
+ /**
+ * Checks if left mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if left mouse was clicked, false otherwise.
+ */
+ public static boolean isLeftMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiers() & InputEvent.BUTTON1_MASK) != 0);
+ }
+
+ /**
+ * Checks if middle mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if middle mouse was clicked, false otherwise.
+ */
+ public static boolean isMiddleMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiersEx() & InputEvent.BUTTON2_DOWN_MASK)
+ == InputEvent.BUTTON2_DOWN_MASK);
+ }
+
+ /**
+ * Checks if right mouse button was clicked.
+ *
+ * @param event the event to check
+ *
+ * @return true if right mouse was clicked, false otherwise.
+ */
+ public static boolean isRightMouseButton(MouseEvent event)
+ {
+ return ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK)
+ == InputEvent.BUTTON3_DOWN_MASK);
+ }
+
+ /**
+ * This frame should be used when constructing a Window/JDialog without
+ * a parent. In this case, we are forced to use this frame as a window's
+ * parent, because we simply cannot pass null instead of parent to Window
+ * constructor, since doing it will result in NullPointerException.
+ */
+ private static class OwnerFrame extends Frame
+ {
+ public void setVisible(boolean b)
+ {
+ // Do nothing here.
+ }
+
+ public boolean isShowing()
+ {
+ return true;
+ }
+ }
+
+ public static boolean notifyAction(Action action,
+ KeyStroke ks,
+ KeyEvent event,
+ Object sender,
+ int modifiers)
+ {
+ if (action != null && action.isEnabled())
+ {
+ String name = (String) action.getValue(Action.ACTION_COMMAND_KEY);
+ if (name == null
+ && event.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
+ name = new String(new char[] {event.getKeyChar()});
+ action.actionPerformed(new ActionEvent(sender,
+ ActionEvent.ACTION_PERFORMED,
+ name, modifiers));
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
Change the shared, UI-managed {@link ActionMap} for a given
+ * component. ActionMaps are arranged in a hierarchy, in order to
+ * encourage sharing of common actions between components. The hierarchy
+ * unfortunately places UI-managed ActionMaps at the end of the
+ * parent-pointer chain, as illustrated:
Our goal with this method is to replace the first ActionMap along
+ * this chain which is an instance of {@link ActionMapUIResource}, since
+ * these are the ActionMaps which are supposed to be shared between
+ * components.
+ *
+ *
If the provided ActionMap is null, we interpret the
+ * call as a request to remove the UI-managed ActionMap from the
+ * component's ActionMap parent chain.
Change the shared, UI-managed {@link InputMap} for a given
+ * component. InputMaps are arranged in a hierarchy, in order to
+ * encourage sharing of common input mappings between components. The
+ * hierarchy unfortunately places UI-managed InputMaps at the
+ * end of the parent-pointer chain, as illustrated:
Our goal with this method is to replace the first InputMap along
+ * this chain which is an instance of {@link InputMapUIResource}, since
+ * these are the InputMaps which are supposed to be shared between
+ * components.
+ *
+ *
If the provided InputMap is null, we interpret the
+ * call as a request to remove the UI-managed InputMap from the
+ * component's InputMap parent chain.
+ */
+ public static void replaceUIInputMap(JComponent component,
+ int condition,
+ InputMap uiInputMap)
+ {
+ InputMap child = component.getInputMap(condition);
+ if (child == null)
+ component.setInputMap(condition, uiInputMap);
+ else
+ {
+ InputMap parent = child.getParent();
+ while (parent != null && !(parent instanceof InputMapUIResource))
+ {
+ child = parent;
+ parent = parent.getParent();
+ }
+ // Sanity check to avoid loops.
+ if (child != uiInputMap)
+ child.setParent(uiInputMap);
+ }
+ }
+
+ /**
+ * Subtracts a rectangle from another and return the area as an array
+ * of rectangles.
+ * Returns the areas of rectA which are not covered by rectB.
+ * If the rectangles do not overlap, or if either parameter is
+ * null, a zero-size array is returned.
+ * @param rectA The first rectangle
+ * @param rectB The rectangle to subtract from the first
+ * @return An array of rectangles representing the area in rectA
+ * not overlapped by rectB
+ */
+ public static Rectangle[] computeDifference(Rectangle rectA, Rectangle rectB)
+ {
+ if (rectA == null || rectB == null)
+ return new Rectangle[0];
+
+ Rectangle[] r = new Rectangle[4];
+ int x1 = rectA.x;
+ int y1 = rectA.y;
+ int w1 = rectA.width;
+ int h1 = rectA.height;
+ int x2 = rectB.x;
+ int y2 = rectB.y;
+ int w2 = rectB.width;
+ int h2 = rectB.height;
+
+ // (outer box = rectA)
+ // -------------
+ // |_____0_____|
+ // | |rectB| |
+ // |_1|_____|_2|
+ // | 3 |
+ // -------------
+ int H0 = (y2 > y1) ? y2 - y1 : 0; // height of box 0
+ int H3 = (y2 + h2 < y1 + h1) ? y1 + h1 - y2 - h2 : 0; // height box 3
+ int W1 = (x2 > x1) ? x2 - x1 : 0; // width box 1
+ int W2 = (x1 + w1 > x2 + w2) ? x1 + w1 - x2 - w2 : 0; // w. box 2
+ int H12 = (H0 + H3 < h1) ? h1 - H0 - H3 : 0; // height box 1 & 2
+
+ if (H0 > 0)
+ r[0] = new Rectangle(x1, y1, w1, H0);
+ else
+ r[0] = null;
+
+ if (W1 > 0 && H12 > 0)
+ r[1] = new Rectangle(x1, y1 + H0, W1, H12);
+ else
+ r[1] = null;
+
+ if (W2 > 0 && H12 > 0)
+ r[2] = new Rectangle(x2 + w2, y1 + H0, W2, H12);
+ else
+ r[2] = null;
+
+ if (H3 > 0)
+ r[3] = new Rectangle(x1, y1 + H0 + H12, w1, H3);
+ else
+ r[3] = null;
+
+ // sort out null objects
+ int n = 0;
+ for (int i = 0; i < 4; i++)
+ if (r[i] != null)
+ n++;
+ Rectangle[] out = new Rectangle[n];
+ for (int i = 3; i >= 0; i--)
+ if (r[i] != null)
+ out[--n] = r[i];
+
+ return out;
+ }
+
+ /**
+ * Calculates the intersection of two rectangles. The result is stored
+ * in rect. This is basically the same
+ * like {@link Rectangle#intersection(Rectangle)}, only that it does not
+ * create new Rectangle instances. The tradeoff is that you loose any data in
+ * rect.
+ *
+ * @param x upper-left x coodinate of first rectangle
+ * @param y upper-left y coodinate of first rectangle
+ * @param w width of first rectangle
+ * @param h height of first rectangle
+ * @param rect a Rectangle object of the second rectangle
+ *
+ * @throws NullPointerException if rect is null
+ *
+ * @return a rectangle corresponding to the intersection of the
+ * two rectangles. An empty rectangle is returned if the rectangles
+ * do not overlap
+ */
+ public static Rectangle computeIntersection(int x, int y, int w, int h,
+ Rectangle rect)
+ {
+ int x2 = (int) rect.x;
+ int y2 = (int) rect.y;
+ int w2 = (int) rect.width;
+ int h2 = (int) rect.height;
+
+ int dx = (x > x2) ? x : x2;
+ int dy = (y > y2) ? y : y2;
+ int dw = (x + w < x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
+ int dh = (y + h < y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
+
+ if (dw >= 0 && dh >= 0)
+ rect.setBounds(dx, dy, dw, dh);
+ else
+ rect.setBounds(0, 0, 0, 0);
+
+ return rect;
+ }
+
+ /**
+ * Calculates the width of a given string.
+ *
+ * @param fm the FontMetrics object to use
+ * @param str the string
+ *
+ * @return the width of the the string.
+ */
+ public static int computeStringWidth(FontMetrics fm, String str)
+ {
+ return fm.stringWidth(str);
+ }
+
+ /**
+ * Calculates the union of two rectangles. The result is stored in
+ * rect. This is basically the same as
+ * {@link Rectangle#union(Rectangle)} except that it avoids creation of new
+ * Rectangle objects. The tradeoff is that you loose any data in
+ * rect.
+ *
+ * @param x upper-left x coodinate of first rectangle
+ * @param y upper-left y coodinate of first rectangle
+ * @param w width of first rectangle
+ * @param h height of first rectangle
+ * @param rect a Rectangle object of the second rectangle
+ *
+ * @throws NullPointerException if rect is null
+ *
+ * @return a rectangle corresponding to the union of the
+ * two rectangles; a rectangle encompassing both is returned if the
+ * rectangles do not overlap
+ */
+ public static Rectangle computeUnion(int x, int y, int w, int h,
+ Rectangle rect)
+ {
+ int x2 = (int) rect.x;
+ int y2 = (int) rect.y;
+ int w2 = (int) rect.width;
+ int h2 = (int) rect.height;
+
+ int dx = (x < x2) ? x : x2;
+ int dy = (y < y2) ? y : y2;
+ int dw = (x + w > x2 + w2) ? (x + w - dx) : (x2 + w2 - dx);
+ int dh = (y + h > y2 + h2) ? (y + h - dy) : (y2 + h2 - dy);
+
+ if (dw >= 0 && dh >= 0)
+ rect.setBounds(dx, dy, dw, dh);
+ else
+ rect.setBounds(0, 0, 0, 0);
+ return rect;
+ }
+
+ /**
+ * Tests if a rectangle contains another.
+ * @param a first rectangle
+ * @param b second rectangle
+ * @return true if a contains b, false otherwise
+ * @throws NullPointerException
+ */
+ public static boolean isRectangleContainingRectangle(Rectangle a, Rectangle b)
+ {
+ // Note: zero-size rects inclusive, differs from Rectangle.contains()
+ return b.width >= 0 && b.height >= 0 && b.width >= 0 && b.height >= 0
+ && b.x >= a.x && b.x + b.width <= a.x + a.width && b.y >= a.y
+ && b.y + b.height <= a.y + a.height;
+ }
+
+ /**
+ * Returns the InputMap that is provided by the ComponentUI of
+ * component for the specified condition.
+ *
+ * @param component the component for which the InputMap is returned
+ * @param cond the condition that specifies which of the three input
+ * maps should be returned, may be
+ * {@link JComponent#WHEN_IN_FOCUSED_WINDOW},
+ * {@link JComponent#WHEN_FOCUSED} or
+ * {@link JComponent#WHEN_ANCESTOR_OF_FOCUSED_COMPONENT}
+ *
+ * @return The input map.
+ */
+ public static InputMap getUIInputMap(JComponent component, int cond)
+ {
+ if (UIManager.getUI(component) != null)
+ // we assume here that the UI class sets the parent of the component's
+ // InputMap, which is the correct behaviour. If it's not, then
+ // this can be considered a bug
+ return component.getInputMap(cond).getParent();
+ else
+ return null;
+ }
+
+ /**
+ * Returns the ActionMap that is provided by the ComponentUI of
+ * component.
+ *
+ * @param component the component for which the ActionMap is returned
+ */
+ public static ActionMap getUIActionMap(JComponent component)
+ {
+ if (UIManager.getUI(component) != null)
+ // we assume here that the UI class sets the parent of the component's
+ // ActionMap, which is the correct behaviour. If it's not, then
+ // this can be considered a bug
+ return component.getActionMap().getParent();
+ else
+ return null;
+ }
+
+ /**
+ * Processes key bindings for the component that is associated with the
+ * key event. Note that this method does not make sense for
+ * JComponent-derived components, except when
+ * {@link JComponent#processKeyEvent(KeyEvent)} is overridden and super is
+ * not called.
+ *
+ * This method searches through the component hierarchy of the component's
+ * top-level container to find a JComponent that has a binding
+ * for the key event in the WHEN_IN_FOCUSED_WINDOW scope.
+ *
+ * @param ev the key event
+ *
+ * @return true if a binding has been found and processed,
+ * false otherwise
+ *
+ * @since 1.4
+ */
+ public static boolean processKeyBindings(KeyEvent ev)
+ {
+ Component c = ev.getComponent();
+ KeyStroke s = KeyStroke.getKeyStrokeForEvent(ev);
+ KeyboardManager km = KeyboardManager.getManager();
+ return km.processKeyStroke(c, s, ev);
+ }
+
+ /**
+ * Returns a string representing one of the horizontal alignment codes
+ * defined in the {@link SwingConstants} interface. The following table
+ * lists the constants and return values:
+ *
+ *
+ *
+ *
Code:
Returned String:
+ *
+ *
+ *
{@link SwingConstants#CENTER}
+ *
"CENTER"
+ *
+ *
+ *
{@link SwingConstants#LEFT}
+ *
"LEFT"
+ *
+ *
+ *
{@link SwingConstants#RIGHT}
+ *
"RIGHT"
+ *
+ *
+ *
{@link SwingConstants#LEADING}
+ *
"LEADING"
+ *
+ *
+ *
{@link SwingConstants#TRAILING}
+ *
"TRAILING"
+ *
+ *
+ *
+ * If the supplied code is not one of those listed, this methods will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertHorizontalAlignmentCodeToString(int code)
+ {
+ switch (code)
+ {
+ case SwingConstants.CENTER:
+ return "CENTER";
+ case SwingConstants.LEFT:
+ return "LEFT";
+ case SwingConstants.RIGHT:
+ return "RIGHT";
+ case SwingConstants.LEADING:
+ return "LEADING";
+ case SwingConstants.TRAILING:
+ return "TRAILING";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Returns a string representing one of the vertical alignment codes
+ * defined in the {@link SwingConstants} interface. The following table
+ * lists the constants and return values:
+ *
+ *
+ *
+ *
Code:
Returned String:
+ *
+ *
+ *
{@link SwingConstants#CENTER}
+ *
"CENTER"
+ *
+ *
+ *
{@link SwingConstants#TOP}
+ *
"TOP"
+ *
+ *
+ *
{@link SwingConstants#BOTTOM}
+ *
"BOTTOM"
+ *
+ *
+ *
+ * If the supplied code is not one of those listed, this methods will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertVerticalAlignmentCodeToString(int code)
+ {
+ switch (code)
+ {
+ case SwingConstants.CENTER:
+ return "CENTER";
+ case SwingConstants.TOP:
+ return "TOP";
+ case SwingConstants.BOTTOM:
+ return "BOTTOM";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Returns a string representing one of the default operation codes
+ * defined in the {@link WindowConstants} interface. The following table
+ * lists the constants and return values:
+ *
+ *
+ *
+ *
Code:
Returned String:
+ *
+ *
+ *
{@link WindowConstants#DO_NOTHING_ON_CLOSE}
+ *
"DO_NOTHING_ON_CLOSE"
+ *
+ *
+ *
{@link WindowConstants#HIDE_ON_CLOSE}
+ *
"HIDE_ON_CLOSE"
+ *
+ *
+ *
{@link WindowConstants#DISPOSE_ON_CLOSE}
+ *
"DISPOSE_ON_CLOSE"
+ *
+ *
+ *
{@link WindowConstants#EXIT_ON_CLOSE}
+ *
"EXIT_ON_CLOSE"
+ *
+ *
+ *
+ * If the supplied code is not one of those listed, this method will throw
+ * an {@link IllegalArgumentException}.
+ *
+ * @param code the code.
+ *
+ * @return A string representing the given code.
+ */
+ static String convertWindowConstantToString(int code)
+ {
+ switch (code)
+ {
+ case WindowConstants.DO_NOTHING_ON_CLOSE:
+ return "DO_NOTHING_ON_CLOSE";
+ case WindowConstants.HIDE_ON_CLOSE:
+ return "HIDE_ON_CLOSE";
+ case WindowConstants.DISPOSE_ON_CLOSE:
+ return "DISPOSE_ON_CLOSE";
+ case WindowConstants.EXIT_ON_CLOSE:
+ return "EXIT_ON_CLOSE";
+ default:
+ throw new IllegalArgumentException("Unrecognised code: " + code);
+ }
+ }
+
+ /**
+ * Converts a rectangle in the coordinate system of a child component into
+ * a rectangle of one of it's Ancestors. The result is stored in the input
+ * rectangle.
+ *
+ * @param comp the child component
+ * @param r the rectangle to convert
+ * @param ancestor the ancestor component
+ */
+ static void convertRectangleToAncestor(Component comp, Rectangle r,
+ Component ancestor)
+ {
+ if (comp == ancestor)
+ return;
+
+ r.x += comp.getX();
+ r.y += comp.getY();
+
+ Component parent = comp.getParent();
+ if (parent != null && parent != ancestor)
+ convertRectangleToAncestor(parent, r, ancestor);
+ }
+}
diff --git a/libjava/classpath/javax/swing/Timer.java b/libjava/classpath/javax/swing/Timer.java
new file mode 100644
index 000000000..e9954b4b1
--- /dev/null
+++ b/libjava/classpath/javax/swing/Timer.java
@@ -0,0 +1,476 @@
+/* Timer.java --
+ Copyright (C) 2002, 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.Serializable;
+import java.util.EventListener;
+
+import javax.swing.event.EventListenerList;
+
+/**
+ * Fires one or more action events after the specified delay. This is
+ * a specialised version of java.util.Timer just for
+ * firing ActionEvents. All Timers share one (daemon)
+ * Thread (or java.util.Timer). All events are fired from the event
+ * queue.
+ *
+ * @author Ronald Veldema
+ * @author Audrius Meskauskas (audriusa@Bionformatics.org) - bug fixes
+ * and documentation comments
+ */
+public class Timer
+ implements Serializable
+{
+ /**
+ * Given to the shared java.util.Timer to (possibly repeatedly) call
+ * queueEvent().
+ */
+ private class Task extends java.util.TimerTask
+ {
+ public void run()
+ {
+ if (logTimers)
+ System.out.println("javax.swing.Timer -> queueEvent()");
+ queueEvent();
+
+ if (!repeats)
+ task = null;
+ }
+ }
+
+ /**
+ * Use serialVersionUID for interoperability.
+ */
+ private static final long serialVersionUID = -1116180831621385484L;
+
+ /**
+ * The encloding class, used with {@link SwingUtilities#invokeLater}
+ * to invoke the {@link #drainEvents()}.
+ */
+ private Runnable drainer = new Runnable()
+ {
+ public void run()
+ {
+ drainEvents();
+ }
+ };
+
+ /**
+ * The static java.util.Timer daemon which will be used to schedule
+ * all javax.swing.Timer.Task objects. The daemon will always be
+ * running, even if there's no task scheduled in it.
+ */
+ private static java.util.Timer timer = new java.util.Timer("swing.Timer",
+ true);
+
+ /**
+ * If true, the timer prints a message to
+ * {@link System#out} when firing each event.
+ */
+ static boolean logTimers;
+
+ /**
+ * A field to store all listeners who are listening to this timer.
+ */
+ protected EventListenerList listenerList = new EventListenerList();
+
+ /**
+ * true if the timer coalesces events.
+ */
+ boolean coalesce = true;
+
+ /**
+ * true if the timer is firing repetetive events.
+ */
+ boolean repeats = true;
+
+ /**
+ * The delay between subsequent repetetive events.
+ */
+ int delay;
+
+ /**
+ * The initial delay before the first event.
+ */
+ int initialDelay;
+
+ /**
+ * The number of events that have been already fired by this timer.
+ * This is used as a numeric identifier for the next event that would
+ * be fired.
+ */
+ int ticks;
+
+ /**
+ * The task that calls queueEvent(). When null this Timer is stopped.
+ * This is package private to avoid synthetic accessor method.
+ */
+ Task task;
+
+ /**
+ * This object manages a "queue" of virtual actionEvents, maintained as a
+ * simple long counter. When the timer expires, a new event is queued,
+ * and a dispatcher object is pushed into the system event queue. When
+ * the system thread runs the dispatcher, it will fire as many
+ * ActionEvents as have been queued, unless the timer is set to
+ * coalescing mode, in which case it will fire only one ActionEvent.
+ */
+ private long queue;
+
+ /**
+ * synchronized(queueLock) replaces
+ * synchronized(queue) that is not supported by this language.
+ */
+ private Object queueLock = new Object();
+
+ /**
+ * Creates a new Timer object.
+ *
+ * @param d the default value for both initial and between event delay, in
+ * milliseconds.
+ * @param listener the first action listener, can be null.
+ */
+ public Timer(int d, ActionListener listener)
+ {
+ delay = d;
+ initialDelay = d;
+
+ if (listener != null)
+ addActionListener(listener);
+ }
+
+ /**
+ * Get the array of action listeners.
+ *
+ * @return the array of action listeners that are listening for the events,
+ * fired by this timer
+ *
+ * @since 1.4
+ */
+ public ActionListener[] getActionListeners()
+ {
+ return (ActionListener[]) listenerList.getListeners(ActionListener.class);
+ }
+
+ /**
+ * Sets whether the Timer coalesces multiple pending event firings.
+ * If the coalescing is enabled, the multiple events that have not been
+ * fired on time are replaced by the single event. The events may not
+ * be fired on time if the application is busy.
+ *
+ * @param c true (default) to enable the event coalescing,
+ * false otherwise
+ */
+ public void setCoalesce(boolean c)
+ {
+ coalesce = c;
+ }
+
+ /**
+ * Checks if the Timer coalesces multiple pending event firings.
+ * If the coalescing is enabled, the multiple events that have not been
+ * fired on time are replaced by the single event. The events may not
+ * be fired on time if the application is busy.
+ *
+ * @return true if the coalescing is enabled,
+ * false otherwise
+ */
+ public boolean isCoalesce()
+ {
+ return coalesce;
+ }
+
+ /**
+ * Get the event listeners of the given type that are listening for the
+ * events, fired by this timer.
+ *
+ * @param listenerType the listener type (for example, ActionListener.class)
+ *
+ * @return the array of event listeners that are listening for the events,
+ * fired by this timer
+ * @since 1.3
+ */
+ public T[] getListeners(Class listenerType)
+ {
+ return listenerList.getListeners(listenerType);
+ }
+
+ /**
+ * Set the timer logging state. If it is set to true, the
+ * timer prints a message to {@link System#out} when firing each
+ * action event.
+ *
+ * @param lt true if logging is enabled, false
+ * (default value) otherwise
+ */
+ public static void setLogTimers(boolean lt)
+ {
+ logTimers = lt;
+ }
+
+ /**
+ * Return the logging state.
+ *
+ * @return true if the timer is printing a message to
+ * {@link System#out}
+ * when firing each action event
+ */
+ public static boolean getLogTimers()
+ {
+ return logTimers;
+ }
+
+ /**
+ * Set the delay between firing the subsequent events.
+ * This parameter does not change the value of the initial delay before
+ * firing the first event.
+ *
+ * @param d The time gap between the subsequent events, in milliseconds
+ *
+ * @throws IllegalArgumentException if d is less than zero.
+ */
+ public void setDelay(int d)
+ {
+ if (d < 0)
+ throw new IllegalArgumentException("Invalid delay: " + d);
+ delay = d;
+ }
+
+ /**
+ * Get the delay between firing the subsequent events.
+ *
+ * @return The delay between subsequent events, in milliseconds
+ */
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ /**
+ * Set the intial delay before firing the first event since calling
+ * the {@link #start()} method. If the initial delay has not been
+ * set, it is assumed having the same value as the delay between the
+ * subsequent events.
+ *
+ * @param i the initial delay, in milliseconds
+ *
+ * @throws IllegalArgumentException if i is less than zero.
+ */
+ public void setInitialDelay(int i)
+ {
+ if (i < 0)
+ throw new IllegalArgumentException("Invalid initial delay: " + i);
+ initialDelay = i;
+ }
+
+ /**
+ * Get the intial delay before firing the first event since calling
+ * the {@link #start()} method. If the initial delay has not been
+ * set, returns the same value as {@link #getDelay()}.
+ *
+ * @return the initial delay before firing the first action event.
+ */
+ public int getInitialDelay()
+ {
+ return initialDelay;
+ }
+
+ /**
+ * Enable firing the repetetive events.
+ *
+ * @param r true (default value) to fire repetetive events.
+ * false to fire
+ * only one event after the initial delay
+ */
+ public void setRepeats(boolean r)
+ {
+ repeats = r;
+ }
+
+ /**
+ * Check is this timer fires repetetive events.
+ *
+ * @return true if the timer fires repetetive events,
+ * false if it fires
+ * only one event after the initial delay
+ */
+ public boolean isRepeats()
+ {
+ return repeats;
+ }
+
+ /**
+ * Get the timer state.
+ *
+ * @return true if the timer has been started and is firing
+ * the action events as scheduled. false
+ * if the timer is inactive.
+ */
+ public boolean isRunning()
+ {
+ return task != null;
+ }
+
+ /**
+ * Add the action listener
+ *
+ * @param listener the action listener to add
+ */
+ public void addActionListener(ActionListener listener)
+ {
+ listenerList.add(ActionListener.class, listener);
+ }
+
+ /**
+ * Remove the action listener.
+ *
+ * @param listener the action listener to remove
+ */
+ public void removeActionListener(ActionListener listener)
+ {
+ listenerList.remove(ActionListener.class, listener);
+ }
+
+ /**
+ * Cancel all pending tasks and fire the first event after the initial
+ * delay.
+ */
+ public void restart()
+ {
+ stop();
+ start();
+ }
+
+ /**
+ * Start firing the action events.
+ */
+ public void start()
+ {
+ Task t = task;
+ if (t == null)
+ {
+ t = new Task();
+ if (isRepeats())
+ timer.schedule(t, getInitialDelay(), getDelay());
+ else
+ timer.schedule(t, getInitialDelay());
+ task = t;
+ }
+ }
+
+ /**
+ * Stop firing the action events.
+ */
+ public void stop()
+ {
+ Task t = task;
+ if (t != null)
+ {
+ t.cancel();
+ task = null;
+ }
+ }
+
+ /**
+ * Fire the given action event to the action listeners.
+ *
+ * @param event the event to fire
+ */
+ protected void fireActionPerformed(ActionEvent event)
+ {
+ ActionListener[] listeners = getActionListeners();
+
+ for (int i = 0; i < listeners.length; i++)
+ listeners [ i ].actionPerformed(event);
+ }
+
+ /**
+ * Fire the action event, named "Timer" and having the numeric
+ * identifier, equal to the numer of events that have been
+ * already fired before.
+ */
+ void fireActionPerformed()
+ {
+ fireActionPerformed(new ActionEvent(this, ticks++, "Timer"));
+ }
+
+ /**
+ * Fire the queued action events.
+ * In the coalescing mode, a single event is fired as a replacement
+ * for all queued events. In non coalescing mode, a series of
+ * all queued events is fired.
+ * This is package-private to avoid an accessor method.
+ */
+ void drainEvents()
+ {
+ synchronized (queueLock)
+ {
+ if (isCoalesce())
+ {
+ if (queue > 0)
+ fireActionPerformed();
+ }
+ else
+ {
+ while (queue > 0)
+ {
+ fireActionPerformed();
+ queue--;
+ }
+ }
+ queue = 0;
+ }
+ }
+
+ /**
+ * Post a scheduled event to the event queue.
+ * Package-private to avoid an accessor method.
+ */
+ void queueEvent()
+ {
+ synchronized(queueLock)
+ {
+ queue++;
+ if (queue == 1)
+ SwingUtilities.invokeLater(drainer);
+ }
+ }
+}
diff --git a/libjava/classpath/javax/swing/ToolTipManager.java b/libjava/classpath/javax/swing/ToolTipManager.java
new file mode 100644
index 000000000..8a31f79f6
--- /dev/null
+++ b/libjava/classpath/javax/swing/ToolTipManager.java
@@ -0,0 +1,593 @@
+/* ToolTipManager.java --
+ Copyright (C) 2002, 2004, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+/**
+ * This class is responsible for the registration of JToolTips to Components
+ * and for displaying them when appropriate.
+ */
+public class ToolTipManager extends MouseAdapter implements MouseMotionListener
+{
+ /**
+ * This ActionListener is associated with the Timer that listens to whether
+ * the JToolTip can be hidden after four seconds.
+ */
+ protected class stillInsideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new stillInsideTimerAction object.
+ */
+ protected stillInsideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method hides the JToolTip when the Timer has finished.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ hideTip();
+ }
+ }
+
+ /**
+ * This Actionlistener is associated with the Timer that listens to whether
+ * the mouse cursor has re-entered the JComponent in time for an immediate
+ * redisplay of the JToolTip.
+ */
+ protected class outsideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new outsideTimerAction object.
+ */
+ protected outsideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method is called when the Timer that listens to whether the mouse
+ * cursor has re-entered the JComponent has run out.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ // TODO: What should be done here, if anything?
+ }
+ }
+
+ /**
+ * This ActionListener is associated with the Timer that listens to whether
+ * it is time for the JToolTip to be displayed after the mouse has entered
+ * the JComponent.
+ */
+ protected class insideTimerAction implements ActionListener
+ {
+ /**
+ * This method creates a new insideTimerAction object.
+ */
+ protected insideTimerAction()
+ {
+ // Nothing to do here.
+ }
+
+ /**
+ * This method displays the JToolTip when the Mouse has been still for the
+ * delay.
+ *
+ * @param event The ActionEvent.
+ */
+ public void actionPerformed(ActionEvent event)
+ {
+ showTip();
+ }
+ }
+
+ /**
+ * The Timer that determines whether the Mouse has been still long enough
+ * for the JToolTip to be displayed.
+ */
+ Timer enterTimer;
+
+ /**
+ * The Timer that determines whether the Mouse has re-entered the JComponent
+ * quickly enough for the JToolTip to be displayed immediately.
+ */
+ Timer exitTimer;
+
+ /**
+ * The Timer that determines whether the JToolTip has been displayed long
+ * enough for it to be hidden.
+ */
+ Timer insideTimer;
+
+ /** A global enabled setting for the ToolTipManager. */
+ private transient boolean enabled = true;
+
+ /** lightWeightPopupEnabled */
+ protected boolean lightWeightPopupEnabled = true;
+
+ /** heavyWeightPopupEnabled */
+ protected boolean heavyWeightPopupEnabled = false;
+
+ /** The shared instance of the ToolTipManager. */
+ private static ToolTipManager shared;
+
+ /** The current component the tooltip is being displayed for. */
+ private JComponent currentComponent;
+
+ /** The current tooltip. */
+ private JToolTip currentTip;
+
+ /**
+ * The tooltip text.
+ */
+ private String toolTipText;
+
+ /** The last known position of the mouse cursor. */
+ private Point currentPoint;
+
+ /** */
+ private Popup popup;
+
+ /**
+ * Creates a new ToolTipManager and sets up the timers.
+ */
+ ToolTipManager()
+ {
+ enterTimer = new Timer(750, new insideTimerAction());
+ enterTimer.setRepeats(false);
+
+ insideTimer = new Timer(4000, new stillInsideTimerAction());
+ insideTimer.setRepeats(false);
+
+ exitTimer = new Timer(500, new outsideTimerAction());
+ exitTimer.setRepeats(false);
+ }
+
+ /**
+ * This method returns the shared instance of ToolTipManager used by all
+ * JComponents.
+ *
+ * @return The shared instance of ToolTipManager.
+ */
+ public static ToolTipManager sharedInstance()
+ {
+ if (shared == null)
+ shared = new ToolTipManager();
+
+ return shared;
+ }
+
+ /**
+ * This method sets whether ToolTips are enabled or disabled for all
+ * JComponents.
+ *
+ * @param enabled Whether ToolTips are enabled or disabled for all
+ * JComponents.
+ */
+ public void setEnabled(boolean enabled)
+ {
+ if (! enabled)
+ {
+ enterTimer.stop();
+ exitTimer.stop();
+ insideTimer.stop();
+ }
+
+ this.enabled = enabled;
+ }
+
+ /**
+ * This method returns whether ToolTips are enabled.
+ *
+ * @return Whether ToolTips are enabled.
+ */
+ public boolean isEnabled()
+ {
+ return enabled;
+ }
+
+ /**
+ * This method returns whether LightweightToolTips are enabled.
+ *
+ * @return Whether LighweightToolTips are enabled.
+ */
+ public boolean isLightWeightPopupEnabled()
+ {
+ return lightWeightPopupEnabled;
+ }
+
+ /**
+ * This method sets whether LightweightToolTips are enabled. If you mix
+ * Lightweight and Heavyweight components, you must set this to false to
+ * ensure that the ToolTips popup above all other components.
+ *
+ * @param enabled Whether LightweightToolTips will be enabled.
+ */
+ public void setLightWeightPopupEnabled(boolean enabled)
+ {
+ lightWeightPopupEnabled = enabled;
+ heavyWeightPopupEnabled = ! enabled;
+ }
+
+ /**
+ * This method returns the initial delay before the ToolTip is shown when
+ * the mouse enters a Component.
+ *
+ * @return The initial delay before the ToolTip is shown.
+ */
+ public int getInitialDelay()
+ {
+ return enterTimer.getDelay();
+ }
+
+ /**
+ * Sets the initial delay before the ToolTip is shown when the
+ * mouse enters a Component.
+ *
+ * @param delay The initial delay before the ToolTip is shown.
+ *
+ * @throws IllegalArgumentException if delay is less than zero.
+ */
+ public void setInitialDelay(int delay)
+ {
+ enterTimer.setDelay(delay);
+ }
+
+ /**
+ * This method returns the time the ToolTip will be shown before being
+ * hidden.
+ *
+ * @return The time the ToolTip will be shown before being hidden.
+ */
+ public int getDismissDelay()
+ {
+ return insideTimer.getDelay();
+ }
+
+ /**
+ * Sets the time the ToolTip will be shown before being hidden.
+ *
+ * @param delay the delay (in milliseconds) before tool tips are hidden.
+ *
+ * @throws IllegalArgumentException if delay is less than zero.
+ */
+ public void setDismissDelay(int delay)
+ {
+ insideTimer.setDelay(delay);
+ }
+
+ /**
+ * This method returns the amount of delay where if the mouse re-enters a
+ * Component, the tooltip will be shown immediately.
+ *
+ * @return The reshow delay.
+ */
+ public int getReshowDelay()
+ {
+ return exitTimer.getDelay();
+ }
+
+ /**
+ * Sets the amount of delay where if the mouse re-enters a
+ * Component, the tooltip will be shown immediately.
+ *
+ * @param delay The reshow delay (in milliseconds).
+ *
+ * @throws IllegalArgumentException if delay is less than zero.
+ */
+ public void setReshowDelay(int delay)
+ {
+ exitTimer.setDelay(delay);
+ }
+
+ /**
+ * This method registers a JComponent with the ToolTipManager.
+ *
+ * @param component The JComponent to register with the ToolTipManager.
+ */
+ public void registerComponent(JComponent component)
+ {
+ component.addMouseListener(this);
+ component.addMouseMotionListener(this);
+ }
+
+ /**
+ * This method unregisters a JComponent with the ToolTipManager.
+ *
+ * @param component The JComponent to unregister with the ToolTipManager.
+ */
+ public void unregisterComponent(JComponent component)
+ {
+ component.removeMouseMotionListener(this);
+ component.removeMouseListener(this);
+ }
+
+ /**
+ * This method is called whenever the mouse enters a JComponent registered
+ * with the ToolTipManager. When the mouse enters within the period of time
+ * specified by the reshow delay, the tooltip will be displayed
+ * immediately. Otherwise, it must wait for the initial delay before
+ * displaying the tooltip.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseEntered(MouseEvent event)
+ {
+ if (currentComponent != null
+ && getContentPaneDeepestComponent(event) == currentComponent)
+ return;
+ currentPoint = event.getPoint();
+
+ currentComponent = (JComponent) event.getSource();
+ toolTipText = currentComponent.getToolTipText(event);
+ if (exitTimer.isRunning())
+ {
+ exitTimer.stop();
+ showTip();
+ return;
+ }
+ // This should always be stopped unless we have just fake-exited.
+ if (!enterTimer.isRunning())
+ enterTimer.start();
+ }
+
+ /**
+ * This method is called when the mouse exits a JComponent registered with the
+ * ToolTipManager. When the mouse exits, the tooltip should be hidden
+ * immediately.
+ *
+ * @param event
+ * The MouseEvent.
+ */
+ public void mouseExited(MouseEvent event)
+ {
+ if (getContentPaneDeepestComponent(event) == currentComponent)
+ return;
+
+ currentPoint = event.getPoint();
+ currentComponent = null;
+ hideTip();
+
+ if (! enterTimer.isRunning())
+ exitTimer.start();
+ if (enterTimer.isRunning())
+ enterTimer.stop();
+ if (insideTimer.isRunning())
+ insideTimer.stop();
+ }
+
+ /**
+ * This method is called when the mouse is pressed on a JComponent
+ * registered with the ToolTipManager. When the mouse is pressed, the
+ * tooltip (if it is shown) must be hidden immediately.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mousePressed(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ else if (insideTimer.isRunning())
+ {
+ insideTimer.stop();
+ hideTip();
+ }
+ }
+
+ /**
+ * This method is called when the mouse is dragged in a JComponent
+ * registered with the ToolTipManager.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseDragged(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ }
+
+ /**
+ * This method is called when the mouse is moved in a JComponent registered
+ * with the ToolTipManager.
+ *
+ * @param event The MouseEvent.
+ */
+ public void mouseMoved(MouseEvent event)
+ {
+ currentPoint = event.getPoint();
+ if (currentTip != null && currentTip.isShowing())
+ checkTipUpdate(event);
+ else
+ {
+ if (enterTimer.isRunning())
+ enterTimer.restart();
+ }
+ }
+
+ /**
+ * Checks if the tooltip's text or location changes when the mouse is moved
+ * over the component.
+ */
+ private void checkTipUpdate(MouseEvent ev)
+ {
+ JComponent comp = (JComponent) ev.getSource();
+ String newText = comp.getToolTipText(ev);
+ String oldText = toolTipText;
+ if (newText != null)
+ {
+ if (((newText != null && newText.equals(oldText)) || newText == null))
+ {
+ // No change at all. Restart timers.
+ if (popup == null)
+ enterTimer.restart();
+ else
+ insideTimer.restart();
+ }
+ else
+ {
+ // Update the tooltip.
+ toolTipText = newText;
+ hideTip();
+ showTip();
+ exitTimer.stop();
+ }
+ }
+ else
+ {
+ // Hide tooltip.
+ currentTip = null;
+ currentPoint = null;
+ hideTip();
+ enterTimer.stop();
+ exitTimer.stop();
+ }
+ }
+
+ /**
+ * This method displays the ToolTip. It can figure out the method needed to
+ * show it as well (whether to display it in heavyweight/lightweight panel
+ * or a window.) This is package-private to avoid an accessor method.
+ */
+ void showTip()
+ {
+ if (!enabled || currentComponent == null || !currentComponent.isEnabled()
+ || !currentComponent.isShowing())
+ {
+ popup = null;
+ return;
+ }
+
+ if (currentTip == null || currentTip.getComponent() != currentComponent)
+ currentTip = currentComponent.createToolTip();
+ currentTip.setTipText(toolTipText);
+
+ Point p = currentPoint;
+ Point cP = currentComponent.getLocationOnScreen();
+ Dimension dims = currentTip.getPreferredSize();
+
+ JLayeredPane pane = null;
+ JRootPane r = ((JRootPane) SwingUtilities.getAncestorOfClass(JRootPane.class,
+ currentComponent));
+ if (r != null)
+ pane = r.getLayeredPane();
+ if (pane == null)
+ return;
+
+ p.translate(cP.x, cP.y);
+ adjustLocation(p, pane, dims);
+
+ currentTip.setBounds(0, 0, dims.width, dims.height);
+
+ PopupFactory factory = PopupFactory.getSharedInstance();
+ popup = factory.getPopup(currentComponent, currentTip, p.x, p.y);
+ popup.show();
+ }
+
+ /**
+ * Adjusts the point to a new location on the component,
+ * using the currentTip's dimensions.
+ *
+ * @param p - the point to convert.
+ * @param c - the component the point is on.
+ * @param d - the dimensions of the currentTip.
+ */
+ private Point adjustLocation(Point p, Component c, Dimension d)
+ {
+ if (p.x + d.width > c.getWidth())
+ p.x -= d.width;
+ if (p.x < 0)
+ p.x = 0;
+ if (p.y + d.height < c.getHeight())
+ p.y += d.height;
+ if (p.y + d.height > c.getHeight())
+ p.y -= d.height;
+
+ return p;
+ }
+
+ /**
+ * This method hides the ToolTip.
+ * This is package-private to avoid an accessor method.
+ */
+ void hideTip()
+ {
+ if (popup != null)
+ popup.hide();
+ }
+
+ /**
+ * This method returns the deepest component in the content pane for the
+ * first RootPaneContainer up from the currentComponent. This method is
+ * used in conjunction with one of the mouseXXX methods.
+ *
+ * @param e The MouseEvent.
+ *
+ * @return The deepest component in the content pane.
+ */
+ private Component getContentPaneDeepestComponent(MouseEvent e)
+ {
+ Component source = (Component) e.getSource();
+ Container parent = SwingUtilities.getAncestorOfClass(JRootPane.class,
+ currentComponent);
+ if (parent == null)
+ return null;
+ parent = ((JRootPane) parent).getContentPane();
+ Point p = e.getPoint();
+ p = SwingUtilities.convertPoint(source, p, parent);
+ Component target = SwingUtilities.getDeepestComponentAt(parent, p.x, p.y);
+ return target;
+ }
+}
diff --git a/libjava/classpath/javax/swing/TransferHandler.java b/libjava/classpath/javax/swing/TransferHandler.java
new file mode 100644
index 000000000..abf6e8c2e
--- /dev/null
+++ b/libjava/classpath/javax/swing/TransferHandler.java
@@ -0,0 +1,654 @@
+/* TransferHandler.java --
+ Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragGestureRecognizer;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceContext;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.InputEvent;
+import java.awt.event.MouseEvent;
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Method;
+
+public class TransferHandler implements Serializable
+{
+
+ /**
+ * An implementation of {@link Transferable} that can be used to export
+ * data from a component's property.
+ */
+ private static class PropertyTransferable
+ implements Transferable
+ {
+ /**
+ * The component from which we export.
+ */
+ private JComponent component;
+
+ /**
+ * The property descriptor of the property that we handle.
+ */
+ private PropertyDescriptor property;
+
+ /**
+ * Creates a new PropertyTransferable.
+ *
+ * @param c the component from which we export
+ * @param prop the property from which we export
+ */
+ PropertyTransferable(JComponent c, PropertyDescriptor prop)
+ {
+ component = c;
+ property = prop;
+ }
+
+ /**
+ * Returns the data flavors supported by the Transferable.
+ *
+ * @return the data flavors supported by the Transferable
+ */
+ public DataFlavor[] getTransferDataFlavors()
+ {
+ DataFlavor[] flavors;
+ Class propClass = property.getPropertyType();
+ String mime = DataFlavor.javaJVMLocalObjectMimeType + "; class="
+ + propClass.getName();
+ try
+ {
+ DataFlavor flavor = new DataFlavor(mime);
+ flavors = new DataFlavor[]{ flavor };
+ }
+ catch (ClassNotFoundException ex)
+ {
+ flavors = new DataFlavor[0];
+ }
+ return flavors;
+ }
+
+ /**
+ * Returns true when the specified data flavor is supported,
+ * false otherwise.
+ *
+ * @return true when the specified data flavor is supported,
+ * false otherwise
+ */
+ public boolean isDataFlavorSupported(DataFlavor flavor)
+ {
+ Class propClass = property.getPropertyType();
+ return flavor.getPrimaryType().equals("application")
+ && flavor.getSubType().equals("x-java-jvm-local-objectref")
+ && propClass.isAssignableFrom(flavor.getRepresentationClass());
+ }
+
+ /**
+ * Returns the actual transfer data.
+ *
+ * @param flavor the data flavor
+ *
+ * @return the actual transfer data
+ */
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException, IOException
+ {
+ if (isDataFlavorSupported(flavor))
+ {
+ Method getter = property.getReadMethod();
+ Object o;
+ try
+ {
+ o = getter.invoke(component);
+ return o;
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Property read failed: "
+ + property.getName());
+ }
+ }
+ else
+ throw new UnsupportedFlavorException(flavor);
+ }
+ }
+
+ static class TransferAction extends AbstractAction
+ {
+ private String command;
+
+ public TransferAction(String command)
+ {
+ super(command);
+ this.command = command;
+ }
+
+ public void actionPerformed(ActionEvent event)
+ {
+ JComponent component = (JComponent) event.getSource();
+ TransferHandler transferHandler = component.getTransferHandler();
+ Clipboard clipboard = getClipboard(component);
+
+ if (clipboard == null)
+ {
+ // Access denied!
+ Toolkit.getDefaultToolkit().beep();
+ return;
+ }
+
+ if (command.equals(COMMAND_COPY))
+ transferHandler.exportToClipboard(component, clipboard, COPY);
+ else if (command.equals(COMMAND_CUT))
+ transferHandler.exportToClipboard(component, clipboard, MOVE);
+ else if (command.equals(COMMAND_PASTE))
+ {
+ Transferable transferable = clipboard.getContents(null);
+
+ if (transferable != null)
+ transferHandler.importData(component, transferable);
+ }
+ }
+
+ /**
+ * Get the system cliboard or null if the caller isn't allowed to
+ * access the system clipboard.
+ *
+ * @param component a component, used to get the toolkit.
+ * @return the clipboard
+ */
+ private static Clipboard getClipboard(JComponent component)
+ {
+ try
+ {
+ return component.getToolkit().getSystemClipboard();
+ }
+ catch (SecurityException se)
+ {
+ return null;
+ }
+ }
+ }
+
+ private static class SwingDragGestureRecognizer extends DragGestureRecognizer
+ {
+
+ protected SwingDragGestureRecognizer(DragGestureListener dgl)
+ {
+ super(DragSource.getDefaultDragSource(), null, NONE, dgl);
+ }
+
+ void gesture(JComponent c, MouseEvent e, int src, int drag)
+ {
+ setComponent(c);
+ setSourceActions(src);
+ appendEvent(e);
+ fireDragGestureRecognized(drag, e.getPoint());
+ }
+
+ protected void registerListeners()
+ {
+ // Nothing to do here.
+ }
+
+ protected void unregisterListeners()
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ private static class SwingDragHandler
+ implements DragGestureListener, DragSourceListener
+ {
+
+ private boolean autoscrolls;
+
+ public void dragGestureRecognized(DragGestureEvent e)
+ {
+ JComponent c = (JComponent) e.getComponent();
+ TransferHandler th = c.getTransferHandler();
+ Transferable t = th.createTransferable(c);
+ if (t != null)
+ {
+ autoscrolls = c.getAutoscrolls();
+ c.setAutoscrolls(false);
+ try
+ {
+ e.startDrag(null, t, this);
+ return;
+ }
+ finally
+ {
+ c.setAutoscrolls(autoscrolls);
+ }
+ }
+ th.exportDone(c, t, NONE);
+ }
+
+ public void dragDropEnd(DragSourceDropEvent e)
+ {
+ DragSourceContext ctx = e.getDragSourceContext();
+ JComponent c = (JComponent) ctx.getComponent();
+ TransferHandler th = c.getTransferHandler();
+ if (e.getDropSuccess())
+ {
+ th.exportDone(c, ctx.getTransferable(), e.getDropAction());
+ }
+ else
+ {
+ th.exportDone(c, ctx.getTransferable(), e.getDropAction());
+ }
+ c.setAutoscrolls(autoscrolls);
+ }
+
+ public void dragEnter(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dragExit(DragSourceEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dragOver(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ public void dropActionChanged(DragSourceDragEvent e)
+ {
+ // Nothing to do here.
+ }
+
+ }
+
+ private static final long serialVersionUID = -967749805571669910L;
+
+ private static final String COMMAND_COPY = "copy";
+ private static final String COMMAND_CUT = "cut";
+ private static final String COMMAND_PASTE = "paste";
+
+ public static final int NONE = 0;
+ public static final int COPY = 1;
+ public static final int MOVE = 2;
+ public static final int COPY_OR_MOVE = 3;
+
+ private static Action copyAction = new TransferAction(COMMAND_COPY);
+ private static Action cutAction = new TransferAction(COMMAND_CUT);
+ private static Action pasteAction = new TransferAction(COMMAND_PASTE);
+
+ private int sourceActions;
+ private Icon visualRepresentation;
+
+ /**
+ * The name of the property into/from which this TransferHandler
+ * imports/exports.
+ */
+ private String propertyName;
+
+ /**
+ * The DragGestureRecognizer for Swing.
+ */
+ private SwingDragGestureRecognizer recognizer;
+
+ public static Action getCopyAction()
+ {
+ return copyAction;
+ }
+
+ public static Action getCutAction()
+ {
+ return cutAction;
+ }
+
+ public static Action getPasteAction()
+ {
+ return pasteAction;
+ }
+
+ protected TransferHandler()
+ {
+ this.sourceActions = NONE;
+ }
+
+ public TransferHandler(String property)
+ {
+ propertyName = property;
+ this.sourceActions = property != null ? COPY : NONE;
+ }
+
+ /**
+ * Returns true if the data in this TransferHandler can be
+ * imported into the specified component. This will be the case when:
+ *
+ *
The component has a readable and writable property with the property
+ * name specified in the TransferHandler constructor.
+ *
There is a dataflavor with a mime type of
+ * application/x-java-jvm-local-object-ref.
+ *
The dataflavor's representation class matches the class of the
+ * property in the component.
+ *
+ *
+ * @param c the component to check
+ * @param flavors the possible data flavors
+ *
+ * @return true if the data in this TransferHandler can be
+ * imported into the specified component, false
+ * otherwise
+ */
+ public boolean canImport(JComponent c, DataFlavor[] flavors)
+ {
+ PropertyDescriptor propDesc = getPropertyDescriptor(c);
+ boolean canImport = false;
+ if (propDesc != null)
+ {
+ // Check if the property is writable. The readable check is already
+ // done in getPropertyDescriptor().
+ Method writer = propDesc.getWriteMethod();
+ if (writer != null)
+ {
+ Class[] params = writer.getParameterTypes();
+ if (params.length == 1)
+ {
+ // Number of parameters ok, now check mime type and
+ // representation class.
+ DataFlavor flavor = getPropertyDataFlavor(params[0], flavors);
+ if (flavor != null)
+ canImport = true;
+ }
+ }
+ }
+ return canImport;
+ }
+
+ /**
+ * Creates a {@link Transferable} that can be used to export data
+ * from the specified component.
+ *
+ * This method returns null when the specified component
+ * doesn't have a readable property that matches the property name
+ * specified in the TransferHandler constructor.
+ *
+ * @param c the component to create a transferable for
+ *
+ * @return a {@link Transferable} that can be used to export data
+ * from the specified component, or null if the component doesn't
+ * have a readable property like the transfer handler
+ */
+ protected Transferable createTransferable(JComponent c)
+ {
+ Transferable transferable = null;
+ if (propertyName != null)
+ {
+ PropertyDescriptor prop = getPropertyDescriptor(c);
+ if (prop != null)
+ transferable = new PropertyTransferable(c, prop);
+ }
+ return transferable;
+ }
+
+ public void exportAsDrag(JComponent c, InputEvent e, int action)
+ {
+ int src = getSourceActions(c);
+ int drag = src & action;
+ if (! (e instanceof MouseEvent))
+ {
+ drag = NONE;
+ }
+ if (drag != NONE)
+ {
+ if (recognizer == null)
+ {
+ SwingDragHandler ds = new SwingDragHandler();
+ recognizer = new SwingDragGestureRecognizer(ds);
+ }
+ recognizer.gesture(c, (MouseEvent) e, src, drag);
+ }
+ else
+ {
+ exportDone(c, null, NONE);
+ }
+ }
+
+ /**
+ * This method is invoked after data has been exported.
+ * Subclasses should implement this method to remove the data that has been
+ * transferred when the action was MOVE.
+ *
+ * The default implementation does nothing because MOVE is not supported.
+ *
+ * @param c the source component
+ * @param data the data that has been transferred or null
+ * when the action is NONE
+ * @param action the action that has been performed
+ */
+ protected void exportDone(JComponent c, Transferable data, int action)
+ {
+ // Nothing to do in the default implementation.
+ }
+
+ /**
+ * Exports the property of the component c that was
+ * specified for this TransferHandler to the clipboard, performing
+ * the specified action.
+ *
+ * This will check if the action is allowed by calling
+ * {@link #getSourceActions(JComponent)}. If the action is not allowed,
+ * then no export is performed.
+ *
+ * In either case the method {@link #exportDone} will be called with
+ * the action that has been performed, or {@link #NONE} if the action
+ * was not allowed or could otherwise not be completed.
+ * Any IllegalStateException that is thrown by the Clipboard due to
+ * beeing unavailable will be propagated through this method.
+ *
+ * @param c the component from which to export
+ * @param clip the clipboard to which the data will be exported
+ * @param action the action to perform
+ *
+ * @throws IllegalStateException when the clipboard is not available
+ */
+ public void exportToClipboard(JComponent c, Clipboard clip, int action)
+ throws IllegalStateException
+ {
+ action &= getSourceActions(c);
+ Transferable transferable = createTransferable(c);
+ if (transferable != null && action != NONE)
+ {
+ try
+ {
+ clip.setContents(transferable, null);
+ exportDone(c, transferable, action);
+ }
+ catch (IllegalStateException ex)
+ {
+ exportDone(c, transferable, NONE);
+ throw ex;
+ }
+ }
+ else
+ exportDone(c, null, NONE);
+ }
+
+ public int getSourceActions(JComponent c)
+ {
+ return sourceActions;
+ }
+
+ public Icon getVisualRepresentation(Transferable t)
+ {
+ return visualRepresentation;
+ }
+
+ /**
+ * Imports the transfer data represented by t into the specified
+ * component c by setting the property of this TransferHandler
+ * on that component. If this succeeds, this method returns
+ * true, otherwise false.
+ *
+ *
+ * @param c the component to import into
+ * @param t the transfer data to import
+ *
+ * @return true if the transfer succeeds, false
+ * otherwise
+ */
+ public boolean importData(JComponent c, Transferable t)
+ {
+ boolean ok = false;
+ PropertyDescriptor prop = getPropertyDescriptor(c);
+ if (prop != null)
+ {
+ Method writer = prop.getWriteMethod();
+ if (writer != null)
+ {
+ Class[] params = writer.getParameterTypes();
+ if (params.length == 1)
+ {
+ DataFlavor flavor = getPropertyDataFlavor(params[0],
+ t.getTransferDataFlavors());
+ if (flavor != null)
+ {
+ try
+ {
+ Object value = t.getTransferData(flavor);
+ writer.invoke(c, new Object[]{ value });
+ ok = true;
+ }
+ catch (Exception ex)
+ {
+ // If anything goes wrong here, do nothing and return
+ // false;
+ }
+ }
+ }
+ }
+ }
+ return ok;
+ }
+
+ /**
+ * Returns the property descriptor for the property of this TransferHandler
+ * in the specified component, or null if no such property
+ * exists in the component. This method only returns properties that are
+ * at least readable (that is, it has a public no-arg getter method).
+ *
+ * @param c the component to check
+ *
+ * @return the property descriptor for the property of this TransferHandler
+ * in the specified component, or null if no such
+ * property exists in the component
+ */
+ private PropertyDescriptor getPropertyDescriptor(JComponent c)
+ {
+ PropertyDescriptor prop = null;
+ if (propertyName != null)
+ {
+ Class clazz = c.getClass();
+ BeanInfo beanInfo;
+ try
+ {
+ beanInfo = Introspector.getBeanInfo(clazz);
+ }
+ catch (IntrospectionException ex)
+ {
+ beanInfo = null;
+ }
+ if (beanInfo != null)
+ {
+ PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
+ for (int i = 0; i < props.length && prop == null; i++)
+ {
+ PropertyDescriptor desc = props[i];
+ if (desc.getName().equals(propertyName))
+ {
+ Method reader = desc.getReadMethod();
+ if (reader != null)
+ {
+ Class[] params = reader.getParameterTypes();
+ if (params == null || params.length == 0)
+ prop = desc;
+ }
+ }
+ }
+ }
+ }
+ return prop;
+ }
+
+ /**
+ * Searches flavors to find a suitable data flavor that
+ * has the mime type application/x-java-jvm-local-objectref and a
+ * representation class that is the same as the specified clazz.
+ * When no such data flavor is found, this returns null.
+ *
+ * @param clazz the representation class required for the data flavor
+ * @param flavors the possible data flavors
+ *
+ * @return the suitable data flavor or null if none is found
+ */
+ private DataFlavor getPropertyDataFlavor(Class clazz, DataFlavor[] flavors)
+ {
+ DataFlavor found = null;
+ for (int i = 0; i < flavors.length && found == null; i++)
+ {
+ DataFlavor flavor = flavors[i];
+ if (flavor.getPrimaryType().equals("application")
+ && flavor.getSubType().equals("x-java-jvm-local-objectref")
+ && clazz.isAssignableFrom(flavor.getRepresentationClass()))
+ found = flavor;
+ }
+ return found;
+ }
+}
diff --git a/libjava/classpath/javax/swing/UIDefaults.java b/libjava/classpath/javax/swing/UIDefaults.java
new file mode 100644
index 000000000..904b4c2b8
--- /dev/null
+++ b/libjava/classpath/javax/swing/UIDefaults.java
@@ -0,0 +1,847 @@
+/* UIDefaults.java -- database for all settings and interface bindings.
+ Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath 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 for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+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;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Insets;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.lang.reflect.Method;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import javax.swing.border.Border;
+import javax.swing.plaf.ComponentUI;
+import javax.swing.plaf.InputMapUIResource;
+
+/**
+ * UIDefaults is a database where all settings and interface bindings are
+ * stored into. A PLAF implementation fills one of these (see for example
+ * plaf/basic/BasicLookAndFeel.java) with "ButtonUI" -> new BasicButtonUI().
+ *
+ * @author Ronald Veldema (rveldema@cs.vu.nl)
+ */
+public class UIDefaults extends Hashtable